現在普遍的Nginx + PHP cgi的做法是在配置文件中, 通過正則匹配(Nginx(PHP/fastcgi)的PATH_INFO問題)設置SCRIPT_FILENAME, 今天小頓發現了一個這種方式的安全漏洞.
比如, 有http://www.laruence.com/fake.jpg, 那麼通過構造如下的URL, 就可以看到fake.jpg的二進制內容:
- http://www.laruence.com/fake.jpg/foo.php
爲什麼會這樣呢?
比如, 如下的nginx conf:
- location ~ \.php($|/) {
- fastcgi_pass 127.0.0.1:9000;
- fastcgi_index index.php;
- set $script $uri;
- set $path_info "";
- if ($uri ~ "^(.+\.php)(/.*)") {
- set $script $1;
- set $path_info $2;
- }
- include fastcgi_params;
- fastcgi_param SCRIPT_FILENAME $document_root$script;
- fastcgi_param SCRIPT_NAME $script;
- fastcgi_param PATH_INFO $path_info;
- }
通過正則匹配以後, SCRIPT_NAME會被設置爲”fake.jpg/foo.php”, 繼而構造成SCRIPT_FILENAME傳遞個PHP CGI, 但是PHP又爲什麼會接受這樣的參數, 並且把a.jpg解析呢?
這就要說到PHP的cgi SAPI中的參數, fix_pathinfo了:
- ; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI. PHP's
- ; previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok
- ; what PATH_INFO is. For more information on PATH_INFO, see the cgi specs. Setting
- ; this to 1 will cause PHP CGI to fix it's paths to conform to the spec. A setting
- ; of zero causes PHP to behave as before. Default is 1. You should fix your scripts
- ; to use SCRIPT_FILENAME rather than PATH_TRANSLATED.
- cgi.fix_pathinfo=1
如果開啓了這個選項, 那麼就會觸發在PHP中的如下邏輯:
- /*
- * if the file doesn't exist, try to extract PATH_INFO out
- * of it by stat'ing back through the '/'
- * this fixes url's like /info.php/test
- */
- if (script_path_translated &&
- (script_path_translated_len = strlen(script_path_translated)) > 0 &&
- (script_path_translated[script_path_translated_len-1] == '/' ||
- ....//以下省略.
到這裏, PHP會認爲SCRIPT_FILENAME是fake.jpg, 而foo.php是PATH_INFO, 然後PHP就把fake.jpg當作一個PHP文件來解釋執行… So…
這個隱患的危害用小頓的話來說, 是巨大的.
對於一些論壇來說, 如果上傳一個圖片(實際上是惡意的PHP腳本), 繼而構造這樣的訪問請求…
所以, 大家如果有用這種服務器搭配的, 請排查, 如果有隱患, 請關閉fix_pathinfo(默認是開啓的).
詳細漏洞信息, 請移步小頓的BLOG: 80Sec
另: 我認爲這個和Nginx沒啥關係, 不屬於Nginx的漏洞. 是配置的問題, 現在到處都在說是Nginx的Bug, 不妥不妥.