对于 CBC 翻转字节攻击以及 Padding Oracle Attack 这块的知识一直不怎么会运用,所以今天复现了一道在Xman 夏令营打排位赛的一道密码题,算是 Padding Oracle 的一个简化版,思想大同小异,只是这个题没有和服务器交互。
这个题也是仿 hack.lu 2016 的一道题,文章末尾有链接地址。
看下题,给了AESCipher.py
,lockfile.py
和flag.encrypted
三个文件。
代码:
AESCipher.py
1 | from Crypto import Random |
lockfile.py
1 | #!/usr/bin/env python3 |
可以看到加密程序对于 key 的处理很特别,八字符的 key 被分成了四组,每组两个字符哈希后作为 AES 密钥,把明文加密了四次。采用的是下一轮加密上一轮的密文这种形式。
注意到这里的填充方式:
1 | def _pad(self, s): |
采用的是类似 PKCS5 的填充方式,也就是说无论明文多少位都需要填充。
直接爆破八个字符显然不可能,数量级是len(dict)^8
,我们考虑把每步分解,如果四轮加密中,每一轮都能知道当前的两个字符是否正确,那么数量级就变成了4*(len(dict)^2)
,还是很容易的。
既然使用了这个填充规则,每一轮就可以通过判断解密后的最后填充来判断解密密钥是否正确,这是判断填充正确与否的代码:
1 | def checkPadding(raw): |
最后一个字节是chr(0)
肯定不正确,最后一个字节是chr(1)
倒是有可能正确,正好缺了一个字节,但只会发生在填充最原始的明文的时候,因为第二轮开始加密的都是上一轮的密文,都是 AES 分组大小的倍数,不可能差一个字节,而且爆破的过程中如果解密出来最后一个字节正好是 chr(1),就会认为填充正确,判断错误的几率还是很大的,所以代码里直接返回 False 了。
exp 如下:
1 | # -*- coding:utf-8 -*- |
需要注意的是解密出来的明文是一串数字:12010997110123651081141019710012145761019711411010110045676667458097100100105110103125
用正常 hex解码
是乱码,观察发现首部 120,109,97 都像是 ascii 值,数字其实是 flag 字符串每个字符十进制 ascii 值连起来的。
运行结果: xman{Already-Learned-CBC-Padding}
参考: