CBC Byte Flipping Attack

加密


Plaintext:待加密的数据。
IV:用于随机化加密的比特块,保证即使对相同明文多次加密,也可以得到不同的密文。
Key:被一些如AES的对称加密算法使用。
Ciphertext:加密后的数据。
在这里重要的一点是,CBC工作于一个固定长度的比特组,将其称之为块。在本文中,我们将使用包含16字节的块。

解密

因为作者讨厌高数,所以作者造了一些自己的公式(方便记忆):
Ciphertext-0 = Encrypt(Plaintext XOR IV)—只用于第一个组块
Ciphertext-N= Encrypt(Plaintext XOR Ciphertext-N-1)—用于第二及剩下的组块
注意:正如你所见,前一块的密文用来产生后一块的密文。

Plaintext-0 = Decrypt(Ciphertext) XOR IV—只用于第一个组块
Plaintext-N= Decrypt(Ciphertext) XOR Ciphertext-N-1—用于第二及剩下的组块
注意:Ciphertext-N-1(密文-N-1)是用来产生下一块明文;这就是字节翻转攻击开始发挥作用的地方。如果我们改变Ciphertext-N-1(密文-N-1)的一个字节,然后与下一个解密后的组块异或,我们就可以得到一个不同的明文了!You got it?别担心,下面我们将看到一个详细的例子。与此同时,下面的这张图也可以很好地说明这种攻击:

比如下面一段代码,定义了加密与解密方法,我们在调用test方法时,可以定义IV,明文,密文,但是不知道key是多少

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/python

#coding:utf-8
from Crypto.Cipher import AES
from binascii import b2a_hex,a2b_hex

def encrypt(iv,plaintext):
if len(plaintext)%16 != 0:
print "plaintext length is invalid"
return
if len(iv) != 16:
print "IV length is invalid"
return
key="1234567890123456"
aes_encrypt = AES.new(key,AES.MODE_CBC,IV=iv)
return b2a_hex(aes_encrypt.encrypt(plaintext))

def decrypt(iv,cipher):
if len(iv) != 16:
print "IV length is invalid"
return
key="1234567890123456"
aes_decrypt = AES.new(key,AES.MODE_CBC,IV=iv)
return b2a_hex(aes_decrypt.decrypt(a2b_hex(cipher)))


def test():
iv="ABCDEFGH12345678"
plaintext="0123456789ABCDEFhellocbcflipping"
cipher=encrypt(iv, plaintext)
print "cipher:"+cipher
de_cipher = decrypt(iv, cipher)
print "de_cipher:"+de_cipher
print a2b_hex(de_cipher)

test()

得到结果

1
2
3
cipher:4913ceb9d0a80cfe38a0d5f633c63eb27f1c833277f85bc2bf628cb7e0641851
de_cipher:3031323334353637383941424344454668656c6c6f636263666c697070696e67
0123456789ABCDEFhellocbcflipping

如果我们要使用cbc字节翻转攻击使得g变为G,首先对其进行分组

1
2
0123456789ABCDEF
hellocbcflipping

g是第二组的第16个字节,我们需要异或的是第一组密文的第16个字节,也就是cipher[15],因此需要将该字节修改为cipher[15] Xor ord(‘g’) Xor ord(‘G’)
修改test方法,增加一些内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#!/usr/bin/python

#coding:utf-8
from Crypto.Cipher import AES
from binascii import b2a_hex,a2b_hex

def encrypt(iv,plaintext):
if len(plaintext)%16 != 0:
print "plaintext length is invalid"
return
if len(iv) != 16:
print "IV length is invalid"
return
key="1234567890123456"
aes_encrypt = AES.new(key,AES.MODE_CBC,IV=iv)
return b2a_hex(aes_encrypt.encrypt(plaintext))

def decrypt(iv,cipher):
if len(iv) != 16:
print "IV length is invalid"
return
key="1234567890123456"
aes_decrypt = AES.new(key,AES.MODE_CBC,IV=iv)
return b2a_hex(aes_decrypt.decrypt(a2b_hex(cipher)))


def test():
iv="ABCDEFGH12345678"
plaintext="0123456789ABCDEFhellocbcflipping"
cipher=encrypt(iv, plaintext)
print "cipher:"+cipher
de_cipher = decrypt(iv, cipher)
print "de_cipher:"+de_cipher
print a2b_hex(de_cipher)
#-------------------adding 1 start-----------------------------------
bin_cipher = bytearray(a2b_hex(cipher))
bin_cipher[15] = bin_cipher[15] ^ ord('g') ^ ord('G')
de_cipher = decrypt(iv,b2a_hex(bin_cipher))
print "de_cipher2:"+de_cipher
print a2b_hex(de_cipher)
#-------------------adding 1 end-------------------------------------

test()

可以看到第二组明文解密之后的,最后一个字母变为大写了,但是因为修改了第一组的密文,所以第一组解密时变成乱码了

1
2
3
4
5
cipher:4913ceb9d0a80cfe38a0d5f633c63eb27f1c833277f85bc2bf628cb7e0641851
de_cipher:3031323334353637383941424344454668656c6c6f636263666c697070696e67
0123456789ABCDEFhellocbcflipping
de_cipher2:23ab74aa019b5d520559f09d6e461ef668656c6c6f636263666c697070696e47
#´t™õ]RYùnFˆhellocbcflippinG

此时可以修改IV的值来控制第一组密文解密后的结果,但是需要de_cipher2的内容,如果是将前十六个字节都修改为”X”,那么需要将IV与de_cipher2相应下标的值进行异或再与ord(‘X’)进行异或,最后的结果就是新的IV的值

再次修改test方法,增加一些内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/bin/python

#coding:utf-8
from Crypto.Cipher import AES
from binascii import b2a_hex,a2b_hex

def encrypt(iv,plaintext):
if len(plaintext)%16 != 0:
print "plaintext length is invalid"
return
if len(iv) != 16:
print "IV length is invalid"
return
key="1234567890123456"
aes_encrypt = AES.new(key,AES.MODE_CBC,IV=iv)
return b2a_hex(aes_encrypt.encrypt(plaintext))

def decrypt(iv,cipher):
if len(iv) != 16:
print "IV length is invalid"
return
key="1234567890123456"
aes_decrypt = AES.new(key,AES.MODE_CBC,IV=iv)
return b2a_hex(aes_decrypt.decrypt(a2b_hex(cipher)))


def test():
iv="ABCDEFGH12345678"
plaintext="0123456789ABCDEFhellocbcflipping"
cipher=encrypt(iv, plaintext)
print "cipher:"+cipher
de_cipher = decrypt(iv, cipher)
print "de_cipher:"+de_cipher
print a2b_hex(de_cipher)
#-------------------adding 1 start-----------------------------------
bin_cipher = bytearray(a2b_hex(cipher))
bin_cipher[15] = bin_cipher[15] ^ ord('g') ^ ord('G')
de_cipher = decrypt(iv,b2a_hex(bin_cipher))
print "de_cipher2:"+de_cipher
print a2b_hex(de_cipher)
#-------------------adding 1 end-------------------------------------
#-------------------adding 2 start-----------------------------------
bin_decipher = bytearray(a2b_hex(de_cipher))
bin_iv = bytearray(iv)
for i in range(0,len(iv)):
bin_iv[i] = bin_iv[i] ^ bin_decipher[i] ^ ord('X')
de_cipher = decrypt(str(bin_iv),b2a_hex(bin_cipher))
print "de_cipher3:"+de_cipher
print a2b_hex(de_cipher)
#-------------------adding 2 end-------------------------------------

test()

可以看到在不知道key的情况下,通过修改密文和IV(还有个条件是获得每次解密后的结果 ),可以控制输出的明文为自己想要的内容,而且只能从最后一组开始修改,并且每改完一组,都需要重新获取一次解密后的数据,要根据解密后的数据来修改前一组密文的值。

1
2
3
4
5
6
7
cipher:4913ceb9d0a80cfe38a0d5f633c63eb27f1c833277f85bc2bf628cb7e0641851
de_cipher:3031323334353637383941424344454668656c6c6f636263666c697070696e67
0123456789ABCDEFhellocbcflipping
de_cipher2:23ab74aa019b5d520559f09d6e461ef668656c6c6f636263666c697070696e47
#´t™õ]RYùnFˆhellocbcflippinG
de_cipher3:5858585858585858585858585858585868656c6c6f636263666c697070696e47
XXXXXXXXXXXXXXXXhellocbcflippinG

参考资料:1
2