CBC 字节翻转攻击

本文最后更新于:4 年前

CBC 字节翻转攻击的记录

原理

CBC 模式就不多说了,只放解密过程
attack.jpg
可以看到,当修改前一个 Block1 时,会影响下一个 Block2 的解密,因为前一个 Block1 是作为后一块 Block2 的 IV 的

这就是字节翻转攻击开始发挥作用的地方
如果我们改变 Block1 的一个字节,然后与下一个解密后的组块 Block2 异或,我们就可以得到一个不同的明文了

栗子

M = a:2:{s:4:"name";s:6:"sdsdsd";s:8:"greeting";s:20:"echo 'Hello sdsdsd!'";}
我们的目标是将 s:6 当中的数字 6 转换成数字 0。我们需要做的第一件事就是把明文分成 16 个字节的块:

  1. Block 1:a:2:{s:4:"name";
  2. Block 2:s:6:"sdsdsd";s:8
  3. Block 3::"greeting";s:20
  4. Block 4::"echo 'Hello sd
  5. Block 5:sdsd!'";}

我们的目标字符位于块 2,这意味着我们需要改变块 1 的密文来改变第二块的明文。

知道改哪了,那么要改成啥呢?
设图中第一个红块为 C1,它对应的明文块为 M1
第二个红块为 M2,它经过加密函数后的值记为 I(还没经过异或的值),它对应的密文块为 C2

根据这个流程,可知:
C1 ^ I = M2
对应上面的那个例子,就是
C1 为’2’对应的密文
I 为’6’经过加密函数后的值
M2 即为’6’

我们要求 C1’的值,根据异或特性,有
C1’ = M2’ ^ I = M2’ ^ C1 ^ M2
要想使’6’成为我们想要得到的值,C1’ 就要满足上述条件

代码

Attack

Attack.py 是攻击脚本

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
# -*- coding: cp936 -*-
from Server import *
from re import findall
from Crypto.Util import strxor

# 将位于第 Block 个块中第 loc 个字符修改为 char
Block = 1
loc = 7
char = '0'
rawM = '''a:2:{s:4:"name";s:6:"sdsdsd";s:8:"greeting";s:20:"echo 'Hello sdsdsd!'";}'''
#-------------------------------------------------------------------------
print '[!]将 rawM 中的 s:4:"name"; 改为 s:0:"name";'

Blockn, b = divmod(len(rawM), 16)
if b: Blockn = Blockn + 1
rawM = rawM[::-1].zfill(Blockn * 16)[::-1]
C = CreateC(rawM)

print '[+]The rawM is:'
splitM = findall('.{16}', rawM)
for m in splitM: print ' [-]' + m

print '[+]The rawC is:'
splitC = findall('.{32}', C)
for c in splitC: print ' [-]' + c

changeBlock = findall('.{2}', splitC[Block - 1])

print '[!]Block', Block - 1, 'loc', loc, 'char had changed from', changeBlock[loc], 'to',
changeBlock[loc] = hex(int(changeBlock[loc], 16) ^ ord(rawM[(Block-1) * 16 + loc]) ^ ord(char))[2:].zfill(2)
print changeBlock[loc]

splitC[Block-1] = ''.join(changeBlock)

modifyC = ''.join(splitC)
print '[+]The attackedC is:'
for c in findall('.{32}', modifyC): print ' [-]' + c

CheckM = Check(modifyC.decode('hex'))
print '[+]The attacked M is:'
for c in findall('.{16}', CheckM[16:]): print ' [-]' + c

print '[!]All Done!'


模拟服务端

Server.py 对传入的 M 进行加密,使用随机的 Key 以及 IV,并返回解密的密文

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
# -*- coding: cp936 -*-
from Crypto.Cipher import AES
from Crypto import Random
import random
import string

cipher = None

def CreateC(M):
'''使用随机的 key 与 iv 加密明文,返回密文'''

global cipher
K = list(string.ascii_letters + string.digits)
iv = K[:]
random.shuffle(K)
iv = Random.new().read(AES.block_size)
cipher = AES.new(''.join(K[:32]), AES.MODE_CBC, iv)

return iv.encode('hex')+cipher.encrypt(M).encode('hex')



def Check(C):
'''检查 CBC 字节翻转攻击是否成功'''

global cipher
M = cipher.decrypt(C)
return M

结果

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
[!]将 rawM 中的 s:4:"name"; 改为 s:0:"name";
[+]The rawM is:
[-]a:2:{s:4:"name";
[-]s:6:"sdsdsd";s:8
[-]:"greeting";s:20
[-]:"echo 'Hello sd
[-]sdsd!'";}0000000
[+]The rawC is:
[-]bcb20b483bfb9d718ec95a35b2d5ca4d
[-]88a9e8623ea4f5d920313dda254316d5
[-]aed2b33aeada7885f64302a9c1dc2975
[-]df9a6c328d837d1169a34e71d4d0344b
[-]86fcf2c5f11135aa218b3d52958daa41
[-]b38ab120da3bd7d301580bafcd3f1ab5
[!]Block 0 loc 7 char had changed from 71 to 75
[+]The attackedC is:
[-]bcb20b483bfb9d758ec95a35b2d5ca4d
[-]88a9e8623ea4f5d920313dda254316d5
[-]aed2b33aeada7885f64302a9c1dc2975
[-]df9a6c328d837d1169a34e71d4d0344b
[-]86fcf2c5f11135aa218b3d52958daa41
[-]b38ab120da3bd7d301580bafcd3f1ab5
[+]The attacked M is:
[-]a:2:{s:0:"name";
[-]s:6:"sdsdsd";s:8
[-]:"greeting";s:20
[-]:"echo 'Hello sd
[-]sdsd!'";}0000000
[!]All Done!


来呀快活呀