跳至主要内容
全部题解
赛后复盘Web

V1T CTF 2026

Duck Nettool Revenge

Shell 通配符展开如何绕过严格的字符过滤。

发布日期
类别
Web 安全

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 标准库中的一个辅助模块,可以从参数中接收文件名并逐行输出内容。通过已定位的解释器调用这个模块,就无需输入那些会被过滤的常见文件显示命令。

因此,完整攻击链如下:

  1. 提交一个无害地址,让预期命令能够正常开始;
  2. 用允许的命令分隔符结束它;
  3. 通过固定长度的通配符路径段定位解释器;
  4. 使用通配符与唯一可用字母定位标准库文件读取器;
  5. 再用一个唯一通配符模式定位应用程序源码;
  6. 让 shell 展开组合出被禁止的词汇并输出源码。

展开后的命令打印了应用程序源码,从中暴露了嵌入其中的赛事答案。

防御要点

  • 对用户可控输入避免使用 shell=True,应直接向进程 API 传递参数数组。
  • 应验证 IP 地址或主机名等语义值,而不是只过滤字符。
  • 让工具在最小化环境与文件系统视图中运行;此处正是路径的唯一性让通配符展开变得可靠。
  • 不要把秘密放入应用程序源码、注释、进程参数或错误响应中。
  • 只要涉及 shell,就应把通配符展开、分隔符、重定向、替换和环境变量视作代码执行原语。

赛事页面:CTFtime 上的 V1T CTF 2026