fastcgi(9000端口):
以下這段話摘自p神
Fastcgi其實是一個通信協議,和HTTP協議一樣,都是進行數據交換的一個通道。 HTTP協議是瀏覽器和服務器中間件進行數據交換的協議,瀏覽器將HTTP頭和HTTP體用某個規則組裝成數據包,以TCP的方式發送到服務器中間件,服務器中間件按照規則將數據包解碼,並按要求拿到用戶需要的數據,再以HTTP協議的規則打包返回給服務器。 類比HTTP協議來說,fastcgi協議則是服務器中間件和某個語言后端進行數據交換的協議。Fastcgi協議由多個record組成,record也有header和body一說,服務器中間件將這二者按照fastcgi的規則封裝好發送給語言后端,語言后端解碼以后拿到具體數據,進行指定操作,並將結果再按照該協議封裝好后返回給服務器中間件
http數據實際上是由客戶端瀏覽器到達服務器中間件,中間件再將數據再次封裝打包給后端語言進行處理,處理結束再返回給服務器中間件。
php-fpm(fastcgi進程管理器):
FPM其實是一個fastcgi協議解析器,Nginx等服務器中間件將用戶請求按照fastcgi的規則打包好通過TCP傳給FPM。FPM按照fastcgi的協議將TCP流解析成真正的數據。
用戶訪問http://127.0.0.1/index.php?a=1&b=2
,如果web目錄是/var/www/html
,那么Nginx會將這個請求變成如下key-value對:
PHP-FPM拿到fastcgi的數據包后,進行解析,得到上述這些環境變量。然后,執行SCRIPT_FILENAME
的值指向的PHP文件,也就是/var/www/html/index.php
。
比如解析漏洞就和PHP-FPM的處理配置有關系,主要在於其對script_filename的處理方式,比如/ppp.png/.php,從右向左解析,不存在/.php,則去除/.php繼續向左,存在ppp.png,拿來當作php解析,這種為客戶考慮的想法卻導致了漏洞的存在
正確的解決方法有兩種:
一是在Nginx端使用fastcgi_split_path_info將path info信息去除后,用tryfiles判斷文件是否存在;
二是借助PHP-FPM的security.limit_extensions配置項,避免其他后綴文件被解析。
PHP-FPM未授權訪問漏洞主要利用security.limit_extensions配置,PHP-FPM默認監聽9000端口,如果這個端口暴露在公網,則我們可以自己構造fastcgi協議,和fpm進行通信。當然如果在內網中開放的此端口,如果存在SSRF,就能通過SSRF去打fastcgi。
php-fpm根據script_filename的值來執行php文件,如果該文件不存在,則返回404
其限定了只有某些后綴的文件允許被fpm執行,默認是.php,由於這個配置項的限制,如果想利用PHP-FPM的未授權訪問漏洞,首先就得找到一個已存在的PHP文件。
通常使用源安裝php的時候,服務器上都會附帶一些php后綴的文件,可以使用find / -name "*.php"
來全局搜索一下默認環境就能找到一些。接下來已經知道服務器可以來執行我們要指定的php,但只是執行服務器上的文件,不能執行我們的惡意payload,那么接下來就要想辦法構造代碼執行:
PHP.INI中有兩個重要配置項: auto_prepend_file: auto_prepend_file是告訴PHP,在執行目標文件之前,先包含auto_prepend_file中指定的文件; auto_append_file: auto_append_file是告訴PHP,在執行完成目標文件后,包含auto_append_file指向的文件。 假設我們設置auto_prepend_file為php://input,那么就等於在執行任何php文件前都要包含一遍POST的內容。所以,我們只需要把待執行的代碼放在Body中,他們就能被執行了。
(需要開啟遠程文件包含選項allow_url_include) 但是上面兩個值是在服務器端,我們可以通過PHP-FPM來設置auto_prepend_file的值,這又涉及到PHP-FPM的兩個環境變量,PHP_VALUE和PHP_ADMIN_VALUE。
這兩個環境變量就是用來設置PHP配置項的,PHP_VALUE可以設置模式為PHP_INI_USER和PHP_INI_ALL的選項,PHP_ADMIN_VALUE可以設置所有選項。
(disable_functions除外,這個選項是PHP加載的時候就確定了,在范圍內的函數直接不會被加載到PHP上下文中)
比如給php-fpm傳遞如下變量:
{ 'GATEWAY_INTERFACE': 'FastCGI/1.0', 'REQUEST_METHOD': 'GET', 'SCRIPT_FILENAME': '/var/www/html/index.php', 'SCRIPT_NAME': '/index.php', 'QUERY_STRING': '?a=1&b=2', 'REQUEST_URI': '/index.php?a=1&b=2', 'DOCUMENT_ROOT': '/var/www/html', 'SERVER_SOFTWARE': 'php/fcgiclient', 'REMOTE_ADDR': '127.0.0.1', 'REMOTE_PORT': '12345', 'SERVER_ADDR': '127.0.0.1', 'SERVER_PORT': '80', 'SERVER_NAME': "localhost", 'SERVER_PROTOCOL': 'HTTP/1.1' 'PHP_VALUE': 'auto_prepend_file = php://input', 'PHP_ADMIN_VALUE': 'allow_url_include = On' }
設置auto_prepend_file = php://input
且allow_url_include = On
,然后將我們需要執行的代碼放在Body中,即可執行任意代碼。
用gopherus就可以直接生成payload,需要指定一個script_filename,即一個服務器上已經存在的文件,如果沒有指定將使用默認的一個php,/usr/share/php/PERL.php,實際滲透過程中可能需要通過網站報錯,phpinfo信息泄露等去找其存在的php文件,還要輸入我們要執行的系統命令,這個payload實際上利用的正是p牛說的,auto_prepend_file一個php://input,從而在http body中去傳遞我們的payload,如前文所說動態地修改了php.ini里的配置:
env["REQUEST_METHOD"] = "POST" env["PHP_VALUE"] = "auto_prepend_file = php://input" env["PHP_ADMIN_VALUE"] = "allow_url_include = On\ndisable_functions = \nsafe_mode = Off"
測試:
環境:
https://github.com/vulhub/vulhub/tree/master/fpm
路徑改為p牛環境中配的路徑,直接用gopherus生成gopher的payload打,ubuntu16.04的curl的liburl為7.47,不存在%00截斷,因此直接通過gopher發送payload到本機的9000端口
也可以直接用p牛的腳本直接打,直接向127.0.0.1的9000端口發送payload,這里假設是公網的fastcgi端口未授權訪問:
腳本鏈接地址:
https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75
注意:
curl/libcurl 7.43
上 gopher 協議存在 bug(%00 截斷),經測試 7.49 可用
並不限於 PHP 的 SSRF。當存在 XXE、ffmepg SSRF 等漏洞的時候,也可以進行利用
FPM模式:
一般來說,apache服務器常用module方式起php,nginx服務器常用fastcgi模式起php,php-fpm下還可以繼續分,如果使用fastcgi模式,nginx與php-fpm通信可以通過兩種模式,一種是TCP模式,一種是unix 套接字(socket)模式
TCP模式即是php-fpm進程會監聽本機上的一個端口(默認9000),然后nginx會把客戶端數據通過fastcgi協議傳給9000端口,php-fpm拿到數據后會調用cgi進程解析
unix socket其實嚴格意義上應該叫unix domain
socket,它是unix系統進程間通信(IPC)的一種被廣泛采用方式,以文件(一般是.sock)作為socket的唯一標識(描述符),需要通信的兩個進程引用同一個socket描述符文件就可以建立通道進行通信了。
$fp = stream_socket_client("/var/run/php/U_wi11_nev3r_kn0w.sock", $errno, $errstr,30);$out = urldecode("這里填寫payload"); stream_socket_sendto($fp,$out); while (!feof($fp)) {echo htmlspecialchars(fgets($fp, 10)); }fclose($fp);//'
利用以上代碼就可以與unix套接字進行通信,來進行rce
PHP_ADMIN_VALUE['extension'] = /tmp/tr1ple.so 讓php加載擴展
memcache(監聽11211端口):
memcached是一套分布式的高速緩存系統。它以Key-Value(鍵值對)形式將數據存儲在內存中,這些數據通常是應用讀取頻繁的。正因為內存中數據的讀取遠遠大於硬盤,因此可以用來加速應用的訪問。
漏洞成因:
由於memcached安全設計缺陷,客戶端連接memcached服務器后無需認證就可讀取、修改服務器緩存內容。
漏洞影響:
除memcached中數據可被直接讀取泄漏和惡意修改外,由於memcached中的數據像正常網站用戶訪問提交變量一樣會被后端代碼處理,當處理代碼存在缺陷時會再次導致不同類型的安全問題。 不同的是,在處理前端用戶直接輸入的數據時一般會接受更多的安全校驗,而從memcached中讀取的數據則更容易被開發者認為是可信的,或者是已經通過安全校驗的,因此更容易導致安全問題。
具體案例分析見:
DiscuzX 兩處 SSRF 挖掘及利用
https://zhuanlan.zhihu.com/p/51907363
Jenkins(默認8080) 未授權訪問
默認情況下 Jenkins 面板中用戶可以選擇執行腳本界面來操作一些系統層命令,攻擊者可通過未授權訪問漏洞或者暴力破解用戶密碼等進腳本執行界面從而獲取服務器權限。
詳情見:https://paper.seebug.org/409/#0x03-jenkins
MongoDB未授權訪問
開啟 MongoDB 服務時不添加任何參數時,默認是沒有權限驗證的,而且可以遠程訪問數據庫,登錄的用戶可以通過默認端口無需密碼對數據庫進行增、刪、改、查等任意高危操作。
詳情見:https://paper.seebug.org/409/#0x04-mongodb
ZooKeeper (默認端口2181)未授權訪問
Zookeeper 的默認開放端口是2181。Zookeeper 安裝部署之后默認情況下不需要任何身份驗證,造成攻擊者可以遠程利用 Zookeeper,通過服務器收集敏感信息或者在 Zookeeper 集群內進行破壞(比如:kill命令)。攻擊者能夠執行所有只允許由管理員運行的命令。
詳情見:https://paper.seebug.org/409/#0x05-zookeeper
Elasticsearch(默認9200端口)未授權訪問
Elasticsearch 是一款 java 編寫的企業級搜索服務。越來越多的公司使用 ELK 作為日志分析,啟動此服務默認會開放9200端口,可被非法操作數據。
詳情見:https://paper.seebug.org/409/#0x06-elasticsearch
CouchDB (默認5984端口)未授權訪問
CouchDB 默認在 5984 端口開放 Restful 的 API 接口,用於數據庫的管理功能。其 HTTP Server 默認開啟時沒有進行驗證,而且綁定在0.0.0.0,所有用戶均可通過 API 訪問導致未授權訪問。任何連接到服務器端口上的人,都可以調用相關 API 對服務器上的數據進行任意的增刪改查,其中通過 API 修改 local.ini 配置文件,可進一步導致執行任意系統命令,獲取服務器權限
詳情見:https://paper.seebug.org/409/#0x09-couchdb
Docker 未授權訪問
Docker Remote API 是一個取代遠程命令行界面(rcli)的REST API。通過 docker client 或者 http 直接請求就可以訪問這個 API,通過這個接口,我們可以新建 container,刪除已有 container,甚至是獲取宿主機的 shell
詳情見:https://paper.seebug.org/409/#0x010-docker
參考:
https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html
https://paper.seebug.org/409/