作者:LoRexxar'@知道創宇404實驗室
時間:2019年10月25日
英文版本: http://www.jmbmsq.com/1064/
國外安全研究員 Andrew Danau在解決一道 CTF 題目時發現,向目標服務器 URL 發送 %0a 符號時,服務返回異常,疑似存在漏洞。
2019年10月23日,github公開漏洞相關的詳情以及exp。當nginx配置不當時,會導致php-fpm遠程任意代碼執行。
下面我們就來一點點看看漏洞的詳細分析,文章中漏洞分析部分感謝團隊小伙伴@Hcamael#知道創宇404實驗室
漏洞復現
為了能更方便的復現漏洞,這里我們采用vulhub來構建漏洞環境。
https://github.com/vulhub/vulhub/tree/master/php/CVE-2019-11043
git pull并docker-compose up -d
訪問http://{your_ip}:8080/

下載github上公開的exp(需要go環境)。
go get github.com/neex/phuip-fpizdam
然后編譯
go install github.com/neex/phuip-fpizdam
使用exp攻擊demo網站
phuip-fpizdam http://{your_ip}:8080/

攻擊成功
漏洞分析
在分析漏洞原理之前,我們這里可以直接跟入看修復的commit

從commit中我們可以很清晰的看出來漏洞成因應該是path_info的地址可控導致的,再結合漏洞發現者公開的漏洞信息中提到
The regexp in `fastcgi_split_path_info` directive can be broken using the newline character (in encoded form, %0a). Broken regexp leads to empty PATH_INFO, which triggers the bug.
也就是說,當path_info被%0a截斷時,path_info將被置為空,回到代碼中我就不難發現問題所在了。
其中env_path_info就是變量path_info的地址,path_info為0則plien為0.
slen變量來自于請求后url的長度
int ptlen = strlen(pt);
int slen = len - ptlen;
其中
int len = script_path_translated_len;
len為url路徑長度
當請求url為http://127.0.0.1/index.php/123%0atest.php
script_path_translated來自于nginx的配置,為/var/www/html/index.php/123\ntest.php
ptlen則為url路徑第一個斜杠之前的內容長度
當請求url為http://127.0.0.1/index.php/123%0atest.php
pt為/var/www/html/index.php
這兩個變量的差就是后面的路徑長度,由于路徑可控,則path_info可控。

由于path_info可控,在1222行我們就可以將指定地址的值置零,根據漏洞發現者的描述,通過將指定的地址的值置零,可以控制使_fcgi_data_seg結構體的char* pos置零。

其中script_name同樣來自于請求的配置

而為什么我們使_fcgi_data_seg結構體的char* pos置零,就會影響到FCGI_PUTENV的結果呢?
這里我們深入去看FCGI_PUTENV的定義.
char* fcgi_quick_putenv(fcgi_request *req, char* var, int var_len, unsigned int hash_value, char* val);
跟入函數fcgi_quick_putenv
https://github.com/php/php-src/blob/5d6e923d46a89fe9cd8fb6c3a6da675aa67197b4/main/fastcgi.c#L1703

函數直接操作request的env,而這個參數在前面被預定義。
https://github.com/php/php-src/blob/5d6e923d46a89fe9cd8fb6c3a6da675aa67197b4/main/fastcgi.c#L908

繼續跟進初始化函數fcgi_hash_init.
https://github.com/php/php-src/blob/5d6e923d46a89fe9cd8fb6c3a6da675aa67197b4/main/fastcgi.c#L254

也就是說request->env就是前面提到的fcgi_data_seg結構體,而這里的request->env是nginx在和fastcgi通信時儲存的全局變量。
部分全局變量會在nginx的配置中定義

其中變量會在堆上相應的位置儲存

回到利用過程中,這里我們通過控制path_info指向request->env來使request->env->pos置零。
繼續回到賦值函數fcgi_hash_set函數

緊接著進入fcgi_hash_strndup

這里h->data-》pos的最低位被置為0,且str可控,就相當于我們可以在前面寫入數據。
而問題就在于,我們怎么能向我們想要的位置寫數據呢?又怎么向我們指定的配置寫文件呢?
這里我們拿exp發送的利用數據包做例子
GET /index.php/PHP_VALUE%0Asession.auto_start=1;;;?QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ HTTP/1.1
Host: ubuntu.local:8080
User-Agent: Mozilla/5.0
D-Gisos: 8=====================================D
Ebut: mamku tvoyu
在數據包中,header中的最后兩部分就是為了完成這部分功能,其中D-Gisos負責位移,向指定的位置寫入數據。
而Ebut會轉化為HTTP_EBUT這個fastcgi_param中的其中一個全局變量,然后我們需要了解一下fastcgi中全局變量的獲取數據的方法。
https://github.com/php/php-src/blob/5d6e923d46a89fe9cd8fb6c3a6da675aa67197b4/main/fastcgi.c#L328
可以看到當fastcgi想要獲取全局變量時,會讀取指定位置的長度字符做對比,然后讀取一個字符串作為value.
也就是說,只要位置合理,var值相同,且長度相同,fastcgi就會讀取相對應的數據。
而HTTP_EBUT和PHP_VALUE恰好長度相同,我們可以從堆上數據的變化來印證這一點。
在覆蓋之前,該地址對應數據為

然后執行fcgi_quick_putenv

該地址對應數據變為

我們成功寫入了PHP_VALUE并控制其內容,這也就意味著我們可以控制PHP的任意全局變量。
當我們可以控制PHP的任意全局變量就有很多種攻擊方式,這里直接以EXP中使用到的攻擊方式來舉例子。

exp作者通過開啟自動包含,并設置包含目錄為/tmp,之后設置log地址為/tmp/a并將payload寫入log文件,通過auto_prepend_file自動包含/tmp/a文件構造后門文件。
漏洞修復
在經過對漏洞的深入研究后,我們推薦兩種方案修復這個漏洞。
- 臨時修復:
修改nginx相應的配置,并在php相關的配置中加入
try_files $uri =404
在這種情況下,會有nginx去檢查文件是否存在,當文件不存在時,請求都不會被傳遞到php-fpm。
-
正式修復:
- 將PHP 7.1.X更新至7.1.33 https://github.com/php/php-src/releases/tag/php-7.1.33
- 將PHP 7.2.X更新至7.2.24 https://github.com/php/php-src/releases/tag/php-7.2.24
- 將PHP 7.3.X更新至7.3.11 https://github.com/php/php-src/releases/tag/php-7.3.11
漏洞影響
結合EXP github中提到的利用條件,我們可以盡可能的總結利用條件以及漏洞影響范圍。
1、Nginx + php_fpm,且配置location ~ [^/]\.php(/|$)會將請求轉發到php-fpm。
2、Nginx配置fastcgi_split_path_info并且以^開始以$,只有在這種條件下才可以通過換行符來打斷正則表達式判斷。
ps: 則允許index.php/321 -> index.php
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
3、fastcgi_param中PATH_INFO會被定義通過fastcgi_param PATH_INFO $fastcgi_path_info;,當然這個變量會在fastcgi_params默認定義。
4、在nginx層面沒有定義對文件的檢查比如try_files $uri =404,如果nginx層面做了文件檢查,則請求不會被轉發給php-fmp。
這個漏洞在實際研究過程中對真實世界危害有限,其主要原因都在于大部分的nginx配置中都攜帶了對文件的檢查,且默認的nginx配置不包含這個問題。
但也正是由于這個原因,在許多網上的范例代碼或者部分沒有考慮到這個問題的環境,例如Nginx官方文檔中的范例配置、NextCloud默認環境,都出現了這個問題,該漏洞也正真實的威脅著許多服務器的安全。
在這種情況下,這個漏洞也切切實實的陷入了黑暗森林法則,一旦有某個帶有問題的配置被傳播,其導致的可能就是大批量的服務受到牽連,確保及時的更新永遠是對保護最好的手段:>
參考鏈接
- 漏洞issue
- 漏洞發現者提供的環境
- 漏洞exp
- 漏洞成因代碼段
- 漏洞修復commit
- vulhub
- https://www.nginx.com/resources/wiki/start/topics/examples/phpfcgi/
- Seebug漏洞收錄
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.jmbmsq.com/1063/