The Heartbleed Bug is a serious vulnerability in the popular OpenSSL cryptographic software library. This weakness allows stealing the information protected, under normal conditions, by the SSL/TLS encryption used to secure the Internet. SSL/TLS provides communication security and privacy over the Internet for applications such as web, email, instant messaging (IM) and some virtual private networks (VPNs).
The Heartbleed bug allows anyone on the Internet to read the memory of the systems protected by the vulnerable versions of the OpenSSL software. This compromises the secret keys used to identify the service providers and to encrypt the traffic, the names and passwords of the users and the actual content. This allows attackers to eavesdrop on communications, steal data directly from the services and users and to impersonate services and users.
关于Heartbleed 的常见问题和描述,可以看The Heartbleed Bug ps.专门为了这个漏洞建了一个网站可以想象这个漏洞在当年多么严重。
Heartbeat & TCP keepalive
首先是heartbeat
,它是计算机中周期性发送的某些操作或同步的信号,常常用于检测或判断资源是否可用。
在默认情况下,在建立TCP连接之后,空闲时刻客户端和服务端不会互相发送数据包确认连接。假如有一端发生异常而掉线(如死机、防火墙拦截包、服务器爆炸),另一端若不进行连接确认,则会一直消耗资源。
为了保证连接的有效性,可以检测到对方端非正常的断开,我们通常利用两种机制来实现:
- 利用TCP协议的
Keepalive
- 在应用层实现心跳检测
Heartbeat
TCP keepalive
通过定时发送Keepalive探测包来探测连接的对端是否存活.在收到对端的确认报文后,设置keepalive timer。
当长时间两端无交互并且保活定时器超时的时候,本段会发送keepalive probe等待对端确认。若对端在一定时间内确认(具体的规则貌似比较繁琐),keepalive timer重置;否则,长时间未响应,连接终止。
TCP Keepalive默认是关闭的,因为它会消耗额外的资源,并且可能会关闭正常的连接。Linux 默认net.ipv4.tcp_keepalive_time
为7200s
TCP keepalive 与 HTTP keep-alive
- 前者探测连接是否存活
- 后者是让连接活的久一点
Heartbeat
- 应用层Heartbeat往往由自己编写
- 灵活且可复用,应用层心跳包不依赖于传输层协议,无论传输层协议是TCP还是UDP都可以用
- 应用层心跳包可以定制,可以应对更复杂的情况或传输一些额外信息
- TCP keepalive仅代表连接保持着,而心跳包往往还代表客户端可正常工作
OpenSSL
OpenSSL
是一个开放源代码的软件库,应用程序可以使用这个包来进行安全通信,避免窃听,同时确认另一端连接者的身份。这个库被广泛应用在互联网的网页服务器上。其主要库是以C语言所写成,实现了基本的加密功能,实现了SSL与TLS协议。关于SSL/TLS,可以参考我的这篇文章TLS/SSL流程详解
OpenSSL漏洞不仅影响以https开头的网站,攻击者还可利用此漏洞直接对个人电脑发起Heartbleed
攻击
|
|
漏洞影响
主要影响涉及开启了Heartbeat扩展的OpenSSL版本1.0.1f, 1.0.1e, 1.0.1d, 1.0.1c, 1.0.1b, 1.0.1a, 1.0.1
可以通过以下代码判断网页是否开启了SSL Heartbeat扩展
|
|
漏洞分析
这次的漏洞属于SSL实现问题,协议本身没有问题。引用一张科普图来解释这个洞。
通俗的解释就是OpenSSL
使用heartbeat
探测对方主机是否在线,提供一个字符串和其长度,希望对方回应返回原样内容,但却未对长度与实际长度做验证,导致内存信息越界访问。由于可以不断尝试,返回的数据有可能是任何内容:用户请求密码,甚至是服务器私钥都有可能。
|
|
在存在问题的版本,其逻辑是这样的,假设hb
上面上发过来心跳包结构,type
是类型,length
是data长度,*data
是实际的内容,所以类型是1字节,长度是2字节,剩下的length字节是数据,我们找出openssl-1.0.1e的源码
|
|
定义完之后:
|
|
我们可以看到,首先hbtype
取了p
的第一个字节,n2s(p,payload)
函数是将p
指向的2个字节赋值给payload
,然后再p+2
, pl
指向剩余的部分。
|
|
代码非常清晰(我这种不会c的都看得懂):
- 首先判断数据包类型,如果接收到的是心跳包,则处理
- 为其分配了
1 + 2 + payload + padding
字节的缓冲区,用于返回内容,记做buffer
,bp
指向同一位置 - 设置返回包数据类型
TLS1_HB_RESPONSE
,s2n
同n2s
用于赋值payload
长度 - 然后从
pl
出复制了payload
长度的内容给bp
- 之后给
bp
随机填充
我们可以看到,由于payload
的是用户传递过来了,其并不一定等于实际消息内容,当payload
值大于pl
中实际剩余内容的长度时,就造成了漏洞,由于我们不知道pl
后面内存中的内容,由于当时全网ssl还没有普及,一些关键部分使用ssl反而更加严重。(例如,账号密码,HTTP报头cookie)
我们可以分析,由于payload
为2字节unsigned int
,最大值为65525
,即最多泄露64KB
内容
补丁分析
|
|
我们可以看到,相比于之前直接取2个字节给payload
,其验证了长度和考虑的长度为0的情况,其余与原来的代码一致。
漏洞利用
官方的POC如下,我们做一下分析:
|
|
我们可以看到,先建立了socket连接,然后发了client Hello
,hello的内容为那一群二进制,推测应该是SSL client Hello的内容,我们抓包看一下,具体的字段分析可以看之前的SSL文章,这里注意,扩展中开启了heartbeat
:
直到收到(typ==22)
即 server hello
类型的数据包,然后下一步。发送hb
然后调用hit_hb
,其中hb
内容为我们构造的恶意heartbeat
数据包
其中request message
为01 40 00
分别对应:
|
|
这里只返回了16KB 的内容,极限是F0 00(64KB)