攻击场景:对称密码采用PKCS5或PKCS7 padding,存在接口可以为攻击者提供多次解密。
攻击原理:由于PKCS5和PKCS7的填充规则,每次解密以后通过检验填充部分的内容来确定解密是否正确,即key是否正确。遍历可能的key进行解密和验证,确定正确的key。
危害程度:出现过相关的CVE,高危。
例1.Xman 夏令营的一道题,给了AESCipher.py
,lockfile.py
和flag.encrypted
三个文件
AESCipher.py
1 | from Crypto import Random |
lockfile.py
1 | #!/usr/bin/env python3 |
可以看到加密程序对于 key 的处理很特别,八字符的 key 被分成了四组,每组两个字符哈希后作为 AES 密钥,把明文加密了四次。采用的是下一轮加密上一轮的密文这种形式。
padding是PKCS5:
1 | def _pad(self, s): |
直接爆破八个字符显然不可能,数量级是len(dict)^8
,我们考虑把每步分解,如果四轮加密中,每一轮都能知道当前的两个字符是否正确,那么数量级就变成了4*(len(dict)^2)
,就能减少运算复杂度。而由于padding是PKCS5,是可以验证每轮解密是否正确的。check代码:
1 | def checkPadding(raw): |
解密得到的明文最后字节是0的时候,key一定错误,因为没有填充0的用法;最后一位字符是1是有可能的,正好缺了一个字节,但只会发生在填充最原始的明文的时候,因为第二轮开始加密的都是上一轮的密文,都是 AES 分组大小的倍数,不可能差一个字节,而且爆破的过程中如果解密出来最后一个字节正好是 chr(1),就会认为填充正确,判断错误的几率还是很大的,所以代码里直接返回 False 更容易找到正确的明文。
完整exp:
1 | # -*- coding:utf-8 -*- |
上述题目是最简单的padding类型,解更复杂的题目则需要利用CBC模式特征,如例2
还有其他攻击场景,比如在不知道密钥的情况下,完成数据的加密,绕过服务端的校验(解密成功+明文有效),达到攻击的目的。
还有类似于例3这样的变式题;这是tjctf的一道题,padding方式虽然略有不同,但核心思想一致。