靶场地址
CTFhub → 历年真题 → Web → 专家 → nmap(2020-网鼎杯-朱雀组)

一句话漏洞点
页面把用户输入的 host 直接拼进 nmap 命令执行 → 命令注入,用 nmap 自己的参数读文件。
看源码:注释直接给了 flag 路径
view-source: 打开首页,最后一行是 <!-- flag is in /flag -->。

输入 ' -iL /flag -oN flag.txt ' 让 nmap 把 /flag 当目标列表
' -iL /flag -oN flag.txt '-iL /flag:让 nmap 从/flag文件里读取扫描目标,于是它会把文件内容当主机名逐个处理;-oN flag.txt:把扫描结果按规定格式输出到flag.txt,写在 Web 根目录下就能直接下载;- 两端的单引号是为了让这段内容被当成一个整体参数拼进命令。

访问 flag.txt,报错信息里直接吐出 flag
http://challenge-92b4e19209e12fe7.sandbox.ctfhub.com:10800/flag.txtnmap 把 /flag 里的内容当作主机名去解析,解析失败会把内容原样写进报告:
Failed to resolve "ctfhub{1cbbdd96315d80d283ee6ba8}".
WARNING: No targets were specified, so 0 hosts scanned.
拿到 flag
ctfhub{1cbbdd96315d80d283ee6ba8}为什么报错里会出现 flag
整条命令的实际效果相当于:
nmap -Pn -T4 -F --host-timeout 1000ms -oX xml/ebfce -iL /flag -oN flag.txt ' -iL /flag -oN flag.txt 'nmap 读 /flag 时并不知道里面是 flag,它把这一行当成主机名去解析。解析失败时 nmap 会把这条主机名写进结果里(Failed to resolve "..."),于是 flag 就借着报错文本落到了输出文件 flag.txt。
命令注入是什么
漏洞根子是用户输入被拼进系统命令执行(类似 nmap xxx → shell_exec("nmap ".$host))。只要没做过滤,就能用命令自带的参数或 ;、|、&& 追加自己的操作。
- 本题的巧妙处是不靠
;拼接,而是利用 nmap 自身的-iL(从文件读目标列表)和-oN(输出到文件)两个参数完成"读文件 + 写文件"。 - 判断特征:功能类页面(扫描、ping、图片处理、压缩)出现输入框,基本都是命令拼接注入。
- 修复方式:使用参数化调用(如 PHP 的
escapeshellarg()),或直接换成库函数/白名单校验 host 格式。
补充
-iL会把文件按行当目标,所以 flag 只有一行也能被完整读出;若文件有多行,会逐行写进报告。- 同类思路还有:
;cat /flag > 1.txt、$(cat /flag)等,但本题过滤了部分符号,才改用 nmap 参数实现。
附:nmap 最常用的几条命令
| 命令 | 作用 |
|---|---|
nmap 192.168.1.1 | 默认扫描,常用端口 + 服务探测 |
nmap -sV 192.168.1.1 | 探测端口对应的服务及版本 |
nmap -p 80,443,8080 192.168.1.1 | 只扫指定端口(-p- 扫全部 65535 个端口) |
nmap -sn 192.168.1.0/24 | 只做主机存活探测,不扫端口 |
nmap -sS 192.168.1.1 | SYN 半开扫描,快且不易留日志(需管理员权限) |
nmap -O 192.168.1.1 | 尝试识别目标操作系统 |
nmap -A 192.168.1.1 | 综合扫描:-sV + -O + 脚本 + traceroute |
nmap -T4 192.168.1.1 | 提高扫描速度(-T0~-T5,越大越快越容易被发现) |
nmap -Pn 192.168.1.1 | 跳过存活探测,目标禁 ping 时照扫 |
nmap --script=vuln 192.168.1.1 | 调用漏洞检测类 NSE 脚本 |
nmap -iL targets.txt | 从文件读取目标列表(本题读 flag 用的就是这个) |
nmap -oN out.txt 192.168.1.1 | 结果输出到普通文本文件(-oX 为 XML,-oA 输出全部格式) |
记忆要点:-s 系列选扫描方式,-p 选端口,-o 系列管输出文件,-i 系列管输入来源。
评论 (0)