商密测评:“心脏滴血漏洞”测评经验分享
1 前言
1.1 编写目的
“心脏滴血漏洞”测评经验分享
1.2 读者对象
本文档读者对象为:测评相关工程师、项目管理者、测试工程师、质量管理人员、文档工程师等技术人员。
1.3 名词术语
1.4 参考文档
2 测评前的准备
2.1 心脏滴血漏洞是什么?
Heartbleed漏洞,也叫“心脏滴血漏洞”。是OpenSSL加密软件库中的一个严重漏洞。这个漏洞允许窃取正常情况下使用SSL/TLS加密保护的信息;
自2011年12月31日,漏洞就已经存在,而且随着OpenSSL版本1.0.1于2012年3月14日释出,有缺陷的代码被互联网广泛使用;Heartbleed漏洞允许互联网上的任何人不受限制的读取受OpenSSL漏洞保护系统的内存。攻击者可根据漏洞窃听信息,直接从服务和用户那里窃取潜在的敏感数据,甚至包括服务器的专用主密钥;
如果存在此漏洞;攻击者可以通过被动中间人攻击,获取此时服务器内存中存储的信息(如果服务器和客户端未使用完全正向保密,或使用了完全正向保密时攻击方发动“主动中间人攻击”)。攻击者无法控制服务器返回的数据,因为服务器会使用一个随机内存块(最大64KB)作为响应;而且攻击者可以不断发送攻击请求报文,获取服务器内存中的数据。
安全公司Codenomicon和Google工程师Neel Mehta最早于2014年4月8日发现(公布)了 OpenSSL的Heartbleed漏洞;安全公司Codenomicon为这个漏洞制作了一个符合漏洞形象的logo;Codenomicon公司的工程师“Ossi Herrala”为漏洞名起了很炫的名字:“Heartbleed”;心脏滴血的名称由此而来。
2.2 漏洞范围
Heartbleed于2011年12月被引入到OpenSSL中,自OpenSSL于2012年3月14日发布1.0.1以来就一直存在。2014年4月7日发布的OpenSSL 1.0.1g修复了这个漏洞。.
漏洞详细说明见:https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-0160
自2014年漏洞发布起即全球%30的网站存在被攻击的风险,漏洞影响的范围到底有多大? 根据以下当年发布的信息可以说明情况;
节选来自Heartbleed的官方说明:OpenSSL在Web容器如Apache/Nginx中使用,这两款中间件的全球份额超过66%。一些大型的互联网公司常使用 OpenSSL,而这曾经被认为是最安全的数据传输手段之一;
众多网络路由器厂商包括 Cisco Systems 与 Juniper Networks等,在漏洞公布当天纷纷发布了紧急公告,列出一系列受此漏洞影响的路由器产品。虽说这些产品由于使用旧版SSL,厂商也表示会尽快更新补洞,据悉,有的黑客在漏洞发布一年前就已经在利用这个漏洞了,获取到了不少大网站的敏感信息;
国内知名企业如淘宝、阿里、360、京东(滚动资讯)、微信、支付宝等公司的技术团队也在漏洞发布当天彻夜奋战,和黑客们开展起了一场“你盗我堵”的赛跑,在黑客窃取更多用户数据前赶紧予以修复…
下面图片反应了2014年在发布漏洞后的紧急情况:
2.3 漏洞描述
Heartbleed漏洞,这项严重缺陷(CVE-2014-0160)的产生是由于未能在memcpy()调用受害用户输入内容作为长度参数之前正确进行边界检查。攻击者可以追踪OpenSSL所分配的64KB缓存、将超出必要范围的字节信息复制到缓存当中再返回缓存内容,这样一来受害者的内存内容就会以每次(每次SSL心跳报文请求)64KB的速度进行泄露;原理如下图所示:
2.4 漏洞原理
2.4.1 心跳检测报文(Heartbeat)
要对心脏滴血漏洞进行更好的理解,首先需要知道心跳检测报文的作用和运行原理,这里做简要解释;
(A)心跳检测报文的作用
TLS心跳扩展报文为TLS/DTLS提供了一种允许在不重新进行商议和发送路径MTU探索包(PMTU)的情况下而使用保持持续通信功能的新协议。
所谓心跳检测,就是建立一个 Client Hello 问询来检测对方服务器是不是正常在线 ,服务器发回 Server hello,表明正常保持SSL通讯。就像我们打电话时会问对方“喂听得到吗?”一样;
每次问询都会附加一个问询的字符长度“pad length”,如果这个“pad length” 大于实际的长度,应答方仍是会返回相同字符长度的字符信息。
(B)TLS/DTLS心跳检测报文的结构
TLS数据包格式如下:
DTLS数据包格式如下:
(C)TLS/DTLS心跳检测报文抓包示范
心跳检测报文包括A、请求包(heartbeat Request)和B、响应包(heartbeat Response);本文以常见TLS数据包为例,通过wireshark进行抓包获取信息如下:
A、TLS请求包抓包示例如下:
B、TLS响应包抓包示例如下:
通过对比心跳检测请求报文和响应报文不难看出,响应报文和请求报文使用相同的报文格式;如果仔细观察请求报文,可以发现请求报文请求的报文长度是16384,但实际的载荷(Payload)值为0;但应答方回复请求方的实际载荷(Payload)为16381(约64KB);这里,就是心脏发生滴血的地方;即可以发现攻击方通过制造畸形心跳检测请求报文进行了攻击;
如果打开应答报文Payload字段,可以看到因漏洞产生内存溢出的数据;溢出的数据是随机的,但是攻击方可以通过多次进行攻击,从而获取更多应答方内存中的数据,直到获取有价值的数据;而且64KB是可以泄露出足够多的信息的;如下图攻击方通过伪造报文已经获取了关键的用户账户和口令信息:
这里还想说一些题外话,此类漏洞主要是因为攻击方制造了畸形报文进行了攻击,目前可以抵御的手段可以通过IPS、WAF等安全防护规则库进行拦截;但根本上最好的修复方法还是更新SSL/TLS版本;后续的SSL/TLS版本中对协议代码进行了优化(对输入内容作为长度参数之前正确的进行了边界检查)。
2.4.2 代码原理示例
漏洞出现在SSL服务ssl/d1_both.c中,对用户传入的payload长度没有检查就进行内存拷贝;下面是代码示意:
漏洞出现在SSL服务ssl/d1_both.c中,对用户传入的payload长度没有检查就进行内存拷贝;下面是代码示意:
1)基本结构
-------------------------------------------------------------------------
int
dtls1_process_heartbeat(SSL *s)
{
unsigned char *p = &s->s3->rrec.data[0], *pl;
unsigned short hbtype;
unsigned int payload;
unsigned int padding = 16;
}
-------------------------------------------------------------------------
2)p指向一条SSLv3的记录,结构如下:
-------------------------------------------------------------------------
typedef struct ssl3_record_st
{
int type;
unsigned int length;
unsigned int off;
unsigned char *data;
unsigned char *input;
unsigned char *comp;
unsigned long epoch;
unsigned char seq_num[8];
} SSL3_RECORD;
-------------------------------------------------------------------------
3)回到上面的程序,接下来对指针进行一些操作:
-------------------------------------------------------------------------
hbtype = *p++;
n2s(p, payload);
pl = p;
-------------------------------------------------------------------------
4)这里先读取个type,然后n2s把下一个length的两个字节放到payload里面,作为后面内存拷贝的长度;
-------------------------------------------------------------------------
unsigned char *buffer, *bp;
int r;
buffer = OPENSSL_malloc(1