You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

193 lines
12 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 12 | 怎么利用加密端攻击?
你好,我是范学雷。
上一讲我们讨论了怎么利用解密端攻击CBC模式的对称密钥分组算法。那么加密端的攻击方案是怎么进行的有什么办法可以规避加密端的攻击这是我们这一次要解决的问题。
虽然我们之前说CBC模式应该退休了但是现实中它还有广泛的应用。我们花了很多篇幅来讨论CBC模式除了提醒你CBC模式的缺陷外还希望你了解缺陷修补的办法这样你就可以修复现有的应用了。
这一次的讨论,比起上一次,稍显烧脑。因为,这是一个相对较新的攻击技术。不过,我可以保证,如果你了解了这种攻击技术,将会有一个巨大的收获。
## 怎么利用加密端攻击?
我们先来看看是怎么利用加密端攻击的其实CBC模式针对加密端的攻击和针对解密端的攻击方案一样最常见的攻击方式也是通过异或运算展开的。
### 攻击面应该怎么选?
要想了解针对加密端的攻击方案我们先来回顾一下CBC模式的加密过程。在这个过程中一个明文分组的加密需要如下的输入数据
* 上一次的密文分组Ci-1
* 这一次的明文分组Pi
* 加密和解密共享的密钥K。
这一次的明文分组Pi和上一次的密文分组Ci-1进行异或运算获得中间结果Ti。
* 异或运算获得的中间结果TiTi = Ci-1 ^ Pi
然后异或运算获得的中间结果Ti和密钥K通过加密函数的运算产生密文分组
* 密文分组Ci
而加密端的异或运算,发生在明文分组和上一次的密文分组(对于第一次运算,上一次的密文分组就是初始化向量)之间。针对加密端的攻击,同样,我们也是在明文分组和上一次的密文分组或者初始化向量上想办法。
![](https://static001.geekbang.org/resource/image/e7/c2/e739603982e283d7976d95a4fca682c2.jpeg)
### 攻击理论上可行吗?
假设目标明文分组是Pi但是攻击者并不知道Pi的明文内容而他攻击的目的就是解密出Pi的明文数据。由于密文数据不是保密的所以攻击者可以知道所有的密文分组包括参与Pi加密运算的上一次密文分组Ci-1和这一次的密文分组Ci。
如果攻击者可以使用加密接口,攻击者就可以构造明文分组,然后调用加密运算,观测加密运算后的密文分组。如果攻击者观测到密文分组和目标明文分组对应的密文分组相同,那么他就可以确定目标明文分组的具体数据了。
那明文分组该怎么构造呢像解密端的攻击方案一样我们还是要利用异或运算的归零律和恒等律。假设已经知道了Pi当前的加密运算需要使用上一次的密文分组Cj-1如果攻击使用的明文可以构造成下面的形式那么攻击就能够成功
> Pj = Pi ^ Ci-1 ^ Cj-1
加密运算后的结果,就是目标明文分组对应的密文分组。
> Tj = Pj ^ Cj-1
> \= Pi ^ Ci-1 ^ Cj-1 ^ Cj-1
> \= Pi ^ Ci-1
> \= Ti
> \=>
> EkTj= EkTi
> \=>
> Cj = Ci
> Tj ^ Ci-1 = Ti ^ Ci-1 = Pi ^ Ci-1 ^ Ci-1 = Pi
> Pi = Tj ^ Ci-1 = Pj ^ Cj-1 ^ Ci-1
假设终归是假设攻击者要是知道明文分组也就不需要实施攻击了。似乎这个美好的运算并不能够执行。对于AES算法来说一个明文分组有16个字节也就是128位。如果一位一位地尝试需要2^128次运算。这种数量级的运算现在的计算能力还是无能为力的。
### 攻击实践上可行吗?
那么既然完整破解有16个字节的明文分组不可行能不能只破解一个字节
怎么破解一个字节呢我们还要继续从假设开始。假设攻击者已经知道了明文分组里的15个字节只剩下最后一个字节未知。攻击者能不能破解出这个字节这样的破解太简单了。
攻击者只需要改动最后一个字节按照上述的构造明文分组的方式他最多尝试255次最后一个字节就可以破解了。原则上只要有一个字节能够被破解这个算法就算是被破解了。
不过我们还需要验证这个假设有没有实际的可能性也就是说一个明文分组里攻击者有没有可能会知道15个字节只有1个字节是未知的
遗憾的是网络里的数据大部分都是格式化的比如HTTP的数据。下面的数据是我在写这些文字的时候从geekbang.org登录页面记录下来的HTTP请求的头部数据。
这里面的数据除了Cookie之外大部分都是重复的数据。
```
POST /account/sms/login HTTP/1.1
Host: account.geekbang.org
Connection: keep-alive
Content-Length: 371
sec-ch-ua: "\\Not;A\"Brand";v="99", "Google Chrome";v="85", "Chromium";v="85"
Accept: application/json, text/plain, */*
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36
Content-Type: application/json
Origin: https://account.geekbang.org
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://account.geekbang.org/signin
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: <deleted>
```
以及HTTP的请求内容。请求的内容除了电话号码cellphone和手机验证码之外code其他的数据也都是重复的。
```
{"country":86,"cellphone":"12345678901","code":"123456","ucode":"","platform":3,"appid":1,"remember":1,"source":"","sc":{"uid":"","report_source":"Web","utm_identify":"","utm_source":"","utm_medium":"","utm_campaign":"","utm_content":"","utm_term":"","share_code":false,"original_id":"1746eb1327656f-05d10c17083f0f-f7b1332-2073600-1746eb132773ba","refer":"极客时间"}}
```
所以攻击者知道15个字节甚至整个明文分组这种情况在实际中是经常出现的。
我们真正关心的是敏感数据,比如手机验证码,是不是能够被破解。那么,首先,我们要了解的是,攻击者能不能破解手机验证码的第一个字节呢?
如果手机验证码的第一个字节刚好是一个明文数据分组的最后一个字节前面我们已经讨论过了这个字节就是可以破解的。由于手机验证码只能是数字破解这个字节最多需要9次计算。比起255次9次计算就更简单了。
![](https://static001.geekbang.org/resource/image/72/af/72aa42496807b1270209e2b1820d3caf.png)
如果手机验证码的第一个字节不是明文数据分组的最后一个字节,该怎么办?思路也很简单粗暴,只要我们想办法把手机验证码的第一个字节挪到明文数据分组的最后一个字节就行。
该怎么操作呢?也很简单,通过更换顺序、删减或者增加手机验证码的前面的字节,就可以了。
![](https://static001.geekbang.org/resource/image/bd/87/bdb098af1d93e93624fd4c3dab9b5587.png)
既然可以想办法把手机验证码的第一个字节挪到明文数据分组的最后一个字节,当然也可以想办法把手机验证码的第二个字节、或者第三个字节,或者任意一个字节挪到明文数据分组的最后一个字节。
每当这个字节是明文数据分组的最后一个字节时最多9次就可以破解这个字节了。一个6位数字的手机验证码攻击者破解起来最多需要6 \* 9 = 54次。
![](https://static001.geekbang.org/resource/image/87/3b/879549d87906224e1d38a1d204dfcb3b.png)
是的如果加密使用的是CBC模式54次运算6位数的手机验证码就可以破解出来了
2011 年 9 月两位天才般的研究人员Juliano RizzoThai Duong公开了上述的攻击方案。并且表示只要给他们几分钟时间他们就可以利用该漏洞入侵你的支付账户。他们给这个攻击技术取了一个超酷的名字叫做BEAST。
2011年9月之前的岁月CBC模式一直是加密算法的主流模式几乎所有的网络加密都选用CBC模式。2011年以后CBC模式就开始正式办理退休手续了。新的协议或者应用不应该继续使用CBC模式了现有的协议或者应用也需要采取措施防范BEAST攻击。
## 有什么防范措施?
那现有的协议或者应用,要怎样采取措施来防范攻击呢?
其实防范措施还是要在初始化向量上想办法。BEAST攻击起作用的关键就是要使用上一次加密运算的最后一个密文分组。
![](https://static001.geekbang.org/resource/image/54/85/544ba69455899ca1866a5765c3907d85.jpg)
如果这一次的加密运算不使用上一次加密运算的数据BEAST攻击就无法运算了。该用什么替换上一次加密运算的最后一个密文分组呢你可以先思考一下。
和防范补齐预言攻击的办法一样,替换的办法就是每一次加密运算,都使用不同的初始化向量。
![](https://static001.geekbang.org/resource/image/7f/8f/7fd1fc3275ae0946fb0bd3e884dd658f.jpg)
比较麻烦的是,对于每一次解密运算,解密端需要加密端使用的初始化向量,然后才能执行解密运算。可是,初始化向量的同步不是一件容易的事情,特别是在数据包可能被有意无意丢弃的环境下。
## 有没有改进的防范措施?
一个改进的办法,就是继续使用上一次加密运算的最后一个密文分组,同时把每一次运算的初始化向量当做第一个明文分组来处理。
这个办法的好处,就是解密端不需要知道加密端选择的初始化向量,就可以执行解密运算。这个办法的坏处,就是解密端需要丢掉初始化向量这一段数据,不能把它当做应用数据来处理。
![](https://static001.geekbang.org/resource/image/38/63/388460ebc9d50a121441e61510946f63.jpg)
如果新的应用不应该再使用CBC模式那么现在推荐使用的加密模式是什么呢在讨论这个新模式之前我们还需要补充一块知识那就是消息验证。下一次我们讨论消息验证的机制然后我们就有足够的知识去了解这个新的加密模式了。
## Take Away今日收获
今天我们讨论了怎么在CBC模式的加密端通过挪移重构明文分组来展开攻击以及针对攻击的防范措施。
除了知道加密端攻击手段和防范措施之外我们还要了解这种挪移重构明文分组的攻击办法。这种攻击办法自BEAST攻击发布以来已经被成功运用于很多密码学算法的破解。如果你需要设计应用协议要小心应对明文数据的挪移以及重构会不会构成威胁的问题。
今天,我们应该:
* 知道CBC模式存在BEAST攻击
* 知道使用初始化向量来阻断BEAST攻击。
## 思考题
今天的思考题,稍微有点和以前不同。这一次,我们去挑战一下怎么给标准协议打补丁。
标准协议一旦成为标准也就意味着它的行为模式有了规范不能随意更改。我们假设一个标准协议已经使用了CBC模式加密它的通信数据而且只有第一次加密运算使用了初始化向量。
后续的加密运算都不再使用初始化向量。也就是说这个协议存在安全漏洞是BEAST攻击的目标。毫无疑问由于是现成的标准我们不能够修改这个协议让每一次的加密运算都使用一个不同的初始化向量。
那么,还有没有办法修复这个协议的漏洞,使得修复后的协议实现,还能够和旧的协议实现通信,而没有明显的兼容性和互操作性问题?
这个问题稍微有点难度。在BEAST攻击发现之前业界已经知道CBC加密端攻击存在理论上的可能性。BEAST攻击的出现简化了攻击方案使得CBC加密端的攻击成为现实。
业界尝试了很多年都没有找到理想的办法BEAST攻击发现之后也经过很长一段时间才找到了解决的办法。最后的解决方案简简单单又出乎意料。
你来试一试,能不能找到这个简单的方案,或者有没有更好的解决方案?
欢迎在留言区留言,记录、讨论你的发现。
好的,今天就这样,我们下次再聊。