Duck Nettool Revenge 为一个常见网络工具套上了严格的输入过滤器。过滤看似严密:只允许数字、标点、空白,以及唯一一个有用的字母。然而,命令最终仍由 shell 执行,因此 shell 的展开规则补齐了过滤器试图删除的词汇。
危险的组合
核心行为等价于:
command = f"ping -c 1 {target}"
output = subprocess.check_output(
command,
shell=True,
stderr=subprocess.STDOUT,
)target 在插值前会经过允许列表检查。但这仍无法保证命令安全,因为 shell=True 赋予了剩余标点明确的语法含义:
;结束原本的 ping 命令,并开始执行另一条命令;/分隔路径段;?在路径名展开时匹配单个字符;- 空格分隔展开后的参数。
允许列表可以限制字符,却无法限制这些剩余字符仍然能够表达的 shell 语法。
用通配符拼出单词
复盘使用了具有精确路径段长度的 ? 模式。如果某个模式只匹配一条已安装路径,shell 就会在执行前用该路径替换模式。因此,载荷可以抽象为:
benign-address ; globbed-interpreter globbed-file-reader globbed-source唯一获准的字母,足以在相邻文件名中明确选中标准库的文件读取模块。其余部分则依靠路径段长度和容器中规模较小的文件系统完成定位。进程启动时,shell 已将三个通配符表达式全部展开为普通的可执行文件与文件参数。
关键区别在于:应用程序从未接收被禁止的字母,但子进程最终仍然收到了它们。展开发生在验证之后、执行之前。
为什么文件读取器是关键
Python 标准库中的一个辅助模块,可以从参数中接收文件名并逐行输出内容。通过已定位的解释器调用这个模块,就无需输入那些会被过滤的常见文件显示命令。
因此,完整攻击链如下:
- 提交一个无害地址,让预期命令能够正常开始;
- 用允许的命令分隔符结束它;
- 通过固定长度的通配符路径段定位解释器;
- 使用通配符与唯一可用字母定位标准库文件读取器;
- 再用一个唯一通配符模式定位应用程序源码;
- 让 shell 展开组合出被禁止的词汇并输出源码。
展开后的命令打印了应用程序源码,从中暴露了嵌入其中的赛事答案。
防御要点
- 对用户可控输入避免使用
shell=True,应直接向进程 API 传递参数数组。 - 应验证 IP 地址或主机名等语义值,而不是只过滤字符。
- 让工具在最小化环境与文件系统视图中运行;此处正是路径的唯一性让通配符展开变得可靠。
- 不要把秘密放入应用程序源码、注释、进程参数或错误响应中。
- 只要涉及 shell,就应把通配符展开、分隔符、重定向、替换和环境变量视作代码执行原语。
赛事页面:CTFtime 上的 V1T CTF 2026。
