padding oracle attack
问题描述
In this assignment, you must decrypt a challenge ciphertext generated using AES in CBC-mode with PKCS #5 padding. (Note: technically this is PKCS #7 padding,since the block size of AES is 16 bytes. But the padding is done in exactly the same way as PKCS #5 padding.) To do so, you will be given access to a server that will decrypt any ciphertexts you send it (using the same key that was used to generate the challenge ciphertext)…but that will only tell you whether or not decryption results in an error!
要求解密一个 CBC 工作模式,PKCS #7 方式填充,AES 加密的挑战密文。为此,你需要访问一个服务器(该服务器能用挑战密钥解密任何你发送的密文),它将返回你发送的密文是否填充正确。
All the files needed for this assignment are available here, including a README file that should explain everything.
此作业所需的所有文件都已在这里给出,包括一个解释一切的 README 文件。
Note that this assignment requires the ability to perform basic networking. Because we do not assume students necessarily know this, we have provided stub code for doing basic networking in C, Java, Ruby, and Python, but you are welcome to use any language of your choice as long as you are able to write code for basic networking functionality in that language. (Students may feel free to post stub code in other languages for the networking component on the discussion boards.)
注意到此作业需要执行基本网络的功能。因为我们不要求学生必须知道这一点,所以我们提供了在 C,Java,Ruby 和 Python 中进行基本网络连接的存根代码,但是欢迎使用任何您选择的语言,只要您能够编写代码该语言的基本网络功能。(学生可以自由地在讨论板上为其他语言的网络组件发布存根代码。)
The first step in this project is to send the challenge ciphertext to the server, and verify that you receive back a “no error” message. Once you can do that, the rest is “just” crypto…
该项目的第一步是将挑战密文发送到服务器,并验证您是否收到 “无错误” 消息。一旦你可以做到这一点,其余的是 “只是” 加密…
The plaintext,when converted to ASCII, is readable English text, and so you should be able to tell once you have been successful. Once you have successfully recovered the plaintext (in ASCII).
明文转换为 ASCII 时,是可读的英文文本,所以一旦成功恢复明文,便可得知。
前置技能
CBC 工作模式
CBC 模式是一种分组链接模式,目的是为了使原本独立的分组密码加密过程形成迭代,使每次加密的结果影响到下一次加密。初始化向量 IV 与第一组明文(分组 1)异或后,再经过运算得到的结果作为新的 IV,用于下一分组(分组 2),不断迭代下去:
PKCS #7 填充
以整字节填充。每个填充字节的值是用于填充的字节数,即是说,添加 N 个字节,每个的值都是 N。 填充的字节数取决于信息末尾到块边缘的距离。
如:
分组长度8bytes
dd: 数据
缺少 1 位(1 个 Padding), 填充位为: 0x01
1 | dd dd dd dd dd dd dd 01 |
缺少 5 位(5 个 Padding), 填充位为: 0x05 0x05 0x05 0x05 0x05
1 | dd dd dd 05 05 05 05 05 |
padding-oracle attack 攻击原理
padding-oracle attack 针对 CBC 工作模式攻击,与具体加密算法无关。
服务器判断解密出的明文是否出错一般从后开始检验明文末尾字节的值,若末尾为0x01
则末尾一字节为0x01
,填充末尾为0x03
则末尾三字节均为0x03
。利用服务器的返回值,可以在不知道密钥的情况下得到明文。
假设
例如现在有一个 CBC 工作模式,PKCS #7 方式填充的密文 IV||C(分组大小为 8 字节):
1 | 21 A2 DD 04 35 0A BA 58 | CD DD AA DD 34 23 A1 64 | |
为了方便描述,用 $C[i]$ 表示第 $i$ 个分组,$C[i][j]$ 表示第 $i$ 个分组第 $j$ 个字节,$D(*)$ 表示解密算法。
我们想要解密最后一个分组 $C[n]$ ,即求 $P[n]$ 。
先求明文最后一个分组的最后一位,即 $P[n][7]$ 。
最后一位
假设此时仅填充一个字节,构造初始向量 $IV_0$ 值为:
1 | | 00 00 00 00 00 00 00 00 | |
向服务器发送这两个分组构成的密文 $IV_0||C[n]$ :
1 | | 00 00 00 00 00 00 00 00 | CD DD AA DD 34 23 A1 64 | |
要使服务器的返回值为 $1$(即显示 Padding 正确),那么明文最后一位应该为 0x01
。
那么我们需要遍历 $[0,256)$ 修改 $IV_0[7]$ 的值,直到服务器的返回值为 $1$ 。
假设此时初始向量 $IV_1$ 为:
1 | | 00 00 00 00 00 00 00 A0 | |
此时, 明文末尾一位为 $(D(C[n]) \oplus IV_1)[7] == 0x01$。
于是可以得到
$$
D(C[n])[7] = 0x01 \oplus IV_1[7] \\
P[n][7] = D(C[n])[7] \oplus C[n-1][7] = 0x01 \oplus IV_1[7] \oplus C[n-1][7]\\
$$
至此,明文最后一个分组的最后一位已经得到,接下来求明文最后一个分组的倒数第二位。
倒数第二位
1.修正最后一位
目的: 使明文的最后一位是
0x02
方法: 在已知a, b, 且 a xor b = 1 的情况下, 容易获得a’ s.t. a’ xor b = 2
此时假设填充了两个字节,即明文末尾两位均为 $0x02$ 。
由于 $D(C[n][7]) = 0x01 \oplus IV_1[7]$ ,所以要使 $D(C[n][7]) \oplus IV_1^{‘}[7] = 0x02$ 时,有
$$
IV_1^{‘}[7] = D(C[n])[7] \oplus 0X02 = 0X01 \oplus IV_1[7] \oplus 0X02
$$
此时的初始向量 $IV_1^{‘}$ 为:
1 | | 00 00 00 00 00 00 00 A3 | |
2.获取倒数第二位
目的: 使明文的倒数第二位是
0x02
, 以满足padding的要求
遍历 $[0,256)$ 修改 $IV_1^{‘}[6]$ 的值,直到服务器的返回值为 $1$ 。
假设此时初始向量 $IV_2$ 为:
1 | | 00 00 00 00 00 00 40 A3 | |
由于, 明文倒数第二位为0x02
: $D(C[n])[6] \oplus IV_2[6] = 0x02$
于是可得:
$$
D(C[n])[6] = 0X02 \oplus IV_2[6] \\
P[n][6] = D(C[n])[6] \oplus C[n-1][6] = 0X02 \oplus IV_2[6] \oplus C[n-1][6]\\
$$
明文最后一个分组的倒数第二位也已经得到。
依次类推,由此即可以仅根据密文还原出明文。
一般情况
第i轮: 从1开始,注意写代码时数组从0开始
1.准备
开3个数组
- 初始向量: iv = [全0(数字)]
- 中间值(D(c)): Ivalue= []
- 明文: m = []
2.修正
$IV_{i-1}->IV_{i-1}^{‘}$: $Ivalue$ 中的所有非零值与 $0xi$ 进行异或
3.遍历
获取$IV_{i}$
- iv[-i] 赋值
4.计算数据,并保存
$$
Ivalue[-i] = D(C[n][-i]) = 0xi \oplus IV_i[-i] \\
P[n][-i] = D(C[n][-i]) \oplus C[n-1][-i]
$$
- Ivalue.append()
- m.append()
exp
模拟
由于没连接上服务器,所以用随机数来表示是否能正确解密XD
之后需修改judge函数
1 | import random |
1 | Detecting Block 1 |
确认服务器可用
1 | from pwn import * |
最后若返回1
则说明服务器正常
交互
1 | from pwn import * |
1 | Detecting Block 1 |
1 | Yay! You get an A. =) |