| 再回想一下我们介绍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×00C1[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) 
 (编辑:宣城站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |