感谢分享,有所收获。
对字节反转攻击的深入研究
前言
现在无论你是google还是百度,"字节反转攻击"的搜索结果整整几页都是"CBC字节反转攻击",看来字节反转攻击和CBC模式结合的想法已经可以说是深入人心,毕竟它的名字就叫"CBC反转字节攻击",但是不是这种攻击只有CBC分组模式才有呢?如果不用CBC分组模式,是不是就不存在这种重放攻击呢?笔者就这个问题进行了研究。
CBC
CBC模式下的字节反转攻击想必大家都不陌生了,这种攻击方式和分组加密的加密算法无关,是在处理明文加密后的密文块时出现了漏洞:前一块的密文可以影响后一块的明文。
这种图引用自国外一个大佬的文章,攻击手法是简单明了的:
C1 xor D(C2) = P2
那么我们令C1=C1 xor P2 xor P发送给服务器,其中P是我们想要篡改的明文,那么服务器会计算:
(C1 xor P2 xor P) xor D(C2) = (P2 xor D(C2)) xor P2 xor P xor D(C2) = P
也就达到了篡改的效果,下面是作者自己写的测试demo
from Crypto.Cipher import AES
from os import urandom
from Crypto.Util.strxor import strxor
class AES_CBC:
def __init__(self):
self.key = urandom(16)
self.iv = urandom(16)
def encrypt(self, plain):
aes = AES.new(self.key, AES.MODE_CBC, self.iv)
return aes.encrypt(plain)
def decrypt(self, cipher):
aes = AES.new(self.key, AES.MODE_CBC, self.iv)
return aes.decrypt(cipher)
plain = '1'*32
aes = AES_CBC()
cipher = aes.encrypt(plain)
print aes.decrypt(cipher)
cipher = strxor(strxor(cipher[:16], '1'*16), '2'*16)+cipher[16:]
print aes.decrypt(cipher)
运行结果为:
前16个字节乱码是因为我们篡改了C1后,对应的D(C1)也发生了改变
CFB
CFB模式可以将块密码转换为同步的流密码。流密码通俗点讲就是将明文逐字节的进行加密,它生成密钥流块,然后与明文块进行异或,然后获得密文。
先来看一下CFB的加解密模式吧,图片选自wikipedia:
那么按道理说,我们如果将密文的第一块反转,那么明文的第一块也应该被对应篡改,但事实并非如此,我们看这个测试demo
from Crypto.Cipher import AES
from os import urandom
from Crypto.Util.strxor import strxor
class AES_CFB:
def __init__(self):
self.key = urandom(16)
self.iv = urandom(16)
def encrypt(self, plain):
aes = AES.new(self.key, AES.MODE_CFB, self.iv)
return aes.encrypt(plain)
def decrypt(self, cipher):
aes = AES.new(self.key, AES.MODE_CFB, self.iv)
return aes.decrypt(cipher)
plain = '1'*32
aes = AES_CFB()
cipher = aes.encrypt(plain)
print aes.decrypt(cipher)
ct = strxor(strxor(cipher[:16], '1'*16), '2'*16)+cipher[16:]
print aes.decrypt(ct)
运行结果如下:
一片乱码?但是如果你仔细观察的话,会发现其实第一个字符已经被改成了2,那为什么后面的都是乱码呢?
我们修改一下代码,只更改第一个字符:
ct = strxor(strxor(cipher[:1], '1'*1), '2'*1)+cipher[1:]
程序运行结果如下:
第一个字符依然是2,但是为什么后面有15个字符没有被篡改呢?
其实,wikipedia的图并不能很好的展示CFB的工作模式 ,真实的CFB模式是这样的:
图中的Shift register代表的是移位寄存器,图中的s一般代表的是8bit也就是1字节,同时,如果我们更改了Ci,那么Ci是要被存放到下一个移位寄存器里的,并且这个Ci会一直保存在寄存器里,直到它慢慢从寄存器移出去,所以这个Ci会影响128/8=16个字节的明文,故最后有32-1-16=15个字节的明文没有受影响,所以,CFB模式也是有字节反转攻击的,只是我们每次只能改动一个字节,完整的攻击demo如下:
from Crypto.Cipher import AES
from os import urandom
from Crypto.Util.strxor import strxor
class AES_CFB:
def __init__(self):
self.key = urandom(16)
self.iv = urandom(16)
def encrypt(self, plain):
aes = AES.new(self.key, AES.MODE_CFB, self.iv)
return aes.encrypt(plain)
def decrypt(self, cipher):
aes = AES.new(self.key, AES.MODE_CFB, self.iv)
return aes.decrypt(cipher)
plain = '1'*32
aes = AES_CFB()
cipher = aes.encrypt(plain)
print aes.decrypt(cipher)
for i in range(32):
pt = aes.decrypt(cipher)
cipher = cipher[:i]+strxor(strxor(cipher[i], pt[i]), '2')+cipher[i+1:]
print aes.decrypt(cipher)
程序运行结果为:
OFB
OFB和CFB类似,也是将块密码转换为流密码的一种分组模式,加解密图示如下:
可以从解密模式发现,OFB模式和CBC模式比较相似,并且如果其中一块的密文进行了改变的话,并不会影响它后面的密文
所以类似的,OFB模式也存在字节反转攻击,同时由于流密码的性质,我们可以很简单的做到对明文任意长度字符的篡改
demo如下:
from Crypto.Cipher import AES
from os import urandom
from Crypto.Util.strxor import strxor
class AES_OFB:
def __init__(self):
self.key = urandom(16)
self.iv = urandom(16)
def encrypt(self, plain):
aes = AES.new(self.key, AES.MODE_OFB, self.iv)
return aes.encrypt(plain)
def decrypt(self, cipher):
aes = AES.new(self.key, AES.MODE_OFB, self.iv)
return aes.decrypt(cipher)
plain = '1'*32
aes = AES_OFB()
cipher = aes.encrypt(plain)
print aes.decrypt(cipher)
for i in range(32):
ct = strxor(strxor(cipher[:i+1], '1'*(i+1)), '2'*(i+1))+cipher[i+1:]
print aes.decrypt(ct)
运行结果如下
总结
虽然名字叫做"CBC字节反转攻击",但并不是只有CBC模式才会有这种攻击手法,CFB和OFB模式都是不能抵御这种攻击的,不能认为如果不用CBC模式以后就能完全避免这种密文重放攻击了。