4月8号,公开的Heartbeat 漏洞,对于这个漏洞听说是今天以来最大漏洞,可以获得服务器内存最多64KB数据,可能包括私人重要的信息。自己也对这个漏洞感兴趣,之后就做了这面的了解,并在漏洞修补之前,用openssltest.py进行了测试,在有漏洞的网站,真的可以获得…………………… 下面是通过资料,对其中涉及到的原理,和查看openssl源代码做了简要的分析。
本文主要从下面几个点讲解:
1、Heartbleed漏洞及漏洞发生的原因
2、SSL/TLS/DTLS协议简单的认识
3、TLS的扩展Heartbeat协议认识
4、heartbeat协议实现代码解读
5、实现一个网站支持SSL协议(生成一个伪证书)
1、Heartbleed漏洞及漏洞发生的原因
Heartbleed漏洞藏身于OpenSSL的TLSHeartbeat扩展当中:这是一项持续作用型功能,其中一个连接端会向另一端发送任意数据的有效负载,对方则发回对应数据的精确副本以确保传输过程一切正常。而在实现这个过程中,由于未能在memcpy()调用受害用户输入内容作为长度参数之前正确进行边界检查。攻击者可以追踪OpenSSL所分配的64KB缓存、将超出必要范围的字节信息复制到缓存当中再返回缓存内容,这样一来受害者的内存内容就会以每次64KB的速度进行泄露。
网上资料显示在OpenSSL 1.0.1b到OpenSSL1.0.1f 都存在这个漏洞。对于更低版本的比如OpenSSL0.98的还没采用这个Heartbeat扩展,所以使用此版的不会受到攻击。
2、SSL/TLS/DTLS协议简单的认识
受到Heartbleed漏洞的网站都是支持SSL协议的。SSL全名Secure Socket Layer,是在 TCP连接的基础上的安全层,提供了认证、加密等功能。SSL连接建立的时候需要进行两个来回的握手,第一次握手协商所用的加密方式、获取服务器端的数字证书,第二次握手协商后续数据传输所使用的对称加密密钥。具体建立连接的流程如下:
a. 首先,客户端发一个心跳请求包给服务器;
b. 服务器收到客户端发来的请求包之后,会将自己的的证书副本和公钥发个客户端;
c. 客户端收到证书后,会检查证书的有效性、合法性,如果证书是合法的,会将自己的一个密钥(回话密钥)通过服务器发来的公钥加密,发个服务器。
d. 服务器收到信息用自己的密钥解密得到回话密钥。
e. 之后服务器和客户端进行会话通信时,都是通过这个会话密钥来进行加密解密的。
安全传输层协议(TLS)用于在两个通信应用程序之间提供保密性和数据完整性。该协议由两层组成:TLS 记录协议(TLS Record)和TLS 握手协议(TLS Handshake)。较低的层为TLS 记录协议,位于某个可靠的传输协议(例如 TCP)上面。它与SSL协议很相似,区别TLS比SSL更安全、更复杂等;实现客户端和服务器的握手连接都是一样的过程。
3、TLS的扩展Heartbeat协议认识
在服务器与客户端建立连接为什么要采用Heartbeat扩展?我通过参考RFC6520大概知道其中的原因:在没有实现TLS扩展之前,不管是通过SSL/TLS、DTLS协议连接的客户端和服务器不可能一直保持连接,但超过时间,或者客户端方没有来得及发送请求,之后服务器误认为客户端离开了,所以中断这条连接来释放资源;当同样的客户端在继续进行上次的连接,必须要重新与服务器重新协商,建立连接,而重新协商连接消耗很大。而实现Heartbeat扩展可以解决这个问题。
在采用Heartbeat扩展协议时,当发生上面的情况时,只要客户端发送心跳请求包,服务器会一般会将收到请求包作为心跳响应包立刻发给客户端以确定彼此在线,以支持持续通信功能。基本的流程是:客户端发送一段固定长度的字符串到服务器,服务器接收后,返回该固定长度的字符串。比如客户端发送"hello,world"字符串到服务器,服务器接受后,原样返回"hello,world"字符串,这样客户端就会认为openssl服务器是可用的,但是不需要做重新协商。在RFC6520中有详细过程描述。
可以继续认识TLS Heartbeat协议数据包格式(在REC6520中有介绍),一个TLS heartbeat由下述 4 个字段构成:
(1)心跳包类型:1字节,只有请求和响应两种可能;
(2)载荷长度:2字节,按 RFC 6520规范,不能超过 2\^14 = 16384,不过 OpenSSL的实现只在发送请求的客户端检查了,服务器端没有检查;
(3)载荷(payload):载荷长度这么多字节,可以是任意字节。请求发送什么载荷,服务器就应该响应同样的载荷,就像是 “回显”;
(4)填充(padding):至少 16字节,要把一个 SSL记录填满。请求和响应的填充字节都应该是随机的。
在读RFC文档时,我感觉重要的是知道四个要点:
(1)理解为什么要采用Heartbeat扩展;
(2)明白服务器与客户端在实现心跳协议时,发生的心跳包是相同的;
(3)知道服务器器收到客户端发来的请求心跳包时,有两个放弃请求包的条件,不包含实际数据和要求的载荷长度过长;
(4)明白Heartbeat协议结构
理解了要面四个点,后面读Heartbeat扩展协议的实现代码很方便。
4、Heartbeat协议实现代码解读
SSL/TLS协议时一套理论,最后要应用到实际的生活中,还得靠具体的人编写程序来实现。实现一个算法或者说协议是非常庞大艰巨的工程,OpenSSL提供的功能相当强大和全面,囊括了主要的密码算法、常用的密钥和证书封装管理功能以及SSL协议,并提供了丰富的应用程序供测试或其它目的使用。
由于漏洞发生是在实现Heartbeat扩展那部分代码,所以我就认真分析了这块代码。该分析依据的OpenSSL的版本号为:OpenSSL1.0.1c存在该漏洞的源文件有两个ssl/D1_both.c和ssl/T1_lib.c。心跳处理逻辑分别是dtls1_process_heartbeat和tls1_process_heartbeat两个函数。由于这个两个函数的内容完全一致,所以我就分析了tls1_process_heartbeat函数。函数可见图(一)
在源码中找到了
unsigned char *p = &s->s3->rrec.data[0];具体的数据结构。
首先找到 (SSL *s ) SSL的数据结构,在ssl.h头文件中,有个结构体定义struct ssl_st;然后找的s3对应的结构体;之后再找到rrec所对应的结构体。
typedef struct ssl3_record_st //主要这个结构体 SSL/TLS通信基础构建块
{
int type; /* type of record */
unsigned int length; /* How many bytes available */ //记录了实际数据的长度
unsigned int off; /* read/write offset into 'buf' */
unsigned char *data; /* pointer to the record data */ //指向心跳数据包
unsigned char *input; /* where the decode bytes are */
unsigned char *comp; /* only used with decompression - malloc()ed */
unsigned long epoch; /* epoch number, needed by DTLS1 */
unsigned char seq_num[8]; /* sequence number, needed by DTLS1 */
} SSL3_RECORD;
理解了数据结构,可以进一步来描述代码,找出代码漏洞。
unsigned char *p = &s->s3->rrec.data[0], *pl;
unsigned short hbtype;// 2 type reponse request
unsigned int payload;
unsigned int padding = 16; /* Use minimum padding */
首先函数先定义了四个变量,其中hbtype指向了心跳包的类型,在这里是请求包类型;变量payload记录书包的有效长度,这个值由客户端控制;*p指向心跳记录。
/* Read type and payload length first */
hbtype = *p++;// *p 的内容给hbtype之后再后移一个字节
n2s(p, payload);//gain para length and p move 2
pl = p;//point real data 这里指向了具体的数据
if (hbtype == TLS1_HB_REQUEST)// if it is true , construct a response para
{
unsigned char *buffer, *bp;//
int r;
buffer = OPENSSL_malloc(1 + 2 + payload + padding);//payload is defined by attacker
bp = buffer;
/* Enter response type, length and copy payload */
*bp++ = TLS1_HB_RESPONSE;
s2n(payload, bp);
memcpy(bp, pl, payload);//pl point to request's data if pl'data 'length < payload length
bp += payload;
漏洞就是发生在下面两条语句没有满足RFC6520文件中描述的条:
n2s(p, payload);//gain para length and p move 2
pl = p;//point real data 这里指向了具体的数据
分析可知,如果客户端发来的请求包的实际数据为空,或者载荷的有效长度值设置很大。在执行下面语句时构造的响应包,会导致响应包的数据长度大于请求包的数据长度,而有要拷贝payload长度的数据,这样就导致内存泄露。内容数据就被这样窃取。
buffer = OPENSSL_malloc(1 + 2 + payload + padding);
bp = buffer;
*bp++ = TLS1_HB_RESPONSE;
s2n(payload, bp);
memcpy(bp, pl, payload);
bp += payload;
在OpenSSL1.0.1g版本中代码已经实现了修补,增加了两个判断条件。
5、实现一个网站支持SSL协议(生成一个伪证书)
|