十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

随机数预测与任意写:ctf-tasks Nim 挑战的完整利用链拆解

随机数预测与任意写:ctf-tasks Nim 挑战的完整利用链拆解 随机数预测与任意写ctf-tasks Nim 挑战的完整利用链拆解【免费下载链接】ctf-tasksAn archive of low-level CTF challenges developed over the years项目地址: https://gitcode.com/gh_mirrors/ct/ctf-tasksctf-tasks 是一个收录多年 CTF 低层攻防题目的开源存档库其中「Dragon CTF 2021」的Nim 挑战堪称利用链设计的典范它把随机数预测与任意写write-what-where两条漏洞链严丝合缝地串在一起在 NX、PIE、全 RELRO、栈金丝雀全部开启的 Linux x64 程序上完成了一次优雅的 getshell。整场只有 5/247 支队伍解出这道 408 分的题本文就带你从零拆解这条完整利用链。挑战档案一场只有 5 支队伍解出的 CTF 漏洞利用题项目内容题目名称Nim比赛Dragon CTF 2021分类漏洞利用Exploitation平台Linux x64分值 / 难度408 分中等解题队伍5 / 247题目文件Dragon CTF 2021/Nim/task/程序实现了一个「玩家 vs 庄家」的 Nim尼姆取石子游戏现代缓解措施全开NX、PIE、Full RELRO、Canary却藏着两处隐蔽漏洞随机数种子泄露与连续栈缓冲区溢出。题目说明与官方解法摘要见 README。规则速览Nim 游戏与「尼姆和」必胜策略Nim 的规则很简单若干堆石子双方轮流从任意一堆取走至少 1 颗取走最后一颗者获胜。判断局势的关键是尼姆和nim-sum即所有堆大小的异或值尼姆和 ≠ 0 → 当前行动方处于必胜局面尼姆和 0 → 当前行动方必败对手按必胜策略走必胜策略找到一堆使其满足堆大小 ^ 尼姆和 堆大小把它减到堆大小 ^ 尼姆和即可把尼姆和归零交给对手。这是后文 exploit 中WinGame的核心逻辑也是把庄家安排进必败局面的理论基础。随机数预测两个堆大小如何泄露 libc 地址用地址做种子PRNG 形同虚设程序用 libc 中rand函数的地址作为内部 PRNG 的种子而该 PRNG 是标准的线性同余生成器LCGstate (state × 561860773102413563) mod 1152921504606846883 输出值 state 0x7fffffff游戏里生成的堆大小正是 PRNG 的输出。只要拿到连续两个输出理论上就能反推出种子——因为种子本身就是我们要泄露的秘密。反推 rand2^10 次枚举还原完整地址开局输入 4 堆石子程序用 PRNG 再生成 4 堆庄家先走一步。根据庄家第一步拿走的数量可以还原第一、二堆的原始大小第一堆原始大小 rand地址的低 31 位输出值取state 0x7fffffff高位地址按页对齐只需枚举2^10个候选补齐中间 10 位用候选地址跑一次 LCG若输出等于第二堆大小候选即命中至此随机数预测完成不仅拿到rand的完整 64 位地址还恢复了 PRNG 内部状态。由于rand在 libc 中的偏移固定0x4AE90libc 基址 rand 地址 - 0x4AE90之后每一局的所有堆大小全部尽在掌握。栈溢出升级如何构造 32 位任意写原语Nim 堆大小的初始化存在连续的栈缓冲区溢出用户输入的堆大小以 32 位为单位写入栈上数组输入越多溢出越深。又因为现代 clang 在 x64 下不会把第 7 个及以后的函数参数经栈传递复制到局部缓冲区下方栈上的参数指针完全暴露在溢出范围内。两条线索叠加出本挑战最精妙的设计每次开局玩家的高分score会写入第 8 个函数参数所指向的地址溢出恰好可以改写这个指针于是得到32 位 write-what-where原语想写的值what 玩家分数想写的位置where 被溢出的指针。分数本身既是赌注、又是写入值可以靠下棋慢慢赌出来。完整利用链随机数预测 × 任意写 × ROP 四步拿 shell第一步泄露 libc 基址掌握全部堆大小按上文方法反推rand地址与 libc 基址同时恢复 PRNG 状态。注意一个鲁棒性细节如果 libc 基址的第 31 位为 132 位写入会跨边界产生进位exploit 会检测到并自动重连重试约 50% 概率命中。第二步加倍下注把分数刷成写入值任意写只写 32 位而写入值就是玩家分数。目标是把分数刷到栈迁移 gadget地址的低 32 位。技巧是加倍下注每局下注min(当前分数, 目标分数 - 当前分数)赢了分数近乎翻倍从 9999 分到目标只需约 6 局。同时利用预测出的庄家堆大小通过[1,2,3,xor]等巧妙开局让庄家永远处于必败局面保证局局必胜。第三步溢出改写 strlen 的 GOT 表项最后一局故意下注 1 分并认输把分数停在0x55040栈迁移 gadget 的低 32 位。随后输入超长堆大小序列触发溢出前两个 32 位值把第 8 个参数指针改写为strlen在 libc 中的 GOT 地址程序把分数写入该地址把strlen的 GOT 表项改写成add rsp, 0x398; ret迁移 gadget紧接着程序在函数返回时检测到金丝雀被破坏调用__stack_chk_fail→__fortify_fail→__libc_message。这条报错路径内部会调用strlen而它用的正是一组未被保护、可写的 GOT 指针——控制流就此被接管。全 RELRO 也挡不住这正是本题最大的看点。第四步栈迁移 经典三段 ROP栈迁移 gadget 把 RSP 加上 0x398恰好落进溢出数据区中预先铺好的 ROP 链pop rdi; ret→/bin/sh字符串 →system干净利落地弹出 shellcat flag.txt收工。关键偏移速查表相对 libc 基址目标偏移rand0x4AE90system0x55410/bin/sh 字符串0x1B75AApop rdi; ret0x26B72栈迁移 gadgetadd rsp, 0x398; ret0x55040strlen 的 GOT 表项0x1EB0A8一键复现exploit.py 的完整使用流程整套利用已封装进官方脚本Dragon CTF 2021/Nim/solution/exploit.py注释详尽、开箱即用获取项目文件git clone https://gitcode.com/gh_mirrors/ct/ctf-tasks在 Linux x64 上把Dragon CTF 2021/Nim/task/nim绑定到端口如 4141可用 socat 或 nc 转发 stdin/stdout运行python2 exploit.py 127.0.0.1 4141脚本自动完成泄露 → 刷分 → 溢出 → ROP 全流程拿到 shell 后读取同目录的flag.txt注意必须使用题目自带的Dragon CTF 2021/Nim/task/libc.so偏移才完全匹配 ✅总结与延伸全防护之下利用链依然成立随机数预测的根源是用地址当种子——算法与模数公开时输出即泄露内部状态任意写未必来自数组越界写栈参数被溢出劫持同样致命32 位写配合精心挑选的地址也能改写 GOT全 RELRO 不代表 GOT 绝对安全glibc 内部报错路径__libc_message使用的部分 GOT 指针依旧可写金丝雀、NX、PIE 全开时利用链依然完整——防护永远只是提高门槛这道题把两个小漏洞拧成一条自洽的利用链是学习 pwn 的绝佳素材。完整题目、二进制与注释版 exploit 都在Dragon CTF 2021/Nim/目录下强烈建议亲手跑一遍、逐行读一遍收获会比看任何教程都大。【免费下载链接】ctf-tasksAn archive of low-level CTF challenges developed over the years项目地址: https://gitcode.com/gh_mirrors/ct/ctf-tasks创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表