# Encrypted Flag
首先将附件中的 chall 文件拖入 ida 中分析,同时结合 flag.enc 文件的内容可以知道:1. 加密算法是魔改版的 AES-128;2.flag.enc 文件不是纯密文,它的开头 256 个字节是一张随机生成的查找表(即自定义 AES 中的 S 盒),而文件的剩余部分才是真正用这个 S 盒加密后的 flag 密文;3. 这张 S 盒不是标准的 AES S 盒,而是由程序动态生成的,加密用的 128 位主密钥也是由程序动态生成的,同时无论是生成 S 盒还是生成密钥,程序都使用了 Linux 系统下一个可预测的伪随机数生成器(PRNG):glibc 的 random () 函数。
因此首先通过编写脚本读取 flag.enc 开头的 256 字节 S 盒,有了这个 s 盒就能反推出当初为了填入每一个数字,那个 8 位的随机数所有可能的值。然后用 Python 的 itertools.product 工具,将上一步得到的 4 组 8 位可能性进行笛卡尔积组合,还原出前 31 次调用 random () 时,每一次函数返回值的所有可能性。最后利用公式 state [i-31] + state [i-3],把之前状态的所有可能性组合起来,生成对当前状态 state [i] 的所有预测值。当脚本成功还原出生成 S 盒的前 64 个 random () 状态后,继续预测出第 65、66、67、68 个状态。根据 C 代码,这四个 32 位数拼接起来,正是那 16 字节的加密主密钥:
1 | 562dae334607277a7a05c7512a22f533 |
拿到密钥后就可以开始构建自定义解密程序:从 flag.enc 开头读取自定义 S 盒,flag.enc 中 S 盒之后的所有内容就是密文,再加上得到的密钥,把密文分块,用我魔改后的 AES 函数,配上自定义生成的轮密钥,进行一轮一轮的逆向解密即可得到 flag:
1 | uoftctf{175_ju57_435_bu7_w0r53} |