ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

当 assert 被优化掉之后:ctf-tasks Production 挑战的文件描述符泄漏攻击

当 assert 被优化掉之后:ctf-tasks Production 挑战的文件描述符泄漏攻击 当 assert 被优化掉之后ctf-tasks Production 挑战的文件描述符泄漏攻击【免费下载链接】ctf-tasksAn archive of low-level CTF challenges developed over the years项目地址: https://gitcode.com/gh_mirrors/ct/ctf-tasks一句不起眼的assert(close(fd) 0)在发布版程序里可能根本不会执行——这正是 ctf-tasks 项目收录的 Dragon CTF 2018「Production」挑战的核心。这道题把「assert 被优化掉」变成了一场教科书级的文件描述符泄漏攻击攻击者不靠栈溢出、不靠格式化字符串只靠 fd 越漏越多、配额越耗越尽就绕过了层层防御拿到了 flag。这篇文章将面向 CTF 新手一步步拆解这条完整的攻击链。Production 挑战一个看似人畜无害的歌词浏览器 「Production」是 Dragon CTF 2018 的 Teaser 题目难度 easy/medium分值 343 分当年只有 16/233 支队伍解出。题目背景是一个「生产级质量」的 Lyrics Explorer歌词浏览器让你浏览 8 支摇滚乐队的歌词——Guns N Roses、Led Zeppelin、Metallica、Nirvana、Pink Floyd、The Beatles、The Rolling Stones 都在资料库中。项目内容题目ProductionDragon CTF 2018 Teaser平台Linux x64 ELF防护NX、PIE、Full RELRO 全开关键线索官方直接提供了程序源码程序支持bands、songs、open、read、write、close六条命令看起来平平无奇。但正如题目描述所问的Can you spot any problems with the production-level quality of our lyrics explorer code?——你能从这份生产级代码里找出问题吗相关文件都在Dragon CTF 2018/Teaser/Production/目录下源码task/lyrics.cc可执行文件task/lyrics歌词数据库task/data/最终目标task/flag.txt第一处漏洞单层路径穿越如何绕过过滤 ⚠️程序对用户输入的路径做了过滤open_lyrics()里会调用sanitize_path()static bool sanitize_path(char *buffer) { if (strstr(buffer, ../) ! NULL) { return false; } return true; }问题就出在这里它只过滤了../却放过了单独的..。于是输入songs ..时拼接出的路径是./data/..等于直接列目录上一级的内容——flag.txt、lyrics可执行文件全部暴露在眼前。更关键的是open .. lyrics可以直接打开这个程序自己的二进制文件。这一步为后续的 fd 泄漏攻击铺好了路程序二进制里恰好含有 DrgnS 这个检测关键字而 flag 文件因为路径中带 flag 字样被单独过滤无法用来触发泄漏所以攻击者必须借用lyrics本体。核心漏洞assert 被优化掉引发的文件描述符泄漏 这是整道题的灵魂。在read_lyrics()中当读取的内容包含 DrgnS 时程序会执行防御逻辑if (strstr(buffer, DrgnS)) { printf([-] Attack detected and stopped!\n); assert(close(globals::records[idx]) 0); memmove(...); // 从记录列表中移除该项 globals::records.pop_back(); return true; }assert 在发布版中为何消失NDEBUG 的真相表面上看assert(close(fd) 0)既检查了关闭结果又关掉了文件描述符防御似乎天衣无缝。但别忘了 assert 的经典语义在定义了NDEBUG宏即发布版编译时assert 内的表达式会被整个剔除不会生成任何代码。这道题正是用-DNDEBUG编译的。于是close()调用被静默抹掉记录从数组里删除了但底层文件描述符依然敞开着——这就是一个隐蔽的、完全不可见的 fd 泄漏。每次读取含 DrgnS 的内容进程就白白丢掉一个 fd而程序还自以为防御成功。从泄漏到失控RLIMIT_NOFILE 与 fd 配额耗尽程序在set_limits()里把最大打开文件数限制为 32RLIMIT_NOFILE。这个限制本意是安全措施防止资源被滥用如今却成了攻击者的帮凶攻击者反复执行open .. lyricsread每次触发 DrgnS 检测就泄漏一个 fd而lyrics二进制 26 KB包含的 DrgnS 字符串足够重复触发几十次直到 32 个 fd 配额被耗尽——此时输入输出占用 3 个stdin/stdout/stderr剩下的全部被泄漏吞掉。一旦 fd 配额见底程序的正常逻辑就开始错位了。完整的攻击链从 fd 泄漏到读取 flag 的四个步骤 下面用 exploit 的视角完整走一遍 exploit.py 的四个阶段避免相对链接失效此处以代码样式给出路径Dragon CTF 2018/Teaser/Production/solution/exploit.py。第 1 步铺垫一个读到 EOF的记录先正常打开一首歌如 Pink Floyd 的Wish You Were Here把它完整读到文件末尾。这个记录将来会用来做缓冲区残留的最终读取。第 2 步反复触发 fd 泄漏直到配额耗尽然后循环执行open .. lyricsread每次读取都会命中 DrgnS 检测并泄漏一个 fd。直到open失败——说明配额已经耗尽。此时手动close掉最后一个泄漏记录精确腾出 1 个 fd 名额。第 3 步利用 fd 耗尽绕过 flag 过滤现在执行open .. flag看看open_lyrics()内部发生了什么int fd1 open(path, O_RDONLY); // ① 成功用掉唯一名额 ... globals::records.push_back(fd1); // ② flag 的 fd 被登记进记录 int fd2 open(path, O_RDONLY|O_NOFOLLOW); // ③ 失败没有 fd 可用了 if (fd2 -1) { printf([-] Detected attempt to open a symbolic link!\n); return true; // ④ 提前返回绕过了 flag 检查 } if (strstr(path, flag) ! NULL) { ... } // ⑤ 这段防御根本没执行到第一个open()成功打开了 flag 文件并登记进记录列表第二个用于防符号链接的open()因 fd 耗尽而失败函数提前返回flag 关键字检查被彻底跳过——flag 的文件描述符就这样被合法植入了记录表。第 4 步读取 flag 并利用栈残留二次提取接下来读取 flag 记录flag 内容会被读入read_lyrics()的栈缓冲区char buffer[4096]。由于 flag 本身以DrgnS{...}开头程序打印 Attack detected 并移除记录——但它没有打印 bufferflag 似乎被拦住了别急还有最后一招栈缓冲区残留。此时再读取第 1 步那个已经处于 EOF 的记录read()返回 0 字节程序跳过 DrgnS 检查直接执行printf(%s\n, buffer)——而 buffer 还是上一次调用时留下的、装着 flag 的旧数据flag 就这样被二次提取了出来DrgnS{Huh_Ass3rti0n5_can_b3_unre1i4b13}从这道题能学到的四条安全教训 永远不要把副作用写进 assert。close()、free()这类操作放在 assert 里等于在发布版中消失这是最典型的看似防御实则裸奔。assert 不是安全机制。它只用于调试绝不能作为权限校验、资源清理的依赖。检查flag关键字的逻辑应该放在 fd 登记之前。资源配额可能反过来成为攻击面。RLIMIT_NOFILE这类限制在 fd 泄漏场景下会加速资源耗尽甚至制造出第二次 open 失败 → 绕过检查的 TOCTOU 式漏洞。栈缓冲区不会自动清零。函数返回后旧数据依然留在栈上下一次调用未初始化就使用就会造成敏感信息泄漏。如何复现并亲手体验这道题目 ️想亲手跑一遍完整攻击先克隆仓库git clone https://gitcode.com/gh_mirrors/ct/ctf-tasks然后用socat或netcat把Dragon CTF 2018/Teaser/Production/task/lyrics绑定到 TCP 端口例如 4141再运行Dragon CTF 2018/Teaser/Production/solution/exploit.py即可。你也可以参考Dragon CTF 2018/Teaser/Production/README.md中的官方解题摘要对照每一步验证自己的理解。结语最隐蔽的漏洞往往藏在理所当然里Production 挑战没有花哨的利用技巧却把三个微小的问题——assert 被编译期删除、单层..未被过滤、栈缓冲未清零——环环相扣地串成一条完整的文件描述符泄漏攻击链。对 CTF 新手来说它是理解资源耗尽型攻击、防御逻辑绕过与栈残留泄漏的绝佳入门题对开发者来说它更是一记警钟生产级代码的防御代码可能从未真正执行过。想在 ctf-tasks 仓库里挑战更多类似的低层安全题目Dragon CTF 2021/Nim、Dragon CTF 2018/Pipeline同样是值得一试的 Linux x64 利用题祝你在攻防演练中玩得开心【免费下载链接】ctf-tasksAn archive of low-level CTF challenges developed over the years项目地址: https://gitcode.com/gh_mirrors/ct/ctf-tasks创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表