Nginx 1.3.9、1.4.0缓冲区溢出漏洞以及64位下的漏洞利用分析

前言知识:

CVE-2013-2028:nginx 在处理某些畸形的HTTP请求长度值时存在问题,攻击者利用此漏洞可能造成栈溢出从而执行任意代码,最低限度可造成拒绝服务攻击。

受影响的软件及系统:nginx 1.3.9-1.4.0

解决方法:NSFOCUS建议您升级到nginx 1.4.1或nginx 1.5.0。

简介:

在CVE-2013-2028发布的几天之后,Vnsecurity组织已经成功通过程序溢出对此漏洞进行了深度的利用。但是,为了让漏洞利用程序在真实的渗透环境中更具可靠性和有效性,Vnsecurity组织仍然尝试开发出不同的攻击载荷。自从针对Nginx 32-bit的漏洞利用程序出现在 Metasploit 后,Vnsecurity组织决定公开一些他们的作品。在本文,你会找到如何快速分析出Nginx的漏洞,进而通过使用Vnsecurity组织提供的溢出攻击载荷来对64 位的Linux漏洞进行有效的利用。

91ri.org:metasploit中已有Nginx 32-bit的漏洞利用程序,大家可以自行搜索并测试。:)

BUG详情:

根据nginx.org的补丁来看,攻击者利用此漏洞可能造成栈溢出从而执行任意代码,与Nginx 3个不同的组件有关。

1)     当用户发送一个HTTP请求头并且首部带有“Transfer-Encoding: chunked”时.,Nginx会对这些请求进行“请求长度”的估算,而对实现此估算功能利用的是src/http/ngx_http_parse.c:2011组件。

这是一段简单的代码,用来实现把“请求长度”从十六进制转换成十进制。因为 ctx->size 被定义为 无符号类型size_t,当这个变量的数值是无符号类型且最高位置1的时候,可被机器识别成为一个负数,后面我们会做出详细的说明。

 

2) Nginx处理静态文件组件

Nginx安装处理静态文件组件 (默认安装的情况下),当收到一个请求时, ngx_http_static_handler 在src/http/modules/ngx_http_static_module.c:49 会被调用执行。

然后ngx_http_static_handler 会在src/http/modules/ngx_http_static_module.c:211里访问 ngx_http_discard_request_body;

然后ngx_http_discard_request_body会在src/http/ngx_http_request_body.c:526里访问ngx_http_read_discarded_request_body;

 

一句话总结就是: ngx_http_static_handler——>ngx_http_static_handler——>ngx_http_discard_request_body——>ngx_http_read_discarded_request_body

ngx_http_read_discarded_request_body 是一个有趣的地方,在src/http/ngx_http_request_body.c:630里,我们可以发现一个定义了固定大小的缓冲区 , 如下文代码所示:

 

NGX_HTTP_DISCARD_BUFFER_SIZE在 src/http/ngx_http_request.h:19中 被定义成固定大小4096字节。

在下文的3)处,我们会演示 src/http/ngx_http_request_body.c:649 中的一个十分有趣的缓冲区溢出:

 

3)     当解析HTTP请求时的异常转换状态

我们回到src/http/ngx_http_request_body.c, 在访问调用 ngx_http_read_discarded_request_body之前, nginx会检查HTTP请求头里是否拥有 “chunked” 类型的字段, 然后运行src/http/ngx_http_request_body.c:680里定义的 ngx_http_discard_request_body_filter 。

ngx_http_discard_request_body_filter 会执行 ngx_http_parse_chunked ,这个就是我们在1)提到的代码啦. 完成之后, 返回值 “rc” 会被拿去跟一些常数作对比,以确定下一步要执行的流程. 他们其中一个判断十分有趣:

 

 

假定我们能把rb->chunked->length设置成一个非常大的整数如1)所提, 然后设rc = NGX_AGAIN 3)中所提, 然后便是见证奇迹的时刻啦:

– r->headers_in.content_length_n 变为负数 ( 因为被定义成无符号类型).

– 函数 ngx_http_discard_request_body_filter 返回一个值 ,然后程序转去执行ngx_http_read_discarded_request_body. 这包含了一个有漏洞的缓冲区。

– 最后 recv() 命令被欺骗,接收了多于4096个字节的数据,你们懂的,缓冲区溢出啦!.

这里有很多方法去设置chunked->length, 当 rb->chunked->length在函数ngx_http_parse_chunked( 根据 rb->chunked->size )的末尾被分配的时候,我们就有了直接控制权了。

 

 

为了使 rc = NGX_AGAIN,我们知道nginx在首次通信的时候,会接受请求低于1024字节的数据包。 因此,如果我们发送高于1024字节的数据包,ngx_http_parse_chunked 会返回一个 NGX_AGAIN 值,然后nginx 尝试重新接受一次。

 

缓冲区溢出漏洞的攻击载荷:

– 发送一个首部带有“transfer-encoding: chunked”的HTTP请求头

– 发送一个超过1024字节十六进制的整数,当函数第一次读取的时候。

– 发送4096字节的攻击载荷去溢出缓冲区, 当函数第二次读取的时候。

由于利用程序太长了,就不全部列出了。以下是针对64位机下的一个漏洞利用小片段:

 

 

通过监视其输出:

 

64位机下的漏洞利用程序开发:

这个构想可以成功在cookie里实现,通过利用一个大整数对cookie进行填充。如果导致进程崩溃的话,它会什么也没有返回。此时我们应该多次重复,直到收到输出为止。

下面会有一大堆乱七八糟的术语,请感兴趣的大黑阔们自行查阅。

以下贴出关键代码,要全部的利用代码请到https://github.com/danghvu/nginx-1.4.0/blob/master/exp-nginx.rb获取:

 

At w.w.w.w

 

此漏洞利用的缺点:

这里有几个可能造成你对exp无法作用的原因:

1)     Nginx使用non-blocking recv(),如果我们不能发送足够的数据去重写返回的地址/cookie,那么exp将会失败。这通常是因为普通的服务器会一次性载入多个不同用户的请求。

2)     到此,我们的分析报告是针对服务器默认的情况。如果当管理员修改了默认配置,情况将会变得不一样,甚至我们的exp将会失效。

3)     如果对远程服务器的系统位数不熟悉的话,盲目地攻击将会是十分困难的。当在32位系统的机子上,我们这个exp通常不会成功。

 

原文LINK:http://www.vnsecurity.net/2013/05/analysis-of-nginx-cve-2013-2028/

本文由网络安全实验室(www.91ri.org)原创翻译、修改、整理,转载请注明出处。