freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Padding Oracle原理深入剖析与实践
2023-12-25 15:50:20

之前一直没花时间深入研究过Padding Oracle漏洞的原理细节,恰好最近团队里一位精通密码学的大佬做了次分享,经过多轮的交流请教,终于算是理解得比较清晰了,结合自己分析出来的一些内容,整理分享出来。

网上讲这个的文章其实很多,但是把关键细节原理讲清楚的很少,查了一堆资料,都没能完全解答我对某些细节的疑惑,以至于当我最终把这些问题想明白的时候,发觉自己又失去了几根头发。

0x00 一些核心背景知识

分组加密算法

也叫块加密算法(Block Cipher)。用于将明文消息划分为相等长度的数据块,再对每个数据块进行加密处理。使用分组加密方式的加密算法有很多种,其中最广泛应用的有AES(Advanced Encryption Standard)、DES(Data Encryption Standard)、3DES(TripleDES)算法。

DES/3DES算法的分组长度为64bit(8bytes),AES算法的分组长度为 128bit(16bytes)。注意AES-128、AES-192、AES-256只是秘钥长度不同,分组长度是一样的。

Padding(填充)

分组加密算法要求输入内容长度为分组长度的整数倍,如果加密过程中原始数据长度不满足此要求,则需要在后面进行填充。例如下图,假设算法规定的分组长度是64bit,也就是8个字节,那么我们就要对明文进行填充,让填充后的数据长度为8字节的整数倍。缺几个字节,就在最后一个分组中填充几个字节,并且填充的内容就是缺失的长度。

image

例如FIG还差5个字节,那么就填充5个0x05,以此类推。如果明文恰好是分组长度整数倍,例如plantain,恰好是8个字节,那么也要在后面填充8个0x08。这样的填充方式在满足分组加密的规则同时,也便于解密后对填充位的校验和移除。

比如我想加密的内容就是8个字节的A、B、C、D、E、F、0x02、0x02,如果后面不填充8个0x08,解密后就会把0x02当做填充位给移除掉,也就会导致解密结果丢失2个字节。

PKCS#5和PKCS#7

我们经常看到PKCS#5和PKCS#7填充方式的说法,这两个名词分别代表什么呢?又有哪些区别?
实际上PKCS (Public Key Cryptography Standards) 公钥密码学标准,是 RSA Security LLC公司定义的一系列密码学标准。5和7指的是两个不同的版本。

在PKCS#5标准中规定:Padding填充是8字节的。因为这个标准只讨论了8字节(64位) 块的加密。
而PKCS#7标准中规定:Padding填充可以是1-255任意字节的,分块需要多大,填充就是多少个字节。填充方式两者没有任何区别。由于填充的内容就是缺失的长度,而一个字节最大是255,所以填充长度最大只能是255个字节。

所以PKCS#7其实是在PKCS#5的基础上做了扩展,是兼容PKCS#5的,PKCS#5相当于PKCS#7的子集。

分组密码加密操作模式

我们说的DES、3DES、AES都是加密算法,这些加密算法用来对每一个单独的分组(Block)加密,而加密操作模式,是在加密算法的基础上,增加了分组(Block)之间的关系处理逻辑。任何基于分组加密的算法都可以套用这些模式。

常见加密操作模式:

  • ECB(Electronic Code Book Mode),电子密码本模式。

  • CBC(Cipher Block Chaining Mode),密码分组连接模式。

  • CFB(Cipher Feedback Mode),密码反馈模式。

  • OFB(Output Feedback Mode),输出反馈模式。

  • CTR(Counter Mode),计数器模式。

如果对这些模式不熟悉也没关系,除了CBC其他混个眼熟就好,不影响我们讨论CBC模式,它是Padding Oracle的主角。

密码分组链接模式(CBC)

CBC加密模式中,一段明文首先会被划分成等长的分组,然后在最后一个分组按照我们前面讲过的规则进行Padding填充。

每一个分组的明文,都跟前一个分组的加密后的密文进行异或,之后再使用秘钥key加密。第一个分组跟谁异或呢?就是一个叫初始化向量IV(initialization vector)的串,它的长度和分组的长度一致。在这样的加密模式中,我们可以看出IV对后面加密内容的影响是有传递性的。通常安全实践里我们会要求IV要随机,这样的好处是即使对同一段明文进行加密,使用不同的IV加密后的密文看起来也是完全不相关的。

image

如图,方块中的ENC_key表示使用秘钥key进行加密(Encryption)。⊕符号代表异或计算。

图中最下面的一行IV和所有加密块拼在一起,通常就是在实践中传递的加密数据内容。也有些实现是IV和加密块分开放在不同的参数里传递的。

CBC模式解密过程:
image

如图,DEC_key表示使用秘钥key进行解密(Decryption)。正常的思路来看,如果我们不知道加解密秘钥key,那么就无法直接计算出DEC_key后的解密内容。

Padding Oracle 让我们能够不通过秘钥key,也能暴力猜解出DEC_key的结果。

0x01 Padding Oracle是什么?

Padding就是前面我们说的填充。Oracle在这里跟我们熟悉的Oracle公司并没有什么关系,Oracle的中文翻译是:”神谕;权威;女祭司;(传达神谕的)牧师;(古希腊常有隐含意义的)神示;(古希腊的)神示所;能提供宝贵信息的人(或书);智囊”。

而在计算机领域,Oracle也是一个专业术语,叫预言机(Oracle Machine),又称谕示机,是一种抽象电脑,用来研究决定型问题。预言机具备图灵机的一切功能,并额外拥有一种能力:可以不通过计算直接得到某些问题的答案,这个过程叫做Oracle(神谕)。也就是说,预言机可以解决图灵机通过计算也无法解决的问题,比如从外界获取问题的答案。百度百科对Padding Oracle的解释中说Oracle是提示的意思,这样理解也没毛病,神谕就是“来自神的提示”。有些资料对Padding Oracle的翻译就是“填充预言机”。

所以我们可以把Padding Oracle理解成一个能够给我们提示的服务(或者抽象实体),我们把一段任意的密文传给它,它会告诉我们它对这段密文Padding填充合法性的判断,比如说,如果是错误的Padding,就报错,如果是正确的Padding,就不报错。

0x02 AES CBC加解密过程Python实现

为了解释这个问题,我写了一个AES CBC加密模式的Web服务作为Demo:

from flask import Flask, request

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad

app = Flask(__name__)

# KEY = os.urandom(16)
# print(KEY)
# 测试的时候,可以固定一个秘钥和iv,这样每次启动服务秘钥不会变化,密文可以一直测试。
KEY = bytes.fromhex("d7c910a97f037785efb23566ae99d3fc")
iv = bytes.fromhex("e14950e98b0904dd282de280609b7314")

@app.route('/encrypt')
def encrypt():
    plain_text = request.args.get('pt', '').encode('utf-8')

    cipher = AES.new(KEY, AES.MODE_CBC, iv)
    cipher_text = cipher.encrypt(pad(plain_text, AES.block_size))

    return 'crypted: {}{}'.format(iv.hex(),cipher_text.hex())

@app.route('/decrypt')
def decrypt():
    cipher_text = bytes.fromhex(request.args.get('ct', ''))

    iv = cipher_text[:16]
    cipher = AES.new(KEY, AES.MODE_CBC, iv)

    # 对解密后的内容进行unpad操作,去除加密时的Padding内容,得到最终的明文
    plain_text = unpad(cipher.decrypt(cipher_text[16:]), AES.block_size)

    return 'decrypted: {}'.format(plain_text)


if __name__ == '__main__':
    app.run(debug=True, port=8083)

代码比较简单,服务启动后会在8083端口提供两个接口,一个是加密接口/encrypt,参数pt为要加密的明文内容:

访问URL:http://localhost:8083/encrypt?pt=w4ter0%20is%20awesome!

image

得到加密结果:
crypted: e14950e98b0904dd282de280609b7314fcc1b431b32bc2d5f0f290dd64df1a3f66cce304e5ea77bec3237bd42546f029

这段密文按照16字节进行分组可以分成三组,分别是:

  • IV:e14950e98b0904dd282de280609b7314

  • ciphertext block 1:fcc1b431b32bc2d5f0f290dd64df1a3f

  • ciphertext block 2:66cce304e5ea77bec3237bd42546f029

image

对应我们之前讲过的加密流程,“w4ter0 is a

# 漏洞分析 # 密码学
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录