我对Padding Oracle Attack的分析和思考
道哥的《白帽子讲web安全》有一章提到Padding Oracle Attack的攻击方式,据说这货在2011年的Pwnie Rewards上还被评为"最具价值的服务器漏洞"。
抱着书看了半天,感觉也不是很理解,和密码学结合的比较紧,有一些理论的东西在里面。这里做个学习笔记,研究一下。
1. 相关阅读材料
https://github.com/GDSSecurity/PadBuster PadBuster - Automated script for performing Padding Oracle attacks
http://hi.baidu.com/aullik5/item/49ab45de982a67db251f40f6 道哥的分析
http://www.di-mgt.com.au/cryptopad.html Padding原则
http://hi.baidu.com/306211321/item/faa44923c3c07d98b7326387 分组密码的链接模式
http://blog.gdssecurity.com/labs/2010/9/14/automated-padding-oracle-attacks-with-padbuster.html 作者写的分析
http://www.cnblogs.com/JeffreyZhao/archive/2010/09/25/1834245.html 老赵写的分析(和ASP.NET结合)
http://www.isg.rhul.ac.uk/~kp/secretIV.pdf 国外的牛牛写的分析(拓展Padding Oracle的知识面)
http://www.icylife.net/yunshu/attachments/Padding-Oracle-Attack.pdf 云舒写的分析
http://netifera.com/research/poet/PaddingOraclesEverywhereEkoparty2010.pdf EKOPARTY 2010的演讲PPT
http://netsecurity.51cto.com/art/201101/244089_1.htm 51CT上看到的分析
2. 前引知识
2.1 分组的填充Padding
分组密码Block Cipher需要在加载前确保每个每组的长度都是分组长度的整数倍。一般情况下,明文的最后一个分组很有可能会出现长度不足分组的长度:

这个时候,普遍的做法是在最后一个分组后填充一个固定的值,这个值的大小为填充的字节总数。即假如最后还差3个字符,则填充0x03。


这种Padding原则遵循的是常见的PKCS#5标准。http://www.di-mgt.com.au/cryptopad.html#PKCS5
2.2 CBC(Cipher Block Chaining CBC)模式
这是一种分组链接模式,目的是为了使原本独立的分组密码加密过程形成迭代,使每次加密的结果影响到下一次加密。这行可以强化加密算法的"敏感性",即实现所谓的"雪崩效应",在香浓理论中这就是"扰乱原则"。
之前在《密码学》课后做的笔记:http://hi.baidu.com/306211321/item/faa44923c3c07d98b7326387
这里我个人感觉要注意的一点是:
在链接模式中,初始化IV的长度要和对称加密算法的分组长度一致。原因是链接模式中的异或操作是等长操作。
3. Padding Oracle Attack攻击的原理
因为Padding Oracle Attack是针对CBC链接模式的攻击,和具体的加密算法无关(分组)。所以这里我们选择DES为例进行阐述。
假设明文为: LittleHann(明文长度为10 8 < 10 < 16 即使用2个分组)
经过DES加密(CBC模式)后,其密文为: EFC2807233F9D7C097116BB33E813C5E

加密程序: http://pan.baidu.com/s/1e2o7
密文采用了"ASCII十六进制的表示方法",即两个字符表示一个字节的十六进制数。这是因为密码学算法中得到的密文经常会出现不可打印字符,为了保证在网络上传输的正确而不受不同系统间编码方案的影响,就有必要对密文进行"可视化"转化(即转化成可打印字符)。除了"ASCII十六进制的表示方法"之外,还可以采用"base64编码方法"。
PS:
这里插个题外话:PHP的DES及其他密码学算法的加密是通过"PHP加密扩展库Mcrypt"来实现的。
http://www.php100.com/cover/php/2651.html
整个加密过程如下:

初始化向量IV与明文(第一组明文)XOR后,再经过运算得到的结果作为新的IV,用于下一分组(分组2),如果迭代下去。
解密过程是加密过程的逆过程:

这里要注意,前几个分组的解密结果对我们都没有意义,我们重点关注的是最后一个分组的解密结果。看这张图可能会清楚一点:

注意到最后一个分组的末尾的数值为0x04,即表示填充了4个Padding。如果最后的Padding不正确(值和数量不一致),则解密程序往往会抛出异常(Padding Error)。而利用应用的错误回显,我们就可以判断出Paddig是否正确。
这里有几个概念要先理清一下:
1. 基于密码学算法的攻击,往往第一个要搞清楚的是,我们在攻击谁,或者准确的说我们的攻击点在哪里?在一个密码学算法中,有很多的参数(指攻击者可以控制的参数),攻击者往往是针对其中某一个或某一些参数进行破解,穷举等攻击。
在Padding Oracle Attack攻击中,攻击者输入的参数是IV+Cipher,我们要通过对IV的"穷举"来请求服务器端对我们指定的Cipher进行解密,并对返回的结果进行判断。
2. 和SQL注入中的Blind Inject思想类似。我觉得Padding Oracle Attack也是利用了这个二值逻辑的推理原理,或者说这是一种"边信道攻击(Side channel attack)"。http://en.wikipedia.org/wiki/Side_channel_attack
这种漏洞不能算是密码学算法本身的漏洞,但是当这种算法在实际生产环境中使用不当就会造成问题。
和盲注一样,这种二值逻辑的推理关键是要找到一个"区分点",即能被攻击者用来区分这个的输入是否达到了目的(在这里就是寻找正确的IV)。
比如在web应用中,如果Padding不正确,则应用程序很可能会返回500的错误(程序执行错误);如果Padding正确,但解密出来的内容不正确,则可能会返回200的自定义错误(这只是业务上的规定),所以,这种区别就可以成为一个二值逻辑的"注入点"。
3. 攻击成立的两个重要假设前提:
1. 攻击者能够获得密文(Ciphertext),以及附带在密文前面的IV(初始化向量)
2. 攻击者能够触发密文的解密过程,且能够知道密文的解密结果
4. 可能出现的情况
明文分组和填充就是Padding Oracle Attack的根源所在,但是这些需要一个前提,那就是应用程序对异常的处理。当提交的加密后的数据中出现错误的填充信息时,不够健壮的应用程序解密时报错,直接抛出"填充错误"异常信息(这个错误信息在不同的应用中是不同的体现,在web一般是报500错误)。
攻击者就是利用这个异常来做一些事情,假设有这样一个场景,一个WEB程序接受一个加密后的字符串作为参数,这个参数包含用户名、密码。参数加密使用的最安全的CBC模式,每一个block有一个初始化向量IV(注意:这个IV在服务器第一次生成这个密文的时候就产生了,并保存在服务器上,攻击者需要在提交数据的时候也提交这个IV,后面我们会看到,攻击者实际上就是在"穷举"这个IV)。
当提交参数时,服务端的返回结果会有下面3种情况:
a. 参数是一串正确的密文,分组、填充、加密都是对的(程序运行本身没出问题),包含的内容也是正确的(业务逻辑是对的),那么服务端解密、检测用户权限都没有问题,返回HTTP 200。
b. 参数是一串错误的密文,包含不正确的bit填充(程序运行本身出现致命错误),那么服务端解密时就会抛出异常,返回HTTP 500 server error。
c. 参数是一串正确的密文(程序运行本身没出问题),包含的用户名是错误的(业务逻辑是错的),那么服务端解密之后检测权限不通过,但是依旧会返回HTTP 200戒者HTTP 302,而不是HTTP 500。
攻击者无需关心用户名是否正确,只需要提交错误的密文(因为这里有4中变量情况,为了构造出二值逻辑推理,我们要定住其中2个情况,即让业务逻辑恒错,对Bit Padding的情况进行逻辑推理),根据HTTP Code即可做出攻击。
我们继续回到原理分析上来。
假设有这样一个应用 http://sampleapp/home.jsp?UID=0000000000000000EFC2807233F9D7C097116BB33E813C5E

(中间用箭头隔开了,前面的16个字母(即8字节 ASCII十六进制表示法两个字母为一个字节)为攻击者输入的IV。后面的32个字母(即16字节)为攻击者输入的密文)
我们向服务器发送这样一个请求:
Request: http://sampleapp/home.jsp?UID=0000000000000000EFC2807233F9D7C097116BB33E813C5E
Respose: 500 - Internal Server Error
此时在解密时Padding是不正确的(填充的值和填充的数量不一致)
例如:

程序判断Padding是否出错一般是去检验末尾的那个字节的值,这里是0x3D,显然不对。这里我们再次回忆一下Padding原则:
1个字节的Padding为0x01
2个字节的Padding为0x02
3个字节的Padding为0x03
4个字节的Padding为0x04
5个字节的Padding为0x05
6个字节的Padding为0x06
7个字节的Padding为0x07
8个字节的Padding为0x08(当原始的明文正好是分组的整数倍的时候,Padding一个整组的填充值)
也就是说,Padding的值只可能是0x01~0x08之间。
接下来就是最关键的部分了,也是我一开始看比较难理解的地方。
我们接下来要利用选择密文攻击的思想,不断调整,修正IV。来对Intermediary Value进行猜测。
1) Padding 0x01
我们不断地调整IV的值,以希望解密后,最后一个字节的值为正确的Padding Byte,这里是0x01。因为Intermediary Value是固定的(我们此时不知道Intermediary Value),因此从0x00~0xFF之间,只可能有一个IV的值与Intermediary Value的最后一个字节进行XOR后,结果是0x01(思考: 因为0x01只有最后1 bit为1,其他都是0,所以根据XOR的性质,只能存在一个值能XOR得到0x01)。攻击者通过遍历这255个值,可以找出IV需要的最后一个字节。
Request: http://sampleapp/home.jsp?UID=00000000000000EFC2807233F9D7C097116BB33E813C5E
Respose: 200 OK

通过XOR运算,可以马上推导出此Intermediary Byte的值:
if(Inermediary Byte) ^ 0x66 == 0x01
{
Inermediary Byte = 0x66 ^ 0x01
}
so:
Inermediary Byte = 0x67
在回过头来看看加密过程:

初始化向量IV与明文进行XOR运算得到了Inermediary Value,因此将刚才得到的Inermediary Byte(0x67)与"真实"的IV的最后一个字节0x0F(攻击者事先获取到的)进行XOR运算,即能得到明文:
0x67 ^ 0x0F = 0x68 : H
即得到明文(第一个分组)的最后一个字母H!
这里咱们要稍微停一下,把思路理一理:
1. 我们在得出明文的那次XOR计算中用到的IV(0x0F)和我们攻击者不断"注入"的IV(0x01~0xFF)不是一回事,要区分开来。在计算明文的那个IV(0x0F)是我们事先就获取到的,回想我们之前说的这个Padding Oracle Attack攻击的成立条件:

这个IV(0x0F)是服务器端在发送密文的时候(可能是cookie形式)附带在密文的头部发给我们的。 一般情况下,如果跨系统发送这种"带盐"的密文,都要把"盐(IV)"附带在密文的头部或其他位置一起发送给接收方。这里我们接收到的IV就是0x0F,在不同的环境中这个IV必然是不一样的,关键是要理解原理。
2. 为什么我们可以不断尝试IV?
按理来说,IV不是在服务器第一次生成这段密文的时候就生成好了吗?然后在每次发送的时候都附带在密文的头部,不会再变了......
的确是这样,但这说的是在正常的解密情况下发生的事,而我们攻击者现在做的事并不是在解密(事实上攻击者这个时候也不知道IV和KEY),我们只是在通过不断的修改IV来对目标解密系统进行"试探",从返回的结果来进行"侧信道攻击",从而进行二值逻辑推理。
这就是一种所谓的"选择密文攻击"方式,好像也有叫Bit-Flipping Attack:
http://www.vnsecurity.net/2010/03/codegate-2010-challenge-8-bit-flipping-attack-on-cbc-mode/
继续回到思路分析上来:
在正确"匹配"了Padding 0x01之后,需要做的是继续推导出剩下的Intermediary Byte。根据Padding 原则,在需要Padding两个字节的时候,其值应该是0x02。
2) Padding 0x02
这个时候我们要注意,之前说过,这个攻击过程是个循环迭代的过程,上一步的结果就是作为下一步的基础。
我们之前已经知道Intermediary Byte的最后一个字节为0x61,因此可以"更新"IV(攻击者输入的IV)的第8个字节为 0x66 ^ 0x02 = 0x64。
(思考: 这个Padding Oracle Attack的过程中,攻击者需要不断地调整输入IV的值,之前因为在Padding 0x01中,我们只是在假设Padding 0x01的情况,在这个假设下,我们通过得出IV的最字节,从而计算出Intermediary Byte,进而算出明文的最后一个字节"H"。这里要注意的是,这个假设的IV没有任何意义,只是我们进行"选择密文攻击"过程中的一个路人甲而已。而接下来我们要继续假设Padding 0x02的情况,为了使在假设Padding 0x02中,Intermediary Value的最后一个字节依然为"0x67(之前算出来的)",所以我们要对IV的最后一个字节进行迭代更新: 0x66 ^ 0x02 = 0x64)。
这个时候,本质上攻击者是固定住了IV的最后一个字节不变,开始循环"盲注"倒数第二个字节。开始依照第一步时的方法对倒数第二个字节进行"盲注"逻辑判断。
Request: http://sampleapp/home.jsp?UID=000000000000EFC2807233F9D7C097116BB33E813C5E
Respose: 200 OK
通过遍历可以得出,IV的第7个字节为0x70。
if(Inermediary Byte) ^ 0x70 == 0x02
{
Inermediary Byte = 0x70 ^ 0x02
}
so:
Inermediary Byte = 0x72
对应的Intermediary Byte为0x72。知道了Intermediary Byte的倒数第二个字节为0x72,就可以得出明文的的倒数第二个字节:
0x72 ^ 0x17 = 0x65 : e
(这里的IV: 0x17是我们从服务端接收到的附带在密文头部的IV的倒数第二个字节)
接下来,要继续对IV进行假设,同理,这次"选择密文攻击"的假设是Padding 0x03,对IV进行迭代更新,然后对IV的倒数第三个字节进行"穷举"循环探测。
Padding 0x03
Padding 0x04
Padding 0x05
Padding 0x06
Padding 0x07
Padding 0x08
最终得到这个分组的明文LittleH。这是第一个分组的明文。
注意,Padding Oracle Attack是以单个分组进行的。到了这一步,我们会发现,我们的攻击目标其实就是那个临时中间值变量Intermediary Value,得到了这个值,再加上我们本来就可以获取到IV(服务器端生成的附在密文头部的那个IV),我们可以通过XOR运算得到这个分组的明文。
对于多个分组的密文来说,我们继续观察一下CBC的解密流程:

第二个分组使用的IV(对于第一组来说是附带在密文头部的那段)是第一组分组的密文。因此我们就把第一组的密文带入带第二组的计算中。继续第对二组的Intermediary Value进行逻辑推导,最终得到第二组的密文:ann。
多分组的密文可以依次类推,由此即可以仅根据密文和IV还原出明文。
4. Padding Oracle Attack的攻击利用场景
一旦我们通过暴力破解得到中间值Intermediary Value之后,IV便可以用来生成我们想要的任意值。新的IV可以被放在前一个示例的前面,这样便可以得到一个符合我们要求的,包含两个数据块的密文了。这个过程可以不断重复,这样便能生成任意长度的数据了。
使用PadBuster加密任意的值
https://github.com/GDSSecurity/PadBuster
5. 模拟实验
这是道哥写的python脚本,可以用来模拟实验出Padding Oracle Attack的原理:
可以参考源代码来进行深入的学习,加深理解。
6. 后记
自己按照书上讲的理解了一下,如有不对之处,望指正。
我对Padding Oracle Attack的分析和思考的更多相关文章
- Padding Oracle Attack的一些细节与实现
Padding Oracle Attack还是颇具威力的,ASP.NET的Padding Oracle Attack被Pwnie评为2010年最佳服务端漏洞之一.还是看 Juliano Rizzo a ...
- padding Oracle attack(填充Oracle攻击)
最近学习到一种老式的漏洞,一种基于填充字节的漏洞.就想记录下来,早在2010年的blackhat大会上,就介绍了padding Oracle漏洞,并公布了ASP.NET存在该漏洞.2011年又被评选为 ...
- ASP.NET Padding Oracle Attack EXP
#!/usr/bin/perl## PadBuster v0.3 - Automated script for performing Padding Oracle attacks# Brian Hol ...
- Padding Oracle 和 CBC字节翻转攻击学习
以前一直没时间来好好研究下这两种攻击方式,虽然都是很老的点了= =! 0x01:Padding oracle CBC加密模式为分组加密,初始时有初始向量,密钥,以及明文,明文与初始向量异或以后得到中间 ...
- Padding Oracle攻击
最近在复现LCTF2017的一道题目,里面有一个padding oracle攻击,也算是CBC翻转攻击,这个攻击主要针对CBC加密模式的 网上有关这个攻击的博客文章很多,但是其中有一些细节可能是个人的 ...
- 中国气象局某分院官网漏洞打包(弱口令+SQL注入+padding oracle)
漏洞一.后台弱口令 后台地址:http://www.hnmatc.org/admin/ 直接爆破得到账号admin 密码admin888 漏洞二.SQL注入(前台后台都有) 注入点:http://w ...
- 【原创】实战padding oracle漏洞
首先关于padding oracle漏洞的原理请看: 步入正传~~ 搭建漏洞利用环境Perl 环境下载地址:链接:http://pan.baidu.com/s/1skFxVm1 密码:anuw 首先查 ...
- MS10-070 ASP.NET Padding Oracle信息泄露漏洞项目测试
MS10-070 ASP.NET Padding Oracle信息泄露漏洞1 漏洞描述:ASP.NET由于加密填充验证过程中处理错误不当,导致存在一个信息披露漏洞.成功利用此漏洞的攻击 ...
- Oracle内存全面分析
Oracle内存全面分析 Oracle的内存配置与oracle性能息息相关.而且关于内存的错误(如4030.4031错误)都是十分令人头疼的问题.可以说,关于内存的配置,是最影响Oracle性能的配置 ...
随机推荐
- sql windows server2008 全套激活码
vs2012 - Microsoft Visual Studio Ultimate 2012 旗舰版 有效注册密钥: YKCW6-BPFPF-BT8C9-7DCTH-QXGWC- Microsoft ...
- BZOJ 3572: [Hnoi2014]世界树
BZOJ 3572: [Hnoi2014]世界树 标签(空格分隔): OI-BZOJ OI-虚数 OI-树形dp OI-倍增 Time Limit: 20 Sec Memory Limit: 512 ...
- 24Mybatis_延迟加载——用association来实现
resultMap可以实现高级映射(使用association.collection实现一对一及一对多映射),association.collection具备延迟加载功能. 需求: 如果查询订单并且关 ...
- c# 调用打印机
1.本地打印机 //添加引用并using System.Management; public static void AvailablePrinters() { ManagementScope ms ...
- VS的代码分析工具
来自:[译]Visual Studio 2008 Code Metrics http://www.cnblogs.com/live41/archive/2010/02/08/1665627.html ...
- 好用的SQLSERVER数据库自动备份工具SQLBackupAndFTP(功能全面)
转载:http://www.cnblogs.com/lyhabc/p/3322437.html 挺好用的SQLSERVER数据库自动备份工具SQLBackupAndFTP(功能全面) 这个工具主要就是 ...
- Web API 实现JSONP或者安装配置Cors跨域
前言 照理来说本节也应该讲Web API原理,目前已经探讨完了比较底层的Web API消息处理管道以及Web Host寄宿管道,接下来应该要触及控制器.Action方法,以及过滤器.模型绑定等等,想想 ...
- Backbone源码分析-Backbone架构+流程图
作者:nuysoft/高云/nuysoft@gmail.com 声明:本文为原创文章,如需转载,请注明来源并保留原文链接. Backbone0.9.1源码分析分析系列 jQuery1.6.1源码分析系 ...
- [iOS翻译]《iOS7 by Tutorials》系列:在Xcode 5里使用单元测试(上)
简介: 单元测试是软件开发的一个重要方面.毕竟,单元测试可以帮你找到bug和崩溃原因,而程序崩溃是Apple在审查时拒绝app上架的首要原因. 单元测试不是万能的,但Apple把它作为开发工具包的一部 ...
- 20145208 《Java程序设计》第一周学习总结
20145208 <Java程序设计>第X周学习总结 教材学习内容总结 这几天我学习java的基础内容,这几天我学习了java的基础内容,从教材上面我了解到了java是一种程序语言,但他又 ...