近日,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协议处理的漏洞。未然实验室安全研究人员针对此漏洞的技术细节和利用方法进行了深入的研究,欢迎安全爱好者们一起分享和讨论。
0x01补丁分析
在redhat的bugzilla上可以找到该漏洞:
https://bugzilla.redhat.com/show_bug.cgi?id=1463199
在github上有对该漏洞的修复提交:
https://github.com/apache/httpd/commit/672187c168b94b562d8065e08e2cad5b00cdd0e3
修改前后的代码差异比较如下:
可以看到,修复内容很简单,就是增加了对h2_request_rcreate函数返回值的判断。官方推荐升级到2.4.26修复漏洞。
0x02漏洞成因
从https://archive.apache.org/dist/httpd/httpd-2.4.25.tar.gz 下载到有漏洞的服务器代码后,通过补丁的修改进行漏洞成因的逆向分析。
首先查看漏洞函数 h2_stream_set_request_rec,发现是调用h2_request_rcreat创建http 2.0请求的数据结构req,h2_request_rcreat执行失败时req为空,此时在日志函数ap_log_rerror中直接解引用req导致进程崩溃:
继续查看函数h2_request_rcreate,看到首先会把req置为0,然后判断4个变量r->method**,scheme,r->hostname,path**,任何一个为空则返回失败,而此时req还是0,就会导致进程崩溃:
那么这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 函数中做了判断:
这里的判断逻辑,如果满足下面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为空,从而触发漏洞。**
0x03漏洞验证及漏洞利用
综合上面的分析,该漏洞利用成功需要如下条件:
1) 服务器支持HTTP/2
2) 请求是HTTP/1.0版本
3) 请求中没有Host头
服务器配置
在server上要配置开启HTTP/2功能,使用apache默认的站点配置,在配置文件中首先加载mod_http2.so:
然后加入下面配置,重新启动apache httpd就可以了:
验证POC
验证时,我们首先起一个单一进程的apache httpd服务,方便验证进程崩溃后的效果:
正常访问,返回欢迎页面:
将构造的POC通过burpsuite发送:
果然超时没有响应,同时在服务器上发现httpd进程已经崩溃:
再次访问服务器时,页面已经无法访问了:
漏洞利用
在上一节,我们已经验证了在单一进程模式下,如何通过该漏洞导致apache服务器异常终止。但是通常情况下apache服务器在启动时,会同时启动多个工作进程:
而且当worker进程崩溃时,apache会自动启动新的worker进程。那么在真实的网络环境中,黑客会如何利用此漏洞对服务器进行攻击呢?
我们尝试编写了一个多线程(并发100个线程)的程序,同时发起多个畸形请求,以不断触发后台worker崩溃,并让apache服务器不断陷入重新分配worker的处理之中:
执行脚本时,发起的请求:
请求发起后,我们发现,并不需要特别的并发连接,便可以让服务器进入拒绝服务状态:
0x04 漏洞影响
在apache的漏洞公告中,只有2.4.25版本的httpd服务器受此漏洞影响。但是根据未然实验室的测试,从2.4.17开始的所有版本的httpd服务器,受到这段POC攻击时都会崩溃,而httpd也是从2.4.17版本开始支持HTTP 2.0协议的。因此可以说这个漏洞会影响httpd的所有支持HTTP 2.0的版本,未然实验室强烈建议用户更新到最新的2.4.26版本。
2.4.25之前的版本代码与2.4.25版本不尽相同,以2.4.17为例,在h2_request_rwrite函数中:
收到攻击POC后,r->hostname为空,因此req->authority也为空,而ap_strchr_c其实通过下面的宏进行定义的: