赞
踩
昨天 OpenSSL 爆出了名为 Heartbleed 的重大安全漏洞(CVE-2014-0160),通过 TLS 的 heartbeat 扩展,可以读取运行 HTTPS 服务的服务器上长达 64 KB 的内存,获取内存中可能存在的敏感信息。由于这个漏洞已经存在三年,Debian stable (wheezy) 和 Ubuntu 12.04 LTS、13.04、13.10 等流行的发行版都受影响,无数部署 TLS(HTTPS)的网站都暴露在此漏洞之下。
SSL 全名 Secure Socket Layer,是在 TCP 连接的基础上的安全层,提供了认证、加密等功能。只要你在上网的时候看到网址旁边有个小绿锁,或者网址是 https 开头的,或者网址栏是绿色背景的,就说明你在使用 SSL。涉及到金钱或敏感信息的网站,一般都会使用 SSL 安全连接。
SSL 连接建立的时候需要进行两个来回的握手,第一次握手协商所用的加密方式、获取服务器端的数字证书,第二次握手协商后续数据传输所使用的对称加密密钥(好比两个人协商一个公共密码)。如果服务器在大洋彼岸,这中间的延迟有几百毫秒。因此我们希望尽量重用已经建立的 SSL 连接。
问题是我们并不是在不停地点击网页,服务器怎么知道连接对面的人还 “活着” 呢?如果对面的人 “死掉” 了,服务器需要尽快释放资源,不然就会被僵尸连接占满。这就需要客户端隔一段时间就发一个 “心跳”,告诉服务器 “我还活着”。服务器也会返回一个心跳告诉客户端 “我还没把你忘掉”。这就是 SSL heartbeat 的由来。
SSL 安全连接层又分为两层:底层是记录层,这一层是不加密的,用 wireshark 就能抓出来;上层是握手信息、密钥信息、用户数据、心跳包等,加密后封装成一个个 “记录”。SSL 安全连接层之上,就是我们熟悉的 HTTP 等协议了。也就是说,SSL 安全连接层是透明地插在应用层(如 HTTP)和传输层(如 TCP)之间的。
SSL heartbeat 跟上图中的 Handshake 等并列。一个 SSL heartbeat 由下述 4 个字段构成:
以下漏洞分析基于 OpenSSL-1.0.1e 源码。TLS 是 SSL 的一种传输方式,是比传统 SSL 更安全的,因此如果客户端和服务器都支持,会优先采用它。当服务器收到一个 TLS heartbeat 包时,就会调用 ssl/t1_lib.c 中的 tls1_process_heartbeat 函数(如下图所示)。
注意,不要与 dtls1_process_heartbeat 这个 “孪生兄弟” 弄混,DTLS 是基于 UDP 的 TLS,我们网站一般都是用 TCP 传输,因此调用的是 tls1_process_heartbeat。
不说有没有漏洞,相信有人已经闻到代码里的 “坏味道” 了。变量名 p、bp、pl 都是什么意思?也许能猜出来,但这种命名方式实在是不敢恭维。也许正是因为这些代码生涩难懂,才让 bug 有了藏身之地。
从前面的分析可以看出,这个漏洞利用方式简单,除 OpenSSL 版本外不需要额外条件,不需要考虑服务器上装的是何种 Web 服务器软件、有没有特殊路径的文件等。效果也非常明显:读出最多 64 KB 的服务器内存,且不会在服务器日志里留下痕迹。64 KB 是由于记录层最大记录长度的限制,事实上一个记录最大长度是 65535,根据心跳包格式掐头去尾,载荷长度最大是 65535 – 1 – 2 – 16 = 65516 个字节。
我写了一段 POC(Proof Of Concept)代码来验证。由于我比较懒,就不去研究 SSL 记录层格式和 SSL 握手那些麻烦事了,借用 OpenSSL 客户端库,把发送 heartbeat 请求的 payload 字段改大。如下图所示,修改 ssl/t1_lib.c 的 2599 行,把 payload 换成不超过 65519 字节的数值。这样真正的载荷只有 18 字节,也就是返回的响应里只有前 18 字节是与发送的载荷一致,后面的接近 64 KB 全都是不该泄露的服务器内存。
剩下的部分就是用客户端库完成 SSL 握手、发送和接收 heartbeat 了。由于修改的 tls1_heartbeat 函数是未导出的私有函数,需要直接链接上 .o 文件。(具体的编译命令参见 POC 代码开头的注释)。我把代码放到 gist 上了:POC 代码
这段 POC 代码可以发送正常的 HTTPS 请求来做测试,也可以在 SSL 握手完成后发送 heartbeat 请求并等待响应。如果服务器不支持 TLS heartbeat 扩展或者已经修补了这个漏洞,就不会收到任何响应,此时需要按 Ctrl+C 退出。如果服务器中招了,就会以类似 hexdump -C 的格式输出接近 64 KB 的服务器内存。
首先拿 CSDN 热热身,SSL 证书赫然可见,下图是 X.509 Subject 部分,其中的那些 \x 是 UTF-8 表示的中文字符,翻译过来就是那家公司的中文名字。当然,似乎这里面没有 private key,因此影响不是很大。
我们可爱的 12306 怎么样呢?看,这位哥正买 2014 年 4 月 8 日从呼和浩特(电报码 HHC)到熊岳城(电报码 XYT)的成人票呢!
某公司内部网站,泄露了 nginx 配置。
就连雅虎这么大的网站也未能幸免,用户发送的 HTTP 请求中的 Cookie 清晰可见,据此可以伪装成其他用户登录。
某个人博客上刚发表的评论(垃圾评论,呵呵)。
更有趣的是,由于服务器内存的内容是在不停变动的,每次 SSL heartbeat,泄露出来的 64 KB 内容都可能是不同的。
USTC LUG 所有服务器对 OpenSSL 进行了紧急升级 以应对这个漏洞。
RFC 6520 中明确规定 heartbeat 包的长度不能超过 2^14(16384),如果载荷过长,这个 heartbeat 请求应该被丢弃:
The total length of a HeartbeatMessage MUST NOT exceed 2^14 or max_fragment_length when negotiated as defined in [RFC6066].If the payload_length of a received HeartbeatMessage is too large, the received HeartbeatMessage MUST be discarded silently.
但 OpenSSL 的实现者们似乎把 RFC 中关于最大载荷长度的限制当成了耳旁风(不然最多泄露 16 KB 而非 64 KB 内存),而且完全信任客户端发来的载荷长度。信任用户的输入在网络编程中是非常危险的,但就是这样拙劣的 bug,在数以百万计的服务器上藏身长达三年之久(这段代码是 2011 年引入的)。
微软漏洞应急响应中心的人曾说,软件的安全问题要靠专业的人去排查和测试,只是眼球足够多,bug 并不一定就能被发现。OpenSSL 作为网络安全的底层支持,应该有足够多的眼球关注了,但仍然不止一次爆出安全漏洞,这是值得我们深思的。
值得称道的是此次漏洞的发现和处理过程:漏洞的发现者没有急于公之于众,而是通过 CVE 平台向大型互联网公司和主要 Linux 发行版的维护团队报告,让关系互联网 “生死存亡” 的大型网站先补上漏洞,Linux 发行版也准备好了补丁。在预定的时间,漏洞向全世界公开,一时间网络上关于此漏洞的报道铺天盖地,此时普通用户只要应用 Linux 发行版准备好的补丁,就能有效防御漏洞,这使得漏洞对整个互联网的危害降到最低。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。