# SSL/TLS协议安全系列：CBC 模式的弱安全性介绍(一)

Author: 乌云历史资料库 (@wooyun_archive)
Published: 2015-06-24T02:45:00Z
Canonical: https://wepostx.com/topics/866

> 乌云历史资料归档
>
> **原始作者：** GoSSIP_SJTU
> **原始编号：** superkieran-wooyundrops:609
> **原始发布时间：** 2015-06-24 10:45
> **声明：内容仅用于技术研究和个人使用，版权归 wooyun.org。**

---

编辑标注: 因drops对数学符号的支持存在问题，所以部分内容采取图片的形式显示，我们会经快解决这个问题，希望见谅。

# 一 前言

在SSL的一些版本中，使用CBC分组模式对数据进行加密，而CBC分组模式在使用中常会出现一些问题。本文对CBC分组模式中Padding Oracle Attack做基本介绍，主要包括Padding Oracle Attack的背景知识、在多种填充模式下的攻击原理以及现实场景应用。

Padding Oracle Attack，顾名思义：Padding是“填充”，这里指在CBC分组加密模式中的填充数据；Oracle是“提示”，意指服务器返回的提示信息。Padding Oracle Attack可理解为“填充提示攻击”，是一种逻辑上的旁路攻击。简要来说，Padding Oracle Attack是指以明文分组和填充为根源，依据系统解密密文时泄露的填充信息，通过不断尝试填充字节直到获得有效填充信息，从而恢复出明文或者构造任意明文对应的密文这样一种攻击方式。利用Padding Oracle Attack，恢复一块长度为b字节的密文块，只需要平均128*b次oracle回应，而不是尝试2^k次（k指密钥长度为多少bit）。

Padding Oracle Attack最初是Serge Vaudenay在EUROCRYPT 2002会议上提出， Vaudenay介绍了CBC模式中基本的Padding Oracle Attack攻击原理；在 2010 年的 BlackHat 欧洲大会上，Juliano Rizzo 与 Thai Duong 把这种攻击应用到了一些流行的Web应用框架上，包括JavaServer Faces，Ruby on Rails 以及 ASP.NET，他们在2011年的Symposium on Security and Privacy大会上发表文章详细分析ASP.NET中的Padding Oracle问题；同年，在Pwnie Rewards中，ASP.NET的Padding Oracle漏洞被评为“最具价值的服务器漏洞”；次年，Romain Bardou和Riccardo Focardi等人发现，这种攻击对于一些安全设备同样有效；在 2014年，谷歌的安全人员又提出了针对 SSL v3协议中Padding Oracle漏洞的POODLE攻击，99%以上使用 https 服务的网站受到影响。

# 二 背景知识

在介绍Padding Oracle Attack原理之前，这里首先简单介绍一些相关的背景知识。

CBC(Cipher Block Chaining)分组模式

常用的对称加密算法(如AES、3DES等)，一般使用分组加密模式，其中CBC是最常见的分组模式之一。CBC分组模式在对消息进行加/解密时，会根据使用的不同对称加密算法，将消息进行分块（block），每块大小可被分为16字节(AES)、8字节(3DES等)。

CBC分组模式加密流程如下图：

![原文图片](/media/2016/06/8d6300fad770e1f4fde08347909d510a)

CBC分组模式中引入初始化随机向量IV，使得相同明文在不同的加密次数中产生不同的密文，IV随密文一起发送给接收方。整个加密过程可以表示为：

![原文图片](/media/2016/06/3b1122c854eed17541755820bb4a9db7)

其中C表示密文块，P表示明文块。从加密过程中，我们可以看出，每一块明文都需要是整齐的，由于在现实中明文不一定可以整齐的分成块，因此在最后一块明文中需要加入填充(Padding)。

CBC分组模式解密流程图：

![原文图片](/media/2016/06/8227f0deca4197f697d114ce8440cd16)

首先每块密文经过对称算法解密成中间值，之后同前一块密文异或得到明文。解密过程可以表示为：
![原文图片](/media/2016/06/5066f721dcd628264ac7d381992ffa95)
 根据CBC分组模式加/解密过程可以看出，假如改变C_i的任何一位，解密时会影响到P_i和P_(i+1)，同时，如果有一块密文改变，也只会影响到它对应的以及之后的一块明文的内容。

几种填充方式

前面我们提到，明文信息可以是任意长度，不一定是block的整数倍，这样难免明文分组后最后一块长度不足。解决的方法就是对最后一块内容进行填充，这里我们介绍几种常见的填充规范。

- PKCS#5

最常见填充规范是PKCS#5，PKCS#5标准的全称是“Public-Key Cryptography Standards”，在 RFC2898里有说明。简单的说，如果最后一块明文缺少N个字节，就用N这个数填充到完整的块。我们以8字节为一块的分组模式举例：

![原文图片](/media/2016/06/42623a061f01de99e88d3fb7feba4047)

- ISO/IEC 9797-1

PKCS#5只是常用的填充标准之一，ISO/IEC也提供了两种填充的标准，分别为ISO/IEC 9797-1和ISO/IEC 10118-1，这里我们具体介绍前一种填充标准，并在后面给出对应的Padding Oracle Attack方法。

ISO/IEC 9797-1标准有三种填充方式：

第一种在明文后面全部补‘0’，直到是block size的整数倍；

第二种在明文后面紧跟着的一位补‘1’，之后补‘0’，直到是block size的整数倍；

第三种方式：

![原文图片](/media/2016/06/6cd40b2093b7c60516a45e6db2ab3ef4)

假设分组模式每块长度为n(bit)，首先右填充，在明文后面全部补充‘0’，直到n的整数倍，然后再进行左填充，(L_D)_2是未填充的明文长度(用bit表示)的二进制表示，然后在该长度左边补充‘0’直到长度为n。这种填充方式如果数据为空，仍然会有一个全‘0’的块作为填充。针对第三种填充方法，我们后面会给出对应的攻击原理。

上面介绍的两种标准，第一种是按字节填充，第二种是按位填充。除了这两种标准，还有很多其他的填充方式，比如John Black和Hector Urtubia在USENIX 2002年大会上提出的，这里我们以4字节填充为例介绍：

![原文图片](/media/2016/06/2a6b8e3a74e0a3dc392e4e3aedbdd3b3)

XY-PAD：选取固定的X和Y，在明文最后填充一个X，之后填充足够的Y；

ESP-PAD：在明文最后填充123..n补齐；

PAIR-PAD:与XY-PAD类似，这里的X和Y不是固定值，每次随机选取；

OZ-PAD：按位填充，在明文最后填充‘1’bit，之后以‘0’补齐；

ABIT-PAD：按位填充，发送者检测明文最后一位是‘0’还是‘1’，在最后以相反的位进行填充；

ABYT-PAD：发送者检测明文最后一个字节，然后选取任意不同的字节‘Y’进行填充；

BOZ-PAD：同XY-PAD，只不过X取的是0x80，Y取0x00。

# 三 攻击原理

1 攻击实施条件：

Padding Oracle Attack依赖于两个假设，也就是实施攻击需要满足的先决条件：

- 攻击者可以窃听通信，并且拦截CBC分组模式的密文。

- 攻击者能够访问一个padding oracle O，并且能够区分返回的响应信息表示填充‘有效’或者‘无效’。

另一点是基于服务器的响应，服务器解密和验证padding时返回的不同信息：

- 当信息解密和填充都合法时，服务器会返回类似200 OK这样的有效信息；

- 如果解密过程中填充正确，但是消息内容不正确，也会返回200 OK；

- 如果出现填充错误，我们就会得到一个密码学异常和一个类似 500 Internal Server Error的响应。比如JSF view states填充错误时，会返回 javax.crypto.BadPaddingException: Given final block not properly padded。这就很明显的告诉我们此时填充不正确。

由此可见，攻击者能够很容易的根据错误消息判断填充是否有效，相当于能够访问一个padding oracle。

2、如何解密

(1)PKCS#5

这里我们首先以PKCS#5填充标准为例，介绍Serge Vaudenay在EURO-CRYPT 2002上提出的Padding Oracle Attack的基本原理。

首先给出几个符号定义，‘b’表示block的字节长度，比如AES中b=16，DES中b=8；N表示明文包含的block数量；在PKCS#5中，一个block序列x1, x2,……xN填充有效，是指xN最后一个字节为0x01，或者最后两个字节为0x02，0x02，以此类推。

<1 Last Word Oracle：

Last Word Oracle，能够获得任意密文块y最后一个字节对应的明文内容。如图所示：

![原文图片](/media/2016/06/eee45df715a987bfe762158748b5d92e)

![原文图片](/media/2016/06/3f4a8e515ce85c471026a0b739291c53)

当然也会出现填充为0x02,0x02或者0x03,0x03,0x03这样的情况，这时也会返回valid，此时从r的最左端一个字节更改，观察是否返回仍然为valid，如果是说明从这个字节往后都是填充，否则向右移动更改r的字节。Vaudenay给出了具体的实施过程：

![原文图片](/media/2016/06/f18e2453c192753143d8d14fada7678f)

这个过程的思想就是枚举‘IV’，密文y解密的中间值不变，通过枚举r，使得异或后的明文产生变化，根据填充方式的特征以及返回的Oracle信息，判断异或后明文最后字节的内容，从而获得中间值对应字节的内容，进一步与前一密文块对应字节异或得到y的最终明文。

<2 Block Decryption Oracle

获得密文块y最后一个字节明文内容后，可以通过修改r_b的值，使得C^(-1) (y)⊕r最后一个字节值为0x02，然后枚举r_(b-1)，如果得到O(r||y)=valid，说明倒数第二字节内容也为0x02，进一步得到倒数第二字节的内容，依此恢复出整个block的内容。

![原文图片](/media/2016/06/67a8c69b4f9c6201d5634e180d8bfa1e)

以上Padding Oracle Attack攻击原理，是Vaudenay于EUROCRYPT 2002大会上提出，主要针对PKCS#5填充标准。下面介绍对于其他填充方式的Padding Oracle Attack。

(2) ISO/IEC 9797-1

ISO/IEC 9797-1第三种填充方式上面我们介绍过：

![原文图片](/media/2016/06/27f9807f9383877ccde22a7e0c7aa3f7)

针对ISO/IEC 9797-1第三种填充方式的Padding Oracle Attack，是Kenneth G. Paterson等在2004年CT-RSA大会上提出，攻击方式分为两个步骤：

<1 确定L_D

![原文图片](/media/2016/06/415e268146193bba849603583278e0a4)

第一种情况：填充完之后数据block数量q>=3（包括L_D）

在CBC分组模式中，改变密文块C_j中第i位，同时会影响解密后的明文P_(j+1)的第i位。当更改倒数第二块密文块C_(q-1)的任意一位，最后一块明文P_q对应的位也会改变。因为q>=3，所以倒数第二块不会是L_D对应的密文，L_D内容不会被修改。将更改后的密文发送给Oracle会有两种情况发生：

- 更改到的位是原始的非填充内容。填充信息仍然是完整正确的，这时Oracle会返回valid。

- 更改的位置是padding内容。这时根据L_D计算的填充信息会错误，Oracle返回invalid。

对于返回valid的情况，将更改位向右移动继续尝试，invalid情况，更改位向左移动，直到移动到填充有效和无效的边界，这时就可以得知填充的位数，从而计算出L_D的内容。

第二种情况：填充之后数据block数量q=2

![原文图片](/media/2016/06/497dad802af3408f0140da859e2cf407)

第三种情况：q=2, L_D=0或者L_D=n

构造
![原文图片](/media/2016/06/11c9bd0faeacc6e09aabbc556ca9ff25)

当L_D=0时，未填充明文数据长度为3n，Oracle会返回invalid；L_D=n时，未填充明文数据长度为2n，Oracle会返回valid。从这种方法判断L_D内容。 解密数据

![原文图片](/media/2016/06/85921d92718d797bc609dd6814a0f01f)

![原文图片](/media/2016/06/987856f07e28611a9301330a7df6a326)

![原文图片](/media/2016/06/13a6a35858491ab4d69d68cb828f7540)

以上就是ISO/IEC9797-1标准第三种填充方式的解密原理。

John Black和Hector Urtubia提出的一些填充方式，可以使用类似PKCS#5的攻击方式进行解密，这里不再详细描述。

3、CBC-R

CBC-R是由John Black和Hector Urtubia 在Blackhat Europe 2010大会上提出，攻击者可以在不知道密钥K的情况下，构造任意明文对应的密文。

回顾一下CBC解密公式：
![原文图片](/media/2016/06/ce1fff1e3b3a6af58d9a8883aae9644d)

如果攻击者获得了C_(i-1)和D_k (C_i)，他就可以控制明文P_i的内容。由于攻击者目的是获得任意明文P_i对应的密文C_i，那么对C_(i-1)可以做任意更改。攻击过程如下：

- 攻击者从截获的数据中任意选择C_i；

- 根据前面介绍的Block Decryption Oracle方法，获得D_k (C_i )；

- 异或D_k (C_i )和P_i就可以获得C_(i-1)的内容。

用这种方法，会使P_(i-1)的内容混乱，但是依此修改C_(i-2)的值可以构造P_(i-2)，依此类推构造整个密文块。具体算法如下：

![原文图片](/media/2016/06/2b3981afed283bad390dcd82815bc75b)

在CBC-R攻击中，会出现IV问题。攻击者在构造任意明文对应的密文时，依此类推最后需要修改C_0也就是IV的值，而在现实应用中，IV可能并不能被攻击者控制。IV在现实应用中可能有以下几种情况：

- IV是密文的一部分，可以完全被攻击者控制；

- IV是固定且公开的值，不能被攻击者修改；

- IV保密且固定；

- IV保密且会变化。

对于IV不受攻击者控制的情况，也有解决办法。

假如服务器希望收到头部(第一块明文)含有某些特定信息的数据，并且此时IV不能被控制，这时候可以牺牲中间某块数据的含义，获得有效的头部数据：

![原文图片](/media/2016/06/b0b6d6007736784f1fbaf382f7af3629)

C_capture是攻击者截获的头部密文，这样解密后只是中间某个block失去意义。

# 四 现实应用

1. POODLE攻击

CVE-2014-3566描述了POODLE(Padding Oracle On Downgraded Legacy Encryption)攻击。在SSL v3.0版本中，CBC分组模式填充方式为：·······N，最后一个字节表示填充内容的长度。这种攻击利用SSL v3.0版本对数据先认证后加密的漏洞，使得攻击者可以拿到服务器返回的填充有效（无效）信息，同时攻击者可以截获修改密文，满足实施Padding Oracle Attack的先决条件，从而利用Padding Oracle Attack获取明文内容。攻击者通过控制HTTP请求的路径和主体，使需要解密的敏感信息与分组块对齐，并将对应的密文块放在密文最后，通过多次发送该请求，获得该密文块最后一个字节内容，然后修改HTTP请求的路径，移动敏感数据下一个字节到密文块边缘。依此类推获得敏感信息。

2. Lucky 13

CVE-2013-0169描述了Lucky 13攻击。简单来说，该攻击利用服务器验证HMAC产生的时间差异，进行明文恢复。当服务器检测完CBC分组模式padding时，并不马上返回Oracle，此时会计算HMAC是否正确。而在计算HMAC时，会因为明文长度不同，计算时间也不同。攻击者通过修改密文，利用HMAC计算时间不同，猜测修改后的密文对应的明文的padding内容，依此获得明文信息。

对于Padding Oracle Attack的应用场景还有很多，比如ASP.NET，JSF view states等，这些很多资料中讨论较多，这里不再详细说明。

# 五 防范方法

Padding Oracle Attack的实施条件中提到，攻击者需要截获修改密文内容，另外能够从服务器获取Oracle信息。在使用CBC分组模式时，如果先进行数据填充和加密，然后进行MAC完整性验证，解密之前先检测MAC，再检查padding，就不会出现padding oracle，这样就阻止了攻击者进行此类攻击。

在TLS1.2版本中，使用的是AEAD (Authenticated Encryption with Additional Data)，也就是AES-GCM模式，避免了使用CBC分组模式，同时避免了类似Padding Oracle Attack攻击。

## Replies
