Padding Oracle Attack
- 分组密码
在密码学中,分组加密(英语:Block cipher),又称分块加密或块密码,是一种对称密钥算法。它将明文分成多个等长的模块(block),使用确定的算法和 对称密钥对每组分别加密解密。 - 什么是PKCS#5?
PKCS#5,就是一种由RSA信息安全公司设计的填充标准。
对于PKCS#5标准来说,一般缺少几位,就填充几位那个数字。
比方说,在上面的例子里,我们有三位空缺,那么就要在空缺处都填上。这样,第二组的内容就变成了‘bc333’ 。
这里需要注意的是,比如说,如果每个分组是8字节,我的明文是‘testabcd’,这样恰好是8字节了。但是按照PKCS#5的标准,我们仍然需要在后面添加一个块,块里的内容全部填充为8 (因为一个块大小为8字节,而第二个块全部为空,因此有8位需要填充)。
前置知识
1.Padding
故名思义,Padding Oracle Attack背后的关键性概念便是加/解密时的填充(Padding)。明文信息可以是任意长度,但是块状加密算法需要所有的信息都由一定数量的数据块组成。为了满足这样的需求,便需要对明文进行填充,这样便可以将它分割为完整的数据块。
加密时可以使用多种填充规则,但最常见的填充方式之一是在PKCS#5标准中定义的规则。PCKS#5的填充方式为:明文的最后一个数据块包含N个字节的填充数据(N取决于明文最后一块的数据长度)。下图是一些示例,展示了不同长度的单词(FIG、BANANA、AVOCADO、PLANTAIN、PASSIONFRUIT)以及它们使用PKCS#5填充后的结果(每个数据块为8字节长)。
请注意,每个字符串都至少有1个字节的填充数据,因此7字节的值(如AVOCADO)则使用0x01进行填充,而8字节的值(如PLANTAIN)则会填充一个额外的数据块。填充字节的值也说明了填充的字节数,因此待加密数据的最后几个字节必须是以下几种情况之一:
一个0x01(0x01)
两个0x02(0x02,0x02)
三个0x03(0x03,0x03,0x03)
四个0x04(0x04,0x04,0x04,0x04)
……
如果解密后的最后一个数据块末尾并非这些合法的字节序列,大部分加/解密程序都会抛出一个填充异常。这个异常对于攻击者尤为关键,它是Padding Oracle Attack的基础。
2.CBC
这是一种分组链接模式,目的是为了使原本独立的分组密码加密过程形成迭代,使每次加密的结果影响到下一次加密。这行可以强化加密算法的"敏感性",即实现所谓的"雪崩效应",在香浓理论中这就是"扰乱原则"。
加密:
解密:
综合以上两点,我们举个栗子:
client给server提交的参数为7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6
才能请求正常的服务
我们从上帝视角(一切变量都是已知)的看这串信息其实是这样的
上帝视角看到的加密过程如下图所示:
上帝视角看到的解密过程:
关注上图绿色圈起来的部分,解密之后的最后一个数据块,其结尾应该包含正确的填充序列。如果这点没有满足,那么加/解密程序就会抛出一个填充异常。Padding Oracle Attack的关键就是利用程序是否抛出异常来判断padding是否正确。
从上帝视角了解到正常情况下是如何运转的之后,我们来到攻击者的视角看看。
首先明确一下,攻击者此时拥有的条件:
- 密文(即上文信息的后一部分
- 原始的初始向量(即上文信息的前一部分)
- 攻击者能够修改向量后触发解密过程,并得知解密结果(用于获悉padding是否正确)
如果attacker把信息的前一部分(即IV的值全改为NULL):
0000000000000000F851D6CC68FC9537
发送出去,server会返回状态码500,意为padding出错,怎么会出错呢?
如上图所示,以最后一个字节为例,由于IV被改为了0x00,所以IV在与中间值(Intermediary Value)异或后生成的数据不是正确的填充,从而导致服务器的500 状态码。作为attacker,可以修改的只有IV,还是以最后一个字节为例,我们可以从0x00遍历到0xff,分别发送给服务器去处理(即与中间值异或),如果服务器返回状态码200,则这一轮的padding成功。
如下图遍历黄色方框的值
Attacker知道,这一轮只有保证Decrypted Value的最后一个字节为0x01时才能成功(为什么?参看前置知识1padding部分的内容),而自己能控制的只有IV。,即下图
此处存在的关系式为
Intermediary Value ^ Initialzation Vector == Decrypted Value
可以推出
Initialzation Vector ^ Decrypted Value == Intermediary Value
以上图为例,我们可以知道此时Decrypted Value[8]为0x01(由服务器返回200状态码得知),而新IV[8]为0x3C(遍历得到),再由上式,异或得Intermediary Value[8]=0x3D,由上帝视角的解密图中可以看到,Plain text[8] = Intermediary Value[8] ^ 旧IV[8]
注:此处的新IV是我们可以用于遍历、可以修改的IV,旧IV是Attacker最开始获取的IV,两者完全不同!!!
那么有一个关键点来了:我们已经有旧IV了,只要拿到正确的Intermediary Value即可得到明文Plain text。
所以,其实Padding Oracle Attack的工作就是拿到Intermediay Value
我们的工作都由此展开。
根据上文的异或关系式,我们需要继续遍历的新IV,使得与Decrypted Value异或得到Intermediary Value。
那么这时的新IV要满足什么关系式呢?安装上面的第一条关系式,需满足Intermediary Value ^ Initialzation Vector == Decrypted Value,而Decrypted Value此时应该为0x02 0x02(原因参看前置知识1padding),然后根据等式遍历新IV。
两个字节,如果遍历的话新IV[7]新IV[8]各有255中情况,遍历起来工作量就很大。
事实上,还是根据上面的关系式,我们可以知道Intermediary value[8]已经固定为0x3C了,现在要求Decrypted Value[8]为0x02,双方异或即可得到新IV[8],此时只需遍历新IV[7]即可,最多只需255次。
如下图所示:
将黄底的0x00最多遍历至0xff,中间会有一个正确的数值使得Decrypted Value为0x02,即下图
基于同样的思想,接下来的步骤就是:
- Intermediary Value[7] = Decrypted Value[7] ^ 新IV[7] = 0x26
2.Plain text[7] = 旧IV[7] ^ 0x26 = 1
3.进行下一轮,此时Decrypted Value[6]== 0x03
Decrypted Value[7]== 0x03
Decrypted Value[8]== 0x03
4.此时新IV[7] = Intermediary Value7^ 0x03
新IV[8] = Intermediary Value[8] (由上上轮得出,固定为0x3D)^ 0x03
5.遍历新IV[6]
。。。。。。
依次下来,就可以得出第一个分组的明文
多个多个分组的密文来说,参照前面的CBC解密流程图可知,第二个分组使用的IV(对于第一组来说是附带在密文头部的那段)是第一组分组的密文。因此我们就把第一组的密文带入带第二组的计算中。继续第对二组的Intermediary Value进行分析,最终得到第二组的密文。。。。。。多分组的密文可以依次类推,由此Attacker即可以仅根据密文和IV还原出明文。
攻击的流程明白之后,我们使用编辑器nano,text editor等打开分析部分代码功能
代码分析:
在代码前部,可以自行设置IV、plain以及算法(padding oracle attack针对下图列出的算法都适用)
根据设置的不同算法,在加解密函数中用if-else逻辑处理
攻击是以分组(块)为单位进行的
必要时进行填充
校验填充的是否符合规则
最为关键的就是分块进行攻击的函数
其他地方注释已经写得非常详细,不再赘述。
运行脚本,结果分析
最开始是上帝视角,明文、IV、密文都知道
然后是攻击者视角,先针对第0个分组进行攻击,由上到下依次攻击第8个字节、第7个字节。。。。。。
攻击结束后得到第0块的明文
继续攻击第1块
得到明文
继续攻击第2块
得到明文
统计得出Intermediary value以及明文
- 关于Padding Oracle Attack分析经典之作https://blog.gdssecurity.com/labs/2010/9/14/automated-padding-oracle-attacks-with-padbuster.html
- 代码参考http://hi.baidu.com/aullik5/blog/item/7e769d2ec68b2d241f3089ce.html