网络安全实验室安全学习程序员

heartbleed漏洞及内存信息窃取

2018-01-29  本文已影响46人  折戟尘风

heartbleed漏洞及内存信息窃取


了解漏洞

Heartbleed漏洞是什么?

Heartbleed漏洞是openssl加密软件库中的一个严重的漏洞。这个漏洞允许在正常情况下通过用于保护因特网的ssl/tls加密来窃取受保护的信息。只要使用的是存在缺陷的OpenSSL实例,无论是服务器还是客户端,都可能因此而受到攻击。
此问题的原因是在实现TLS的心跳扩展时没有对输入进行适当验证(缺少边界检查),因此漏洞的名称来源于“心跳”(heartbeat)。该程序错误属于缓冲区过读,即可以读取的数据比应该允许读取的还多。
这个漏洞在通用漏洞披露(CVE)系统中的编号为CVE-2014-0160。

它的背景和影响是什么?

背景:
OpenSSL于2014年4月7日被公开披露据。信在漏洞披露时,约有17%(大约五十万)通过认证机构认证的互联网安全网络服务器容易受到攻击,导致服务器私钥和用户会话cookie及密码被盗。电子前哨基金会、Ars Technica和布鲁斯·施奈尔均认为心脏出血漏洞是“灾难性的”。福布斯网络安全专栏作家约瑟夫·斯坦伯格写道:“有些人认为,至少就其潜在影响而言,‘心脏出血’是自互联网允许商用以来所发现的最严重的漏洞。”
影响:
经由heartbleed漏洞发动攻击,获得的数据可能包括TLS双方将要交换、但尚未加密的机密内容,包括在用户请求中各种格式的post数据。此外,泄漏的数据还可能含有身份验证密令,如会话cookie及密码,可使攻击者向该服务冒充此用户。
攻击还可能泄漏受攻击双方的私钥,这将使攻击者能解密通信内容(将来或是之前通过被动窃听捕获而存储的通信,除非使用完全正向保密,而在这种情况下,只能解密将来通过中间人攻击截获的通信)。
即使漏洞修复之后,获得受害者认证资料的攻击者仍能伪装成资料的拥有者,只要该资料能被接受(例如,在更改密码或撤销私钥之前)。因此,漏洞对保密性构成了致命威胁。然而,冒充受害人的攻击者也能修改数据,所以间接的后果可能不只是系统机密泄漏那么简单。
一项于2014年4月对美国成人进行的调查显示,60%的人听说过心脏出血漏洞。使用互联网的受访者中有39%的人采取了行动以保护他们的在线账户,如更改密码或注销账户;29%的人认为他们的个人信息因漏洞而处于危险之中;6%的人认为他们的个人信息已遭窃取。

分析漏洞原理

源码中那些地方出现了问题?

我们直接看一下修复后的提交的代码和之前代码的区别:

--- a/ssl/d1_both.c
+++ b/ssl/d1_both.c
@@ -1459,26 +1459,36 @@ dtls1_process_heartbeat(SSL *s)
        unsigned int payload;
        unsigned int padding = 16; /* Use minimum padding */
-       /* Read type and payload length first */
-       hbtype = *p++;
-       n2s(p, payload);
-       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);
+       /* 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;
+
        if (hbtype == TLS1_HB_REQUEST)
                {
                unsigned char *buffer, *bp;
+               unsigned int write_length = 1 /* heartbeat type */ +
+                                           2 /* heartbeat length */ +
+                                           payload + padding;
                int r;
 
+               if (write_length > SSL3_RT_MAX_PLAIN_LENGTH)
+                       return 0;
+
                /* 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);
+               buffer = OPENSSL_malloc(write_length);
                bp = buffer;
 
                /* Enter response type, length and copy payload */
@@ -1489,11 +1499,11 @@ dtls1_process_heartbeat(SSL *s)
                /* Random padding */
                RAND_pseudo_bytes(bp, padding);
 
-               r = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
+               r = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, write_length);
 
                if (r >= 0 && s->msg_callback)
                        s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
-                               buffer, 3 + payload + padding,
+                               buffer, write_length,
                                s, s->msg_callback_arg);
 
                OPENSSL_free(buffer);

从上面的差异我们可以看到,服务器处理心跳原来的方式是首先直接解析type和payload,什么都不做任何的检查。

unsigned int payload;
        unsigned int padding = 16; 
        hbtype = *p++;
        n2s(p, payload);
        pl = p;

修改之后openssl的data进行了16字节的数据对齐,其他格式一致。
修补方式是他加入了两个重要的函数:

  if (1 + 2 + 16 > s->s3->rrec.length)
              return 0; /* silently discard */

if (1 + 2 + payload + 16 > s->s3->rrec.length)
              return 0; /* silently discard per RFC 6520 sec. 4 */

这个判断的目的是为了避免data的length为0这一特殊情况的处理。

从poc中哪里可以体现出漏洞,poc又是怎样利用的漏洞?

从python的poc脚本中最重要的一部分代码可以看到:

hb = h2bin('''
18 03 02 00 03
01 40 00
''')

其中后四位 40 00 是我们请求的数据长度。如果将这个改为其他的数据。比如ff ff就可以请求一个超过我们真是payload长度的数据了。
通过更改请求数据的长度然后就可以利用漏洞读取缓存找中的数据了。

漏洞的复现和利用

安装配置openssl

绝对不能直接卸载原来的openssl。因为有很多依赖,我们直接下载openssl 1.0.1e版本的压缩包,然后解压安装。

$ tar -zxvf   openssl-1.0.1e.tar.gz
$ cd  /openssl-1.0.1e/  //编译安装之前需要执行下面这个否则会报错
$ rm –f /usr/bin/pod2man
$ ./config --prefix=/usr/local/openssl shared -fPIC no-gost
$ //上面的命令,是安装openssl到  /usr/local/openssl 目录,安装之后,编译;
$ make && make install

修改配置文件:
在/etc/ld.so.conf文件的最后面,添加如下内容:

/usr/local/openssl/lib

然后执行:

$ /usr/local/openssl/lib

添加OPESSL的环境变量:
在etc/的profile的最后一行,添加:

export OPENSSL=/usr/local/openssl/bin
export PATH=$OPENSSL:$PATH:$HOME/bin

退出命令界面,再从新登录。(注销一下)

$ cp lib*.so* /usr/lib64          //把这四个文件复制到lib64位置 

依次如下执行:

$ ldd /usr/local/openssl/bin/openssl

查看路径

$ which openssl

查看版本:

$ openssl version
1.png

配置Apache+https

不用卸载原来的apache,直接下线安装2.2版本就行,这样就不用装apr这些依赖,原版本已经存在。
下载安装apache2.2.34:

$ tar –zxvf apache-2.2.34.tar.gz
$ ./configure --prefix=/usr/local/httpd --enable-so --enable-rewrite --enable-ssl --with-ssl=/usr/local/openssl
$ Make && make install

如果编译时出现错误:sort libssl.so.10之类的错误可以直接忽略!
因为这个只是版本的问题,不用理会!我尝试了很多都不能解决这个问题。重装了好几次,最后发现是fedora版本的问题,后面直接忽略了这个报错发现最结果没有影响。
在目录/usr/local/httpd/conf下生成证书:(直接在conf目录下生成证书就不用拷贝了。)

$ openssl genrsa -out server.key 2048   //生成2048位的加密私钥
$ openssl req -new -key server.key -out server.csr   //生成证书签名请求(CSR),这里需要填写许多信息,如国家,省市,公司等
$ openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt    //最后,生成类型为X509的自签名证书。有效期设置3650天,即有效期为10年
2.png
3.png

然后修改httpd.conf的配置文件
将ServerNname的注释取消,改为127.0.0.1:8080
将httpd-ssl的那一行的注释也取消。


4.png
然后重启apache
$ /usr/local/httpd/bin/apachectl  restart

访问指定页面就可以使用https了:


5.png

下载编译安装php5.3.30

$ ./configure --prefix=/usr/local/php   --with-apxs2=/usr/local/httpd/bin/apxs  --with-config-file-path=/usr/local/php/etc   --enable-fpm --enable-mbstring --enable-gd --enable-xml    --with-mysqli=mysqlnd

然后编辑一下:

$ cp php.ini-development  /usr/local/php/etc/php.ini

修改Apache的配置文件httpd.conf

DirectoryIndex index.html index.php #添加index.php
找到:
AddType  application/x-compress .Z
AddType application/x-gzip .gz .tgz
添加如下内容
AddType application/x-httpd-php-source .phps
AddType application/x-httpd-php .php

检查LoadModule php5_module modules/libphp5.so 是否已经添加 libphp5.so文件是否存在。存在就可以,写一个简单的php查看:

6.png

可以用php进行访问。

利用poc进行攻击

从其他主机访问:


7.png
8.png

可以看到我们刚刚生成的私人证书,在其他主机访问则不安全。
下面尝试使用namp自带的脚本进行检测,进行poc攻击:

$ nmap -sV -p 443 --script=ssl-heartbleed 127.0.0.1
9.png

可以看到成功扫出来了漏洞:ssl-heartbleed,危险程度是高。
接着尝试使用网上的脚本进行测试:


10.png
11.png

编译安装配置https+php+mysql环境

下载php5.6.17并编译安装:

$ ./configure --prefix=/usr/local/php  

通过修改心跳包数据、抓包等方式进一步理解heartbleed的漏洞原理;

通过在攻击机执行网上的poc利用 脚本,然后用wireshark抓包观察:
首先攻击机先测试能否访问靶机:


12.png

然后打开wireshark监听网卡,设置过滤指定地址和端口:


13.png
攻击机执行攻击(没有更改长度)脚本:
14.png
抓取到心跳包请求包和响应包,我们点击请求包查看:
15.png

这是正常的请求包,主要这里的payload长度。
下面看看请求包的详细情况:


16.png
后面这16位:
18表示contenet-type:heartbleed(24)
03 02表示 版本
03 表示长度
01 表示是请求
20 00就是payload就是我们要读取的数据的长度。
接着我们进行更改payload的长度进行测试:
17.png
将代码后上面后四位改为ff ff,执行,抓包查看:
18.png

可以看到此时的请求包的payload已经变为了65535。


19.png
因为这里的请求长度就是ff ff。
20.png
同样的响应包的也变成了65535变成了我们更改的64k的数据了,这样我们就成功每次可以读取到64KB数据。
接着我们建立一个简单的php+apache+mysql的网站,然后进行测试。
最新版mysql5.7以上使用SET PASSWORD = '<plaintext_password>’更改密码!!
设置可以使用弱密码set global validate_password_policy=0;
我直接将我数据库的大作业考进知道文件夹然后进行测试:
在攻击机访问我的数据管理系统:
22.png
成功窃取了内存中的数据!!!!

heartbleed漏洞的风险是什么。

如果一个服务器存在heartbleed的漏洞很可能会被攻击者窃取内存中的数据,虽然每次是以最大64kb的数据被窃取,但是很有可能被攻击者窃取了秘钥等重要的信息来进行更危险的攻击。

使用openvas扫描

23.png

可以看到成功扫描出来heartbleed漏洞。

上一篇下一篇

猜你喜欢

热点阅读