搭建環境,訪問 8080 端口
漏洞說明:
Nginx: Nginx是一款輕量級的Web 服務器/反向代理服務器及電子郵件(IMAP/POP3)代理服務器,在BSD-like 協議下發行。其特點是占有內存少,并發能力強,事實上nginx的并發能力在同類型的網頁服務器中表現較好,中國大陸使用nginx網站用戶有:百度、京東、新浪、網易、騰訊、淘寶等。
影響版本:0.8.41 ~ 1.4.3 / 1.5.0 ~ 1.5.7
原理:
此漏洞可導致目錄跨越及代碼執行, 其主要原因是錯誤地解析了請求的URI,
錯誤地獲取到用戶請求的文件名,導致出現權限繞過、代碼執行的連帶影響。
舉個例子,比如,Nginx匹配到.php結尾的請求,就發送給fastcgi進行解析,常見的寫法下:
location ~ \.php$ {include fastcgi_params;fastcgi_pass 127.0.0.1:9000;fastcgi_index index.php;fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name;fastcgi_param DOCUMENT_ROOT /var/www/html;
}
正常情況下(關閉pathinfo的情況下),只有.php后綴的文件才會被發送給fastcgi解析。而存在
CVE-2013-4547的情況下,我們請求 1.gif[0x20][0x00].php ,這個URI可以匹配上正則 .php$,
可以進入這個Location塊;但進入后,由于fastcgi在查找文件時被\0截斷,Nginx卻錯誤地認為
請求的文件是1.gif[0x20],就設置其為SCRIPT_FILENAME的值發送給fastcgi。
fastcgi根據SCRIPT_FILENAME的值進行解析,最后造成了解析漏洞。
所以,我們只需要上傳一個空格結尾的文件,即可使PHP解析之。
再舉個例子,比如很多網站限制了允許訪問后臺的IP
location /admin/ { allow 127.0.0.1; deny all;}
我們可以請求如下URI:/test[0x20]/…/admin/index.php,這個URI不會匹配上location后面的/admin/,
也就繞過了其中的IP驗證;但最后請求的是 /test[0x20]/…/admin/index.php 文件,也就是/admin/index.php,
成功訪問到后臺。(這個前提是需要有一個目錄叫test:這是Linux系統的特點,如果有一個不存在的目錄,
則即使跳轉到上一層,也會爆文件不存在的錯誤,Windows下沒有這個限制)
當我們通過例如下列方式進行 URL 訪問限制的時候,如果攻擊者使用一些沒經過轉義的空格字符(無效的 HTTP 協議,但從 Nginx 0.8.41 開始因為考慮兼容性的問題予以支持)那么這個限制可能無效:
location /protected/ {deny all;}
當請求的是 “/foo /…/protected/file” 這樣的 URL (靜態文件,但 foo 后面有一個空格結尾) 或者是如下的配置:
location ~ \.php$ {fastcgi_pass ...}
當我們請求 “/file \0.php” 時就會繞過限制。
漏洞復現:
上傳一個php文件:由于環境對php文件有黑名單過濾
會顯示文件類型不支持
利用CVE-2013-4547 ,上傳一個jpg,注意后綴名帶空格
上傳成功,接下來構造00截斷來構造Nginx解析漏洞,將shell.jpg 解析成php文件,
訪問
http://【IP】:8080/uploadfiles/shell.jpg .php
結果是訪問不到
原因在于將空格編碼成了 %20 ,服務器沒有shell.jpg%20 這個文件
在16進制下將第二個空格改為00,即 “空格” -> “\0”
就會執行 命令
防護:
該問題已經在 Nginx 1.5.7 和 1.4.4 版本中修復。
補丁程序在:
http://nginx.org/download/patch.2013.space.txt
--- src/http/ngx_http_parse.c
+++ src/http/ngx_http_parse.c
@@ -617,6 +617,7 @@ ngx_http_parse_request_line(ngx_http_reqdefault:r->space_in_uri = 1;state = sw_check_uri;
+ p--;break;}break;
@@ -670,6 +671,7 @@ ngx_http_parse_request_line(ngx_http_reqdefault:r->space_in_uri = 1;state = sw_uri;
+ p--;break;}break;
配置上臨時的解決辦法是:
if ($request_uri ~ " ") {return 444;}