再追念一下我们先容CBC块加密时说过,在一个加密块(Block N)中翻转某一位,则会在解密后导致对应的下一个明文块(Block N+1)中沟通的位举办翻转。因为这个特征,我们可以在不知道密钥的环境下,行使处事器来猜解出明文数据。
最后一字节
详细怎么做呢?再次细心思索一下CBC模式的解密流程,若要解密一个块,则必要其自己的密文C2以及前一个块的密文C1,解密的流程如下:

在这种进攻场景下,我们(进攻者)可以节制输入密文块的内容,而且获取处事器的差别化返回,等于否添补错误。假设C2是最后一个块,那么通过变异C1,就可以猜解C2明文。猜解进程如下:
- 将C1前15字节随机配置,第16字节配置为’x00’
- 将修改后的密文块发送给处事器解密
因为我们修改了C1的最后一个字节,那么按照上文先容,在解密后C2的明文P2最后一个字节也会举办改变,酿成什么我们还不知道,可是我们知道:
- P2[15] = I2[15] xor C1[15]
个中I2是解密算法如AES解密后的中间值,我们不体谅详细解密算法,但总有这么个值。然后,按照处事器的返回我们知道有两种也许:
- 返回添补不正当。此时P2[15]未知。
- 返回添补正当。此时P2[15]必定为0x01,由于只有这样才气呈现正当的添补。
假如是第一种环境,我们就继承变异C1[15],直到呈现正当的添补,即第二种环境。假设我们在变异到C1[15] = 0x42时才呈现正当添补,则此时有:
- P2[15] = I2[15] xor C1[15]
- I2[15] = P2[15] xor C1[15] = 0x01 xor 0x26 = 0x27
回首一下上图,I2的发生与C1无关,只与C2和密钥key相干,可是我们却计较出了I2[15]的值!因此我们可以用I2[15]异或上变异前的C1[15]从而得到原始的明文。
- P2[15] = 0x27 xor C1[15]
这就是Padding Oracle进攻的思绪。
五、下一个字节
为了完成进攻,我们继承行使相同方法猜解I2中更多的内容。
- 将C1前14字节配置为随机值
- C1[14]配置为0×00
- C1[15]配置为能令P2[15] = 0x02的值
- P2[15] = I2[15] xor C1[15]
- C1[15] = P2[15] xor I2[15] = 0x02 xor 0x27 = 0x25
即将C1[15]牢靠为0×25,继承爆破C1[14]知道呈现正当的添补,此时P2[14]=0x02,假设呈现正当添补时辰爆破的C1[14]值为0×68:
- P2[14] = I2[14] xor C1[14] = 0x02
- I2[14] = P2[14] xor C1[14] = 0x02 xor 0x68 = 0x6A
再一次,我们得到了真实的I2[14]值,从何可以算出原始的明文P2[14]。以此类推,最终我们可以计较出完备的明文P2内容。
六、下一个块
按照上述要领,我们已经可以还原最后一个密文块的明文了。而对付CBC模式,每个密文块的解密仅和当前块以及前一个块相干,因此上述进攻可以应用到全部块中,除了第一个。
第一个块的加解密行使初始化向量IV举办,对此没有通用破解要领。可是CBC加密中IV也不是必需保密的,因此在实践中凡是会组合到密文的最前面可能最后头,其长度和块巨细沟通。假如必然要解密第一个块,可以行使这种揣摩要领。
七、示例
实践出真知,我们来看一个详细的例子。起首用Flask写一个简朴的应用,如下:
- #!/usr/bin/env python3
- import binascii
- import string
- import random
-
- from Crypto.Cipher import AES
- from Crypto.Util.Padding import pad, unpad
- from flask import Flask, request
-
- app = Flask(__name__)
- db = {}
- BSIZE = 16
- secret = b'x26' * BSIZE
-
- def get_iv():
- return b'x00' * BSIZE
-
- def decrypt(data):
- datadata = data.encode()
- data = binascii.unhexlify(data)
- iv = data[:BSIZE]
- engine = AES.new(key=secret, mode=AES.MODE_CBC, iviv=iv)
- datadata = data[BSIZE:]
- data = engine.decrypt(data)
- data = unpad(data, BSIZE)
- return data.decode()
-
- def encrypt(data):
- datadata = data.encode()
- iv = get_iv()
- engine = AES.new(key=secret, mode=AES.MODE_CBC, iviv=iv)
- return binascii.hexlify(iv + engine.encrypt(pad(data, BSIZE))).decode()
-
- @app.route('/dec/<data>')
- def dec(data):
- # print('dec:', data)
- try:
- key = decrypt(data)
- except Exception as e:
- return 'Error: ' + str(e)
- if key not in db:
- return 'Error: invalid key'
- return db[key]
-
- @app.route('/enc/<key>')
- def enc(key):
- db[key] = 'valid'
- return encrypt(key)
-
- app.run(debug=False)
(编辑:湖南网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|