nginx空字节允许执行任意代码漏洞

2011-8-28 王健宇 转载

PHP,nginx配置和潜在执行任意代码,我以前的博客文章发布后,我遇到了一个单独的空字节注入漏洞nginx的旧版本(0.5.*,0.6.*,0.7,0.8<=0.7.65<=0.8.37)。通过利用此漏洞,攻击者可以导致服务器使用PHP的FastCGI作为PHP的服务器上执行任何公开访问的文件。

在nginx的含漏洞的版本中,空字节URI中默认允许的(他们的存在是通过命名zero_in_uri在ngx_http_request.h定义一个变量)。单个模块有能力选择退出处理空字节的URI。然而,并非所有这些,特别不FastCGI模块。

这次攻击事件本身很简单:恶意用户发出请求http://9170.org/file.ext%00.php的原因file.ext作为PHP解析。如果一个攻击者可以控制文件的内容(即:使用头像上传形式)基结果是执行任意代码。Ngnix在遇到%00空字节时与后端FastCGI处理不一致,导致可以在图片中嵌入PHP代码然后通过访问xxx.jpg%00.php来执行其中的代码。此漏洞不能得到缓解,像cgi.fix_pathinfo try_files或像PHP配置设置nginx配置设置:唯一的防御升级到较新版本的nginx的或明确地阻止潜在的恶意请求目录包含用户控制的内容。

# This location block will prevent an attacker from exploiting    
# this vulnerability using files in the 'uploads' or 'other_uploads' directory    
location ~ ^/(uploads|other_uploads)/.*.php$    
{    
    deny all;    
}   

虽然nginx的受影响版本比较老(0.7.66发布,2010年6月7日,0.8.38发布了2010年5月24日日),发行说明中没有提到出现的变化。因此,管理员可能没有意识到他们的服务器存在漏洞风险。我发现链接中,包含漏洞的包被分布:
Ubuntu Lucid Lynx (Ubuntu's current LTS offering) and Hardy Heron (via both the hardy and hardy-backports repositories) 通过apt - get提供含漏洞的nginx的版本,lucid和hardy包已经更新。hardy-backports正在等待批复。。Fedora提供了EPEL- 4库的易受攻击的版本。这时,一个更新包还尚未发布。
我发几个电子邮件[email protected]方面的漏洞。我6月24日发出第一个和我于7月4日,7月20日和8月2日,followups。我收到了我8月2日的电子邮件答复如下:

Thank you for report.

I do not consider this as nginx security issue since every application
should validate its input data, so nginx passed the data to application.
Also this is PHP installation issue where scripts and user uploaded
data are not separated. This issue was discussed several times on mailing
list.

At some point I've decided that zero byte in URI should not appear
in any encoding, operating system, etc., and just makes more problems
than helps. So I have remove zero byte test.
For anyone who's curious, the changes can be found at r3528 from svn://svn.nginx.org. At that time, it appears trunk corresponded to nginx 0.8: r3599 merged r3528 into the nginx 0.7 branch. The corresponding commit message is "remove r->zero_in_uri." I've reproduced the output of svn diff below:

Index: src/http/ngx_http_request.h    
===================================================================    
--- src/http/ngx_http_request.h (revision 3527)    
+++ src/http/ngx_http_request.h (revision 3528)    
@@ -56,7 +56,7 @@ 
 #define NGX_HTTP_PARSE_INVALID_HEADER      13    
     
-#define NGX_HTTP_ZERO_IN_URI               1    
+/* unused                                  1 */ 
 #define NGX_HTTP_SUBREQUEST_IN_MEMORY      2   
 #define NGX_HTTP_SUBREQUEST_WAITED         4   
 #define NGX_HTTP_LOG_UNSAFE                8    
@@ -435,9 +435,6 @@    
     /* URI with "+" */   
     unsigned                          plus_in_uri:1;    
     
-    /* URI with "\0" or "%00" */   
-    unsigned                          zero_in_uri:1;    
-    
     unsigned                          invalid_header:1;    
     
     unsigned                          valid_location:1;    
Index: src/http/ngx_http_core_module.c    
===================================================================    
--- src/http/ngx_http_core_module.c (revision 3527)    
+++ src/http/ngx_http_core_module.c (revision 3528)    
@@ -1341,7 +1341,7 @@    
     
     /* no content handler was found */   
     
-    if (r->uri.data[r->uri.len - 1] == '/' && !r->zero_in_uri) {    
+    if (r->uri.data[r->uri.len - 1] == '/') {    
     
         if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) {    
             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,    
@@ -2104,7 +2104,6 @@    
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,    
                    "http subrequest \"%V?%V\"", uri, &sr->args);    
     
-    sr->zero_in_uri = (flags & NGX_HTTP_ZERO_IN_URI) != 0;    
     sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0;    
     sr->waited = (flags & NGX_HTTP_SUBREQUEST_WAITED) != 0;    
     
Index: src/http/ngx_http_special_response.c    
===================================================================    
--- src/http/ngx_http_special_response.c    (revision 3527)    
+++ src/http/ngx_http_special_response.c    (revision 3528)    
@@ -517,8 +517,6 @@    
     
     r->err_status = overwrite;    
     
-    r->zero_in_uri = 0;    
-    
     if (ngx_http_complex_value(r, &err_page->value, &uri) != NGX_OK) {    
         return NGX_ERROR;    
     }    
Index: src/http/ngx_http_upstream.c    
===================================================================    
--- src/http/ngx_http_upstream.c    (revision 3527)    
+++ src/http/ngx_http_upstream.c    (revision 3528)    
@@ -1815,10 +1815,6 @@    
             return NGX_DONE;    
         }    
     
-        if (flags & NGX_HTTP_ZERO_IN_URI) {    
-            r->zero_in_uri = 1;    
-        }    
-    
         if (r->method != NGX_HTTP_HEAD) {    
             r->method = NGX_HTTP_GET;    
         }    
Index: src/http/ngx_http_parse.c    
===================================================================    
--- src/http/ngx_http_parse.c   (revision 3527)    
+++ src/http/ngx_http_parse.c   (revision 3528)    
@@ -438,8 +438,7 @@    
                 r->plus_in_uri = 1;    
                 break;    
             case '\0':    
-                r->zero_in_uri = 1;    
-                break;    
+                return NGX_HTTP_PARSE_INVALID_REQUEST;    
             default:    
                 state = sw_check_uri;    
                 break;    
@@ -496,8 +495,7 @@    
                 r->plus_in_uri = 1;    
                 break;    
             case '\0':    
-                r->zero_in_uri = 1;    
-                break;    
+                return NGX_HTTP_PARSE_INVALID_REQUEST;    
             }    
             break;    
     
@@ -526,8 +524,7 @@    
                 r->complex_uri = 1;    
                 break;    
             case '\0':    
-                r->zero_in_uri = 1;    
-                break;    
+                return NGX_HTTP_PARSE_INVALID_REQUEST;    
             }    
             break;    
     
@@ -1202,7 +1199,7 @@    
                     ch = *p++;    
     
                 } else if (ch == '\0') {    
-                    r->zero_in_uri = 1;    
+                    return NGX_HTTP_PARSE_INVALID_REQUEST;    
                 }    
     
                 state = quoted_state;    
@@ -1304,8 +1301,7 @@    
         }    
     
         if (ch == '\0') {    
-            *flags |= NGX_HTTP_ZERO_IN_URI;    
-            continue;    
+            goto unsafe;    
         }    
     
         if (ngx_path_separator(ch) && len > 2) {    
@@ -1449,34 +1445,19 @@    
 void   
 ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args)    
 {    
-    u_char  ch, *p, *last;    
+    u_char  *p, *last;    
     
-    p = uri->data;    
+    last = uri->data + uri->len;    
     
-    last = p + uri->len;    
+    p = ngx_strlchr(uri->data, last, '?');    
     
-    args->len = 0;    
+    if (p) {    
+        uri->len = p - uri->data;    
+        p++;    
+        args->len = last - p;    
+        args->data = p;    
     
-    while (p < last) {    
-    
-        ch = *p++;    
-    
-        if (ch == '?') {    
-            args->len = last - p;    
-            args->data = p;    
-    
-            uri->len = p - 1 - uri->data;    
-    
-            if (ngx_strlchr(p, last, '\0') != NULL) {    
-                r->zero_in_uri = 1;    
-            }    
-    
-            return;    
-        }    
-    
-        if (ch == '\0') {    
-            r->zero_in_uri = 1;    
-            continue;    
-        }    
+    } else {    
+        args->len = 0;    
     }    
 }    
Index: src/http/modules/ngx_http_gzip_static_module.c    
===================================================================    
--- src/http/modules/ngx_http_gzip_static_module.c  (revision 3527)    
+++ src/http/modules/ngx_http_gzip_static_module.c  (revision 3528)    
@@ -89,10 +89,6 @@    
         return NGX_DECLINED;    
     }    
     
-    if (r->zero_in_uri) {    
-        return NGX_DECLINED;    
-    }    
-    
     gzcf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_static_module);    
     
     if (!gzcf->enable) {    
Index: src/http/modules/ngx_http_index_module.c    
===================================================================    
--- src/http/modules/ngx_http_index_module.c    (revision 3527)    
+++ src/http/modules/ngx_http_index_module.c    (revision 3528)    
@@ -116,10 +116,6 @@    
         return NGX_DECLINED;    
     }    
     
-    if (r->zero_in_uri) {    
-        return NGX_DECLINED;    
-    }    
-    
     ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);    
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);    
     
Index: src/http/modules/ngx_http_random_index_module.c    
===================================================================    
--- src/http/modules/ngx_http_random_index_module.c (revision 3527)    
+++ src/http/modules/ngx_http_random_index_module.c (revision 3528)    
@@ -86,10 +86,6 @@    
         return NGX_DECLINED;    
     }    
     
-    if (r->zero_in_uri) {    
-        return NGX_DECLINED;    
-    }    
-    
     if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {    
         return NGX_DECLINED;    
     }    
Index: src/http/modules/ngx_http_dav_module.c    
===================================================================    
--- src/http/modules/ngx_http_dav_module.c  (revision 3527)    
+++ src/http/modules/ngx_http_dav_module.c  (revision 3528)    
@@ -146,10 +146,6 @@    
     ngx_int_t                 rc;    
     ngx_http_dav_loc_conf_t  *dlcf;    
     
-    if (r->zero_in_uri) {    
-        return NGX_DECLINED;    
-    }    
-    
     dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);    
     
     if (!(r->method & dlcf->methods)) {    
Index: src/http/modules/ngx_http_flv_module.c    
===================================================================    
--- src/http/modules/ngx_http_flv_module.c  (revision 3527)    
+++ src/http/modules/ngx_http_flv_module.c  (revision 3528)    
@@ -80,10 +80,6 @@    
         return NGX_DECLINED;    
     }    
     
-    if (r->zero_in_uri) {    
-        return NGX_DECLINED;    
-    }    
-    
     rc = ngx_http_discard_request_body(r);    
     
     if (rc != NGX_OK) {    
Index: src/http/modules/ngx_http_static_module.c    
===================================================================    
--- src/http/modules/ngx_http_static_module.c   (revision 3527)    
+++ src/http/modules/ngx_http_static_module.c   (revision 3528)    
@@ -66,10 +66,6 @@    
         return NGX_DECLINED;    
     }    
     
-    if (r->zero_in_uri) {    
-        return NGX_DECLINED;    
-    }    
-    
     log = r->connection->log;    
     
     /*    
Index: src/http/modules/ngx_http_autoindex_module.c    
===================================================================    
--- src/http/modules/ngx_http_autoindex_module.c    (revision 3527)    
+++ src/http/modules/ngx_http_autoindex_module.c    (revision 3528)    
@@ -160,10 +160,6 @@    
         return NGX_DECLINED;    
     }    
     
-    if (r->zero_in_uri) {    
-        return NGX_DECLINED;    
-    }    
-    
     if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {    
         return NGX_DECLINED;    
     }    
Index: src/http/modules/perl/ngx_http_perl_module.c    
===================================================================    
--- src/http/modules/perl/ngx_http_perl_module.c    (revision 3527)    
+++ src/http/modules/perl/ngx_http_perl_module.c    (revision 3528)    
@@ -168,10 +168,6 @@    
 static ngx_int_t    
 ngx_http_perl_handler(ngx_http_request_t *r)    
 {    
-    if (r->zero_in_uri) {    
-        return NGX_HTTP_NOT_FOUND;    
-    }    
-    
     r->main->count++;    
     
     ngx_http_perl_handle_request(r);   

标签: nginx漏洞

发表评论:

Powered by emlog sitemap