CVE-2014-0160 Heartbleed Vul Analysis && OpenSSL Cryptographic Software Library Bug

目录

1. Heartbleed漏洞简介
2. 漏洞造成的风险和影响
3. 漏洞的测试、POC
4. OpenSSL漏洞源代码分析
5. 防御、修复方案
6. 从漏洞中得到的攻防思考

1. Heartbleed漏洞简介

从本质上说,这个漏洞的起因是一个操作系统基础软件库OPENSSL在实现TLS/DTLS heartbeat extension (RFC6520) 时存在代码bug,导致越权信息泄漏

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

1. web
2. email
3. instant messaging (IM)
4. 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

1. secret keys used to identify the service providers and to encrypt the traffic
2. names and passwords of the users and the actual content.
3. allows attackers to eavesdrop on communications, steal data directly from the services and users and to impersonate services and users.

0x1: What makes the Heartbleed Bug unique?

Bugs in single software or library come and go and are fixed by new versions. However this bug has left large amount of private keys and other secrets exposed to the Internet. Considering the long exposure, ease of exploitation and attacks leaving no trace this exposure should be taken seriously.

这种敏感信息类型的漏洞造成的影响并不仅仅在于漏洞爆发后到修复这个期间互联网遭受到的黑客攻击,而更重要的是我们需要评估在这个期间应用系统遭受到的"不可逆的损失",这类安全漏洞也有很多,例如

1. drupal注入漏洞导致黑客在数据库中插入管理员帐号:
从爆发到最后官方给出修复方案的8个小时中,目标系统就遭受到了大量的攻击,大量数据库遭受到了污染

2. heartbleed漏洞造成的secret key、username/passwd泄漏

这种漏洞对应用系统造成的危害是长时间的,要做到彻底修复这个漏洞,需要进行密码大规模重置、密钥重置、脏数据回滚等操作

0x2: Is this a design flaw in SSL/TLS protocol specification?

要注意的是,Heartbleed不是一个协议设计漏洞,而是一个代码实现的bug导致的漏洞

0x3: What is being leaked?

当应用系统使用存在漏洞的openssl库的时候,就有可能造成一下的敏感信息外泄

1. primary key material
2. secondary key material 
3. protected content
4. collateral 

Relevant Link:

http://heartbleed.com/

2. 漏洞造成的风险和影响

0x1: What is leaked primary key material
These are the crown jewels, the encryption keys themselves. Leaked secret keys allow the attacker

1. to decrypt any past and future traffic to the protected services
2. to impersonate the service at will.
3. Any protection given by the encryption and the signatures in the X.509 certificates can be bypassed.

0x2: What is leaked secondary key material

These are for example the user credentials (user names and passwords) used in the vulnerable services. 

0x3: What is leaked protected content

This is the actual content handled by the vulnerable services. It may be personal or financial details, private communication such as emails or instant messages, documents or anything seen worth protecting by encryption. Only owners of the services will be able to estimate the likelihood what has been leaked and they should notify their users accordingly

0x4: What is leaked collateral

Leaked collateral are other details that have been exposed to the attacker in the leaked memory content. These may contain technical details such as memory addresses and security measures such as canaries used to protect against overflow attacks

0x5: How widespread is this

1. Apache
2. nginx. 
//Furthermore OpenSSL is used to protect for example 
3. email servers (SMTP, POP and IMAP protocols)
4. chat servers (XMPP protocol)
5. virtual private networks (SSL VPNs)
6. network appliances 
7. wide variety of client side software.
//Fortunately 
8. many large consumer sites are saved by their conservative choice of SSL/TLS termination equipment and software.

OpenSSL is very popular in client software and somewhat popular in networked appliances which have most inertia in getting updates.

0x6: 存在漏洞的OpenSSL版本

OpenSSL 1.0.2-beta 
OpenSSL 1.0.1 - OpenSSL 1.0.1f
openssl-1.0.1e

引用知乎上的一张图进行形象地描述

Relevant Link:

http://baike.baidu.com/view/12769298.htm
http://www.zhihu.com/question/23328658
http://www.infoq.com/cn/news/2014/04/openssl-heartbleed
https://www.trustasia.com/about/news/openssl-heartbleed.html
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-0160

3. 漏洞的测试、POC

0x1: 本机测试是否存在漏洞的方法

这个漏洞的本质是代码级的漏洞,所以检测本机是否存在漏洞的最简单方式就是检查本机的openssl版本

openssl version

结果显示openssl 1.0.1e,为存在漏洞的openssl版本,因此判断本机存在heartbleed漏洞

0x2: 在线漏洞测试的网站

https://filippo.io/Heartbleed/
https://www.trustasia.com/tools/bleed-checker/

4. OpenSSL漏洞源代码分析

0x1: SSL协议格式分析

在开始从源码级别了解Heartbleed漏洞的原理之前,我们需要对SSL协议的格式有一个详细的了解,从黑客角度上来说,要发送这种攻击,需要借助"协议数据包篡改技术",通过构造"畸形"的SSL数据包向引入了存在漏洞的openssl代码库的web server发起请求,从而获取目标web server的TLS Stack上和当前数据报相邻的64KB的数据(之所以是64kb,也和SSL协议本身有关系,协议中可供黑客修改的这个字段的最大长度是3bytes)

关于SSL/TLS协议格式的相关知识请参阅另一篇文章

http://www.cnblogs.com/LittleHann/p/3733469.html

0x2: 漏洞细节分析

下载openssl-1.0.1f.tar.gz源代码

https://www.openssl.org/source/

openssl-1.0.1fssld1_both.c

int dtls1_process_heartbeat(SSL *s)
{
    /*
    So, first we get a pointer to the data within an SSLv3 record. That looks like this:
    typedef struct ssl3_record_st
    {
        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;
    unsigned int payload;
    unsigned int padding = 16; /* Use minimum padding */

    /* Read type and payload length first */
    /*
    The first byte of the SSLv3 record is the heartbeat type. 
    The macro n2s takes two bytes from p, and puts them in payload. This is actually the length of the payload.

    这里要重点注意,代码并没有对SSLv3记录数据的"实际长度"进行判断,而是选择"信任用户发送的数据包中的字段"
    */
    hbtype = *p++;
    n2s(p, payload);
    //The variable pl is then the resulting heartbeat data, supplied by the requester.
    pl = p;

    if (s->msg_callback)
        s->msg_callback(0, s->version, TLS1_RT_HEARTBEAT,
            &s->s3->rrec.data[0], s->s3->rrec.length,
            s, s->msg_callback_arg);

    if (hbtype == TLS1_HB_REQUEST)
        {
        unsigned char *buffer, *bp;
        int r;

        /* Allocate memory for the response, size is 1 byte
         * message type, plus 2 bytes payload length, plus
         * payload, plus padding
         */
        buffer = OPENSSL_malloc(1 + 2 + payload + padding);
        /*
        So we're allocating as much memory as the requester asked for: up to 65535+1+2+16, to be precise. The variable bp is going to be the pointer used for accessing this memory. 
        */
        bp = buffer;

        /* Enter response type, length and copy payload */
        *bp++ = TLS1_HB_RESPONSE;
        s2n(payload, bp);
        memcpy(bp, pl, payload);
        /*
        The macro s2n does the inverse of n2s: it takes a 16-bit value and puts it into two bytes. So it puts the same payload length requested.
        Then it copies payload bytes from pl, the user supplied data, to the newly allocated bp array. After this, it sends this all back to the user
        */
        bp += payload;
        /* Random padding */
        RAND_pseudo_bytes(bp, padding);

        r = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);

        if (r >= 0 && s->msg_callback)
            s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
                buffer, 3 + payload + padding,
                s, s->msg_callback_arg);

        OPENSSL_free(buffer);

        if (r < 0)
            return r;
        }
    else if (hbtype == TLS1_HB_RESPONSE)
        {
        unsigned int seq;

        /* We only send sequence numbers (2 bytes unsigned int),
         * and 16 random bytes, so we just try to read the
         * sequence number */
        n2s(pl, seq);

        if (payload == 18 && seq == s->tlsext_hb_seq)
            {
            dtls1_stop_timer(s);
            s->tlsext_hb_seq++;
            s->tlsext_hb_pending = 0;
            }
        }

    return 0;
    }

对这段代码的逻辑进行一下梳理

1. 函数接收用户发送到服务端的SSLv3数据包,并对其中的字段进行解析
2. 代码无条件"信任"数据包头中的length字段,作为此次SSL数据包的总长度
3. 在从内存申请和填充响应数据包的时候,使用了"受污染"的长度字段
4. 从而导致"内存越界数据获取",将当前TLS Stack中的、和当前SSL Record指针相邻的、最大长度64KB的内存数据全部返回给了数据请求方

黑客只需要将原始正常发送的SSLv3数据包中的length字段改为0xFFFF,就可以非法获取目标web server的64kb泄漏数据
需要注意的是,虽然长度2字节理论上最大是64KB,但是RFC文档规定heartbeat最大长度不能超过2^14B,也就是16KB,出去type和payload_length、padding这三部分,所以最大数据会略小于16KB的,即16KB-19B

openssl-1.0.1essld1_pkt.c
int dtls1_write_bytes(SSL *s, int type, const void *buf, int len)

/* Call this to write data in records of type 'type'
 * It will return <= 0 if not all data has been sent or non-blocking IO.
 */
int dtls1_write_bytes(SSL *s, int type, const void *buf, int len)
{
    int i;

    /*
    openssl-1.0.1esslssl3.h
    Maximum plaintext length: defined by SSL/TLS standards  
    #define SSL3_RT_MAX_PLAIN_LENGTH        16384
    */
    OPENSSL_assert(len <= SSL3_RT_MAX_PLAIN_LENGTH);
    s->rwstate=SSL_NOTHING;
    i=do_dtls1_write(s, type, buf, len, 0);
    return i;
}

snort的入侵检测规则也是基于此建立的

alert tcp $EXTERNAL_NET any -> $HOME_NET 443 (msg:"openssl Heartbleed attack";flow:to_server,established; content:"|18 03|"; depth: 3; byte_test:2, >, 200, 3, big; byte_test:2, <, 16385, 3, big; threshold:type limit, track by_src, count 1, seconds 600; reference:cve,2014-0160; classtype:bad-unknown; sid:20140160; rev:2;)

Relevant Link:

http://blog.existentialize.com/diagnosis-of-the-openssl-heartbleed-bug.html
http://drops.wooyun.org/papers/1381

5. 防御、修复方案

0x1: 代码patch方案

The most important part of the fix was this:

/* Read type and payload length first */
if (1 + 2 + 16 > s->s3->rrec.length)
    return 0; /* silently discard */
hbtype = *p++;
n2s(p, payload);
if (1 + 2 + payload + 16 > s->s3->rrec.length)
    return 0; /* silently discard per RFC 6520 sec. 4 */
pl = p;

防御代码做了2件事

1. 检查zero-length heartbeats
2. 数据包的实际长度和数据包头中指示的长度是否一致

Relevant Link:

http://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=96db9023b881d7cd9f379b0c154650d6c108e9a3

0x2: 升级软件库版本方案

将openssl升级到OpenSSL 1.0.1g及其以上

6. 从漏洞中得到的攻防思考

1. 代码层面的安全
    1) 任何时候都不能信任用户发送的数据,所有的处理逻辑都必须放在服务端动态的完成
    2) any input from users is evil

2. 操作系统基础软件库的代码安全审计
    1) 使用基于JAVA这样的高级安全语言编写的基础软件库
    2) 定期对底层基础软件库进行单元测试和安全综合测试

Copyright (c) 2014 LittleHann All rights reserved

原文地址:https://www.cnblogs.com/LittleHann/p/4074787.html