玄机靶场通关笔记:第九届 XCTF 国际网络攻防联赛总决赛 - Tch3s

张开发
2026/4/19 11:47:04 15 分钟阅读

分享文章

玄机靶场通关笔记:第九届 XCTF 国际网络攻防联赛总决赛 - Tch3s
第九届 XCTF 国际网络攻防联赛总决赛 - Tch3s 通关笔记一、题目信息这题是玄机平台上一道Crypto方向的极难题附件里给了两个文件一个是可执行文件Tch3s另一个是程序跑出来的结果文件output。从output的内容可以看出程序会先输出一段encrypted flag然后连续生成多组随机明文分别打印对应的加密结果、解密结果和耗时信息。我先把平台附件重新下载到本地做了核对。output第一行里的密文是B789EB607A91D08888E2C4C4E4D9573FF5333E4783390B91EDB9D2B4FD2A5FC0和公开题解中的样本完全一致所以可以确定平台上的附件和公开题解分析的是同一道题。[1]项目内容题目Tch3s平台玄机难度极难类型Crypto附件Tch3s、outputFlagflag{tim1ng_a7tck_1s_dangerous}二、程序大致逻辑结合output文件和公开题解这个程序的核心流程并不复杂先调用srand(time(NULL))然后用rand() 0xFF生成 16 字节 key再用这把 key 去加密 flag。之后程序会继续生成多组随机明文分别做加密和解密测试并把结果和耗时打印出来。题目的突破口不在加密算法本身而在随机数种子。因为这里直接用了time(NULL)作为srand的种子搜索空间其实很小再加上output里直接给出了第一组随机明文所以可以反推出当时的 seed。“这里的问题是srand的 seed 空间比较小可以通过 plaintext 的规律逆向找出 seed从而把 key 求出来。”[1]这句话基本就是本题的核心。只要 seed 能还原程序内部当时生成的 key 就有机会被重新复现出来。三、种子恢复output里第一组随机明文如下720B4455C91B6A024135343386B6679D需要注意的是这 16 字节明文并不是srand之后立刻生成的。程序在生成它之前已经先调用了 16 次rand()来生成 key所以爆破 seed 的时候必须先跳过这 16 次调用再去比对后面生成出来的 16 个字节是否和样本一致。我本地写了个脚本按时间戳区间去穷举逻辑就是先srand(seed)再调用 16 次rand()模拟生成 key然后继续取 16 个字节与output中第一组明文逐字节比较。最终恢复出来的结果如下。项目结果seed1753843495东八区时间2025-07-30T10:44:5508:00校验方式首组明文完全匹配这个 seed 和公开题解给出的结果一致说明前面的判断没有跑偏。四、如何拿到 flag有了正确的 seed后面的思路就清楚了。公开题解里没有重新实现整套加解密算法而是直接复用了原程序本身的逻辑通过动态库劫持srand把程序运行时传进去的随机种子替换成已经恢复出来的1753843495。这样程序再次执行时就会进入和出题时相同的随机状态内部生成出的 key 也会一致。接着在调试器里对相关函数下断点把待处理的缓冲区替换成output中给出的 flag 密文再直接调用程序内部已有的解密函数就能把明文解出来。[1] 这一步的关键点其实不是“把算法彻底逆完”而是想办法让程序自己替你完成最后的解密。这类题里能复用原程序逻辑的时候通常都比自己从头重写一套解密过程更省事也更不容易出错。五、最终结果最后恢复出的 flag 是flag{tim1ng_a7tck_1s_dangerous}这个结果已经在平台上验证通过。阶段关键点结果样本核对比对output首行密文确认与公开题解同题种子恢复穷举时间戳并匹配首组明文得到1753843495状态复现劫持srand强制使用正确 seed重建正确随机状态解密 flag复用程序内部解密逻辑得到最终 flag六、总结这题本质上是在考弱随机数种子带来的连锁问题。算法本身未必是主要矛盾真正致命的是 key 来源依赖time(NULL)这样的低熵种子而且输出里还泄露了足够多的随机样本导致 seed 可以被反推出来。实战里碰到类似题目可以优先看三件事一是关键密文和测试样本是不是共享同一随机状态二是程序是否用了rand()这类容易预测的 PRNG三是能不能不重写算法而是直接借助程序自己的逻辑完成解密。这个题基本把这三点都踩全了。

更多文章