menu Ga1@xy's Wor1d
Padding Oracle Attack
489 浏览 | 2021-10-01 | 阅读时间: 约 4 分钟 | 分类: CRYPTO | 标签: 知识点,Crypto
请注意,本文编写于 111 天前,最后修改于 111 天前,其中某些信息可能已经过时。

CBC模式

CBC(Cipher Block Chaining)即密码分组链接,是一种加密模式。在CBC模式中,每个明文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每个密文块都依赖于它前面的所有明文块。同时,为了保证每条消息的唯一性,在第一个块中需要使用初始化向量。

  • 加密流程图

  • 解密流程图

由流程图可以看出解密就是加密的逆过程,但有几点图中没有提到:

  1. 在加密前进行明文分组时,通常分为8字节(64bit)或16字节(128bit)一组,当明文长度不是8字节或16字节的整数倍时,在明文的最后会进行填充,通常采用 PKSC #5 或 PKCS #7 填充规则
  2. 我们通常把解密过程中密文与密钥进行 xor 后产生的字符串成为中间值 Intermediary Value,中间值与 IV 进行 xor 得到第一组明文,以此类推

PKCS #5 与 PKCS #7

PKCS #5 与 PKCS #7 的唯一不同就是前者只适用于8字节以内的填充,而后者填充范围包括 0x00~0xff

以 PKCS #5 为例,将明文填充到 8 字节的整数倍,如果明文已经满足 8 字节的整数倍,那就在明文的最后再添加 8 字节的填充,而填充的内容取决于需要填充的长度,如果需要填充 7 个字节,那就要填充 7 个 0x07,如果明文已经满足 8 字节的整数倍,那就在明文的最后再填充 8 个 0x08

DES算法进行加密时的填充规则是 PKCS #5、填充最多 8 位,而AES算法进行加密时的填充规则是 PKCS #7、填充最多 16 位。

Padding Oracle

Padding 意为填充,Oracle 在此意为提示,在对密文进行解密的过程中,如果解密得到的结果末尾不符合填充规则,那通常相应的解密库会抛出一个异常,提示填充不正确。简单来说,Padding Oracle 就是提示数据不符合填充规则的意思。

攻击详解

Padding Oracle Attack,顾名思义,就是根据返回给我们填充数据是否符合规则的提示进行攻击的方式,此种攻击方式有几个前提条件

  • 攻击者已知密文及初始向量 IV,并且已知服务端使用 CBC模式 进行加密
  • 攻击者能够向服务端提交密文数据并触发解密操作,攻击者能够根据服务端的返回值判断解密是否正常

基本原理

已知服务端可以根据检测填充是否正确来为我们返回不同的值,即可以实现 Padding Oracle,那么我们就可以根据其不同的返回值不断构造初始向量 IV 来爆破中间值(Intermediary Value),例如

  • 已知第一组密文:813EC9D944A5C8347A7CA69AA34D8DC0
  • 已知初始向量 IV:9F0B13944841A832B2421B9EAF6D9836

由于填充是在每个明文的末尾位置,所以我们构造 IV 需要从结尾开始,当填充为 0x01 时,按照填充规则,只填充末尾最后一位,此时如果服务器返回解密正确,则明文的形式应该为 ******************************01,那么我们从 0x00~0xff 爆破最后一位的 IV,得到当最后一位为 0x17 时解密正确,即

Padding: 0x01
iv:      00000000000000000000000000000017
plain:   ******************************01

此时我们利用填充位 xor 构造的 IV 对应位,即可得到正确的中间值,因为在密文不变的情况下,解密得到的中间值是始终不变的

Padding: 0x01
iv:      00000000000000000000000000000017
IValue:  00000000000000000000000000000016
plain:   ******************************01

经过第一轮的爆破,我们已知了中间值的最后一位,那当我们开始第二轮爆破时,就可以预先通过已知的中间值与本轮的填充计算出 IV 的已知位

Padding: 0x02
iv:      00000000000000000000000000000014
IValue:  00000000000000000000000000000016
plain:   ******************************02

再通过爆破倒数第二位的 IV,计算出倒数第二位的中间值

Padding: 0x02
iv:      0000000000000000000000000000f414
IValue:  0000000000000000000000000000f616
plain:   ****************************0202

依次类推,我们就可以爆破出完整的中间值

Padding: 0x10
iv:      d67a7aa57808d75782356efa9f1ce606
Ivalue:  c66a6ab56818c74792257eea8f0cf616
plain:   10101010101010101010101010101010

注:上述 plain 不是密文正确解密得到的明文,只是我们通过构造让服务器认为解密正确得到的明文

当我们得到一整个完整的中间值时,根据 CBC模式 的解密流程,我们将中间值 xor IV 即可得到正确的明文

IValue:  c66a6ab56818c74792257eea8f0cf616
IV:      9F0B13944841A832B2421B9EAF6D9836
plain:   5961792120596F752067657420616E20
text:    "Yay! You get an "

代码实现

爆破 IValue 部分

    for l in range(length / 2): # 爆破每一位 Ivalue (0~15),总长度依次叠加
        iv = ''
        padding = l + 1 # 1~16
        for ll in range(l): # 根据已知 Ivalue 修改 iv
            iv += hex(ord(Ivalue.decode('hex')[ll]) ^ padding)[2:].zfill(2)
        
        for iv_test in range(256):
            iv = iv[0 : l * 2]
            iv += hex(iv_test)[2:].zfill(2)
            #print iv
            iv = iv.ljust(length, '0')
            #print iv
            data = iv.decode('hex')[::-1].encode('hex') + Cry[flag] 
            ctext = [(int(data[i:i+2],16)) for i in range(0, len(data), 2)]
            rc = Oracle_Send(ctext, 2)
            if str(rc) == '1': # padding 正确。根据 padding 值和此时 iv 计算 对应位置 Ivalue
                Ivalue += hex(padding ^ iv_test)[2:].zfill(2)
                print "Ivalue: ", Ivalue.ljust(length, '0').decode('hex')[::-1].encode('hex')
                break
        print "iv:     ", iv.decode('hex')[::-1].encode('hex')
        print '-' * 50

由于实际解密时填充从末尾开始,我们为了方便字符串操作选择先正向叠加,在和需要解密的密文拼接在一起之前再逆序(题目背景是给服务器上传一段16进制字符串密文,服务器会将前一半密文当做后一半密文解密的IV,当服务端返回值为 '1' 时,意为解密正确,即明文填充无误)

完整代码

# coding=utf-8
from oracle import *

c = '9F0B13944841A832B2421B9EAF6D9836813EC9D944A5C8347A7CA69AA34D8DC0DF70E343C4000A2AE35874CE75E64C31'
block_num = 3
length = len(c) / block_num
Cry = []
for i in range(block_num):
    Cry.append(c[i * length : (i + 1) * length])
#print Cry[0], Cry[1], Cry[2]
Oracle_Connect()
# iv = ‘9F0B13944841A832B2421B9EAF6D9836’
# cry1 = '813EC9D944A5C8347A7CA69AA34D8DC0'
# cry2 = 'DF70E343C4000A2AE35874CE75E64C31'
mess = ''
flag = 1
for i in range(1, block_num):
    IV = Cry[i - 1].decode('hex')
    Ivalue = ''
    for l in range(length / 2): # 爆破每一位 Ivalue (0~15),总长度依次叠加
        iv = ''
        padding = l + 1 # 1~16
        for ll in range(l): # 根据已知 Ivalue 修改 iv
            iv += hex(ord(Ivalue.decode('hex')[ll]) ^ padding)[2:].zfill(2)
        
        for iv_test in range(256):
            iv = iv[0 : l * 2]
            iv += hex(iv_test)[2:].zfill(2)
            #print iv
            iv = iv.ljust(length, '0')
            #print iv
            data = iv.decode('hex')[::-1].encode('hex') + Cry[flag] 
            ctext = [(int(data[i:i+2],16)) for i in range(0, len(data), 2)]
            rc = Oracle_Send(ctext, 2)
            if str(rc) == '1': # padding 正确。根据 padding 值和此时 iv 计算 对应位置 Ivalue
                Ivalue += hex(padding ^ iv_test)[2:].zfill(2)
                print "Ivalue: ", Ivalue.ljust(length, '0').decode('hex')[::-1].encode('hex')
                break
        print "iv:     ", iv.decode('hex')[::-1].encode('hex')
        print '-' * 50
    Ivalue = Ivalue.decode('hex')[::-1]
    mess += "".join(chr(ord(x) ^ ord(y)) for x, y in zip(Ivalue, IV))
    print '=' * 70, '\n', mess, '\n', '=' * 70
    flag += 1

Oracle_Disconnect()
print mess

完整输出

Connected to server successfully.
Ivalue:  00000000000000000000000000000016
iv:      00000000000000000000000000000017
--------------------------------------------------
Ivalue:  0000000000000000000000000000f616
iv:      0000000000000000000000000000f414
--------------------------------------------------
Ivalue:  000000000000000000000000000cf616
iv:      000000000000000000000000000ff515
--------------------------------------------------
Ivalue:  0000000000000000000000008f0cf616
iv:      0000000000000000000000008b08f212
--------------------------------------------------
Ivalue:  0000000000000000000000ea8f0cf616
iv:      0000000000000000000000ef8a09f313
--------------------------------------------------
Ivalue:  000000000000000000007eea8f0cf616
iv:      0000000000000000000078ec890af010
--------------------------------------------------
Ivalue:  000000000000000000257eea8f0cf616
iv:      0000000000000000002279ed880bf111
--------------------------------------------------
Ivalue:  000000000000000092257eea8f0cf616
iv:      00000000000000009a2d76e28704fe1e
--------------------------------------------------
Ivalue:  000000000000004792257eea8f0cf616
iv:      000000000000004e9b2c77e38605ff1f
--------------------------------------------------
Ivalue:  000000000000c74792257eea8f0cf616
iv:      000000000000cd4d982f74e08506fc1c
--------------------------------------------------
Ivalue:  000000000018c74792257eea8f0cf616
iv:      000000000013cc4c992e75e18407fd1d
--------------------------------------------------
Ivalue:  000000006818c74792257eea8f0cf616
iv:      000000006414cb4b9e2972e68300fa1a
--------------------------------------------------
Ivalue:  000000b56818c74792257eea8f0cf616
iv:      000000b86515ca4a9f2873e78201fb1b
--------------------------------------------------
Ivalue:  00006ab56818c74792257eea8f0cf616
iv:      000064bb6616c9499c2b70e48102f818
--------------------------------------------------
Ivalue:  006a6ab56818c74792257eea8f0cf616
iv:      006565ba6717c8489d2a71e58003f919
--------------------------------------------------
Ivalue:  c66a6ab56818c74792257eea8f0cf616
iv:      d67a7aa57808d75782356efa9f1ce606
--------------------------------------------------
====================================================================== 
Yay! You get an  
======================================================================
Ivalue:  000000000000000000000000000000cb
iv:      000000000000000000000000000000ca
--------------------------------------------------
Ivalue:  000000000000000000000000000086cb
iv:      000000000000000000000000000084c9
--------------------------------------------------
Ivalue:  000000000000000000000000004686cb
iv:      000000000000000000000000004585c8
--------------------------------------------------
Ivalue:  000000000000000000000000a84686cb
iv:      000000000000000000000000ac4282cf
--------------------------------------------------
Ivalue:  000000000000000000000091a84686cb
iv:      000000000000000000000094ad4383ce
--------------------------------------------------
Ivalue:  00000000000000000000ad91a84686cb
iv:      00000000000000000000ab97ae4080cd
--------------------------------------------------
Ivalue:  00000000000000000077ad91a84686cb
iv:      00000000000000000070aa96af4181cc
--------------------------------------------------
Ivalue:  00000000000000007177ad91a84686cb
iv:      0000000000000000797fa599a04e8ec3
--------------------------------------------------
Ivalue:  000000000000003f7177ad91a84686cb
iv:      0000000000000036787ea498a14f8fc2
--------------------------------------------------
Ivalue:  000000000000c33f7177ad91a84686cb
iv:      000000000000c9357b7da79ba24c8cc1
--------------------------------------------------
Ivalue:  0000000000aec33f7177ad91a84686cb
iv:      0000000000a5c8347a7ca69aa34d8dc0
--------------------------------------------------
Ivalue:  000000006daec33f7177ad91a84686cb
iv:      0000000061a2cf337d7ba19da44a8ac7
--------------------------------------------------
Ivalue:  000000e46daec33f7177ad91a84686cb
iv:      000000e960a3ce327c7aa09ca54b8bc6                                                                                       
--------------------------------------------------                                                                               
Ivalue:  0000e9e46daec33f7177ad91a84686cb
iv:      0000e7ea63a0cd317f79a39fa64888c5
--------------------------------------------------
Ivalue:  0010e9e46daec33f7177ad91a84686cb
iv:      001fe6eb62a1cc307e78a29ea74989c4
--------------------------------------------------
Ivalue:  c010e9e46daec33f7177ad91a84686cb
iv:      d000f9f47dbed32f6167bd81b85696db
--------------------------------------------------
====================================================================== 
Yay! You get an A. =)
======================================================================
Connection closed successfully.
Yay! You get an A. =)

参考文章

全部评论

info 评论功能已经关闭了呐!