CBC翻转字节攻击

CBC又称分密码分组链接,在CBC模式中,每个部分都被分成块,通过异或之后经行加密,为保证消息的唯一性,在第一个模块中需要使用初始向量。

名词解释:

  • plaintext: 明文,待加密的数据
  • IV: 初始向量
  • ciphertext: 加密后的密文
  • key: 密钥

加密原理

1
2
3
4
5
6
7
原理:
ciphertext[0] = encrypt(IV ^ plaintext)--IV只用于第一块明文(16个字节为一组)
ciphertext[1:N] = encrypt(Previous ciphertext ^ plaintext)--用于第二块之后所有的明文

1.首先将初始向量(IV)与第一个明文块异或,异或之后加入key进行encrypt得到第一块ciphertext
2.将第一块ciphertext与第二个明文块异或,异或之后加入key进行encrypt得到第二块ciphertext
3.以此类推......最终密文是IV密文拼接而成的

解密原理

1
2
3
4
5
6
7
8
原理:
plaintext[0] = decrypt(ciphertext[0]) ^ IV--IV只用于第一块密文
plaintext[1:N] = decrypt(current ciphertext) ^ (previous ciphertext)--用于第二块之后所有密文

1.将IV从密文中提取出来
2.将IV与解密后的ciphertext进行异或,得到第一块plaintext
3.将第一块ciphertext与第二块解密后的ciphertext进行异或,得到第二块plaintext
4.以此类推......

攻击方法

这个解密过程中,攻击点有两个:IV 和 N-1个密文(N>1)

IV

假设我们存在一组这样的,A为plaintext,B为IV,C为decrypt(current ciphertext)

1
2
3
A = plaintext[0]=1 #old_str
B = IV = 10 #Old_IV
C = decrypt(ciphertext[0]) = 11

在这里明文通过A = B^C可以得到,假如我们需要改变第一块明文的结果,就需要改变IV

1
2
3
4
5
6
7
8
9
10
11
12
A = plaintext[0]=2 #需要改成的值 target_str
B = New_IV #新的初始向量
C = decrypt(ciphertext[0])
#推导New_IV过程
C ^ Old_IV = old_str
C = Old_IV ^ old_str
#需要
C ^ New_IV = target_str
C = New_IV ^ target_str
#所以
Old_IV ^ old_str == New_IV ^ target_IV
New_IV = Old_IV ^ old_str ^ target_str #获取新的初始向量公式

N-1个密文(N>1)

假如我们需要修改第N组明文内容,那么就可以攻击N-1个密文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
plaintext = [
'comment=helooooa',
'username=monitor',
'date=2019-1-12'
]
#我们需要将username变成isadmin,密文已知可控
old_pt[0] = plaintext[0] = 'comment=helooooa'
old_ct[0] = decrypt(ciphertext[0]) = 已知
old_ct[1] = decrypt(ciphertext[1]) = 已知
New_pt[1] = 'username=isadmin'
#获取New_ct[0]过程
old_ct[0] ^ old_ct[1] = old_pt[1]
old_ct[1] = old_ct[0] ^ old_pt[1]
#还需
New_ct[0] ^ old_ct[1] = New_pt[1]
old_ct[1] = New_ct[0] ^ New_pt[1]
#所以
old_ct[0] ^ old_pt[1] = New_ct[0] ^ New_pt[1]
New_ct[0] = old_ct[0] ^ old_pt[1] ^ New_pt[1]#获取N-1新的密文
#第N-1组新密文 = 第N-1组旧密文 ^ 第N组就明文 ^ 第N组新明文

2017 SKCTF login4

详细代码见题目地址

考核点:CBC翻转字节攻击的两个点,IV和密文

这里的IV和密文储存在cookie中,所以是我们可控的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#明文块:
a:2:{s:8:"userna
me";s:5:"test1";
s:8:"password";s
:7:"monitor"}
#需求--将test1改成admin,即需要的到第一块的新密文
#-*- coding:utf-8 -*-
#伪造密文,提交
import urllib
ciher = "KQLoqqk7v+aiHe2u9RLkCgxNNbMTFxd5ArHflVlA87crG/osNp6bwdxjzb5UYcYoy4AfWiQbYgygpp0h2/MK/A==".decode("base64")
old_pt = "me\";s:5:\"test1\";"
new_pt = "me\";s:5:\"admin\";"
for i in xrange(16):
cipher = cipher[:i]+chr(ord(cipher[i])^ord(old_pt[i])^ord(new_pt[i]))+cipher[i+1:]
print urllib.quote(cipher.encode("base64"))
#报错得到plain,伪造IV
plain = "U6m8Cig8+X/MJdhMyW9Wom1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjc6Im1vbml0b3IiO30=".decode("base64")
old_str = plain[:16]
new_str = 'a:2:{s:8:\"userna'
old_iv = 'cSA4biZczXmBIjoaftE4xg=='.decode('base64')
for i in xrange(16):
new_iv = old_iv[:i]+chr(ord(old_iv[i])^ord(old_str[i])^ord(new_str[i]))+old_iv[i+1:]
print urllib.quote(new_iv.encode("base64"))