這篇文章給大家介紹ThinkPHP漏洞分析以及用法,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。
在陸川等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專(zhuān)注、極致的服務(wù)理念,為客戶提供成都做網(wǎng)站、成都網(wǎng)站建設(shè) 網(wǎng)站設(shè)計(jì)制作定制設(shè)計(jì),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),高端網(wǎng)站設(shè)計(jì),網(wǎng)絡(luò)營(yíng)銷(xiāo)推廣,成都外貿(mào)網(wǎng)站制作,陸川網(wǎng)站建設(shè)費(fèi)用合理。
ThinkPHP是一個(gè)快速、兼容而且簡(jiǎn)單的輕量級(jí)國(guó)產(chǎn)PHP開(kāi)發(fā)框架,遵循Apache 2開(kāi)源協(xié)議發(fā)布,使用面向?qū)ο蟮拈_(kāi)發(fā)結(jié)構(gòu)和MVC模式,融合了Struts的思想和TagLib(標(biāo)簽庫(kù))、RoR的ORM映射和ActiveRecord模式。
ThinkPHP可以支持windows/Unix/Linux等服務(wù)器環(huán)境,正式版需要PHP 5.0以上版本,支持MySQL、PgSQL、Sqlite多種數(shù)據(jù)庫(kù)以及PDO擴(kuò)展。
ThinkPHP發(fā)展至今,核心版本主要有以下幾個(gè)系列,ThinkPHP 2系列、ThinkPHP 3系列、ThinkPHP 5系列、ThinkPHP 6系列,各個(gè)系列之間在代碼實(shí)現(xiàn)及功能方面,有較大區(qū)別。其中ThinkPHP 2以及ThinkPHP 3系列已經(jīng)停止維護(hù),ThinkPHP 5系列現(xiàn)使用最多,而ThinkPHP 3系列也積累了較多的歷史用戶。版本細(xì)分如下圖所示:
通過(guò)對(duì)ThinkPHP漏洞的收集和整理,過(guò)濾出其中的高危漏洞,可以得出如下列表:
從上表數(shù)據(jù)來(lái)看,ThinkPHP 3系列版本的漏洞多是2016/2017年被爆出,而ThinkPHP 5系列版本的漏洞基本為2017/2018年被爆出,從2020年開(kāi)始,ThinkPHP 6系列的漏洞也開(kāi)始被挖掘。
從中可以看出,ThinkPHP近年出現(xiàn)的高風(fēng)險(xiǎn)漏洞主要存在于框架中的函數(shù),這些漏洞均需要在二次開(kāi)發(fā)的過(guò)程中使用了這些風(fēng)險(xiǎn)函數(shù)方可利用,所以這些漏洞更應(yīng)該被稱(chēng)為框架中的風(fēng)險(xiǎn)函數(shù),且這些風(fēng)險(xiǎn)點(diǎn)大部分可導(dǎo)致SQL注入漏洞,所以,開(kāi)發(fā)者在利用ThinkPHP進(jìn)行Web開(kāi)發(fā)的過(guò)程中,一定需要關(guān)注這些框架的歷史風(fēng)險(xiǎn)點(diǎn),盡量規(guī)避這些函數(shù)或者版本,則可保證web應(yīng)用的安全性。
根據(jù)ThinkPHP的歷史高危漏洞,梳理出分版本的攻擊風(fēng)險(xiǎn)點(diǎn),開(kāi)發(fā)人員可根據(jù)以下圖標(biāo),來(lái)規(guī)避ThinkPHP的風(fēng)險(xiǎn)版本,如下ThinkPHP暴露面腦圖。
基于暴露面腦圖,我們可以得出幾種可以直接利用的ThinkPHP框架漏洞利用鏈,不需要進(jìn)行二次開(kāi)發(fā)。
ThinkPHP低于3.0 - GetShell
ThinkPHP 低版本可以使用以上漏洞執(zhí)行任意系統(tǒng)命令,獲取服務(wù)器權(quán)限。
ThinkPHP 5.0.x - GetShell
首先明確ThinkPHP框架系列版本。
根據(jù)ThinkPHP版本,如是0.x版本,即可使用ThinkPHP 5.x遠(yuǎn)程代碼執(zhí)行漏洞,無(wú)需登錄,即可執(zhí)行任意命令,獲取服務(wù)器最高權(quán)限。
ThinkPHP 5.1.x - GetShell
首先明確ThinkPHP框架系列版本。
根據(jù)ThinkPHP版本,如是1.x版本,即可使用ThinkPHP 5.x遠(yuǎn)程代碼執(zhí)行漏洞1,無(wú)需登錄,即可執(zhí)行任意命令,獲取服務(wù)器最高權(quán)限。
如需使用ThinkPHP 5.x遠(yuǎn)程代碼執(zhí)行漏洞2,則需要php文件中跳過(guò)報(bào)錯(cuò)提示,即 文件中有語(yǔ)句:“error_reporting(0);”,故該漏洞在5.1.x系列版本利用需要滿足以上前提,利用較難。
從高危漏洞列表中,針對(duì)ThinkPHP不需二次開(kāi)發(fā)即可利用的高危漏洞進(jìn)行深入分析。
4.1.1、漏洞概要
漏洞名稱(chēng):ThinkPHP 2.x/3.0遠(yuǎn)程代碼執(zhí)行
參考編號(hào):無(wú)
威脅等級(jí):高危
影響范圍:ThinkPHP 2.x/3.0
漏洞類(lèi)型:遠(yuǎn)程代碼執(zhí)行
利用難度:簡(jiǎn)單
4.1.2、漏洞描述
ThinkPHP是為了簡(jiǎn)化企業(yè)級(jí)應(yīng)用開(kāi)發(fā)和敏捷WEB應(yīng)用開(kāi)發(fā)而誕生的開(kāi)源MVC框架。Dispatcher.class.php中res參數(shù)中使用了preg_replace的/e危險(xiǎn)參數(shù),使得preg_replace第二個(gè)參數(shù)就會(huì)被當(dāng)做php代碼執(zhí)行,導(dǎo)致存在一個(gè)代碼執(zhí)行漏洞,攻擊者可以利用構(gòu)造的惡意URL執(zhí)行任意PHP代碼。
4.1.3、漏洞分析
漏洞存在在文件 /ThinkPHP/Lib/Think/Util/Dispatcher.class.php 中,ThinkPHP 2.x版本中使用preg_replace的/e模式匹配路由,我們都知道,preg_replace的/e模式,和php雙引號(hào)都能導(dǎo)致代碼執(zhí)行的,即漏洞觸發(fā)點(diǎn)在102行的解析url路徑的preg_replace函數(shù)中。代碼如下:
該代碼塊首先檢測(cè)路由規(guī)則,如果沒(méi)有制定規(guī)則則按照默認(rèn)規(guī)則進(jìn)行URL調(diào)度,在preg_replace()函數(shù)中,正則表達(dá)式中使用了/e模式,將“替換字符串”作為PHP代碼求值,并用其結(jié)果來(lái)替換所搜索的字符串。
正則表達(dá)式可以簡(jiǎn)化為“\w+/([\^\/])”,即搜索獲取“/”前后的兩個(gè)參數(shù),$var[‘\1’]=”\2”;是對(duì)數(shù)組的操作,將之前搜索到的第一個(gè)值作為新數(shù)組的鍵,將第二個(gè)值作為新數(shù)組的值,我們發(fā)現(xiàn)可以構(gòu)造搜索到的第二個(gè)值,即可執(zhí)行任意PHP代碼,在PHP中,我們可以使用${}里面可以執(zhí)行函數(shù),然后我們?cè)趖hinkphp的url中的偶數(shù)位置使用${}格式的php代碼,即可最終執(zhí)行thinkphp任意代碼執(zhí)行漏洞,如下所示:
index.php?s=a/b/c/${code} index.php?s=a/b/c/${code}/d/e/f index.php?s=a/b/c/d/e/${code}
由于ThinkPHP存在兩種路由規(guī)則,如下所示:
http://serverName/index.php/模塊/控制器/操作/[參數(shù)名/參數(shù)值...]
如果不支持PATHINFO的服務(wù)器可以使用兼容模式訪問(wèn)如下:
http://serverName/index.php?s=/模塊/控制器/操作/[參數(shù)名/參數(shù)值...]
也可采用 index.php/a/b/c/${code}一下形式。
4.2.1、漏洞概要
漏洞名稱(chēng):ThinkPHP 5.0.x-5.1.x 遠(yuǎn)程代碼執(zhí)行漏洞
參考編號(hào):無(wú)
威脅等級(jí):嚴(yán)重
影響范圍:ThinkPHP v5.0.x < 5.0.23,ThinkPHP v5.1.x < 5.0.31
漏洞類(lèi)型:遠(yuǎn)程代碼執(zhí)行
利用難度:容易
4.2.2、漏洞描述
2018年12月10日,ThinkPHPv5系列發(fā)布安全更新,修復(fù)了一處可導(dǎo)致遠(yuǎn)程代碼執(zhí)行的嚴(yán)重漏洞。此次漏洞由ThinkPHP v5框架代碼問(wèn)題引起,其覆蓋面廣,且可直接遠(yuǎn)程執(zhí)行任何代碼和命令。電子商務(wù)行業(yè)、金融服務(wù)行業(yè)、互聯(lián)網(wǎng)游戲行業(yè)等網(wǎng)站使用該ThinkPHP框架比較多,需要格外關(guān)注。由于ThinkPHP v5框架對(duì)控制器名沒(méi)有進(jìn)行足夠的安全檢測(cè),導(dǎo)致在沒(méi)有開(kāi)啟強(qiáng)制路由的情況下,黑客構(gòu)造特定的請(qǐng)求,可直接進(jìn)行遠(yuǎn)程的代碼執(zhí)行,進(jìn)而獲得服務(wù)器權(quán)限。
4.2.3、漏洞分析
本次ThinkPHP 5.0的安全更新主要是在library/think/APP.php文件中增加了對(duì)控制器名的限制,而ThinkPHP 5.1的安全更新主要是在library/think/route/dispatch/Module.php文件中增加了對(duì)控制器名的限制。
從以上補(bǔ)丁更新可知,該漏洞的根源在于框架對(duì)控制器名沒(méi)有進(jìn)行足夠的檢測(cè),從而會(huì)在未開(kāi)啟強(qiáng)制路由的情況下被引入惡意外部參數(shù),造成遠(yuǎn)程代碼執(zhí)行漏洞。
由ThinkPHP的架構(gòu)可知,控制器(controller)是通過(guò)url中的路由進(jìn)行外部傳入的,即/index.php?s=/模塊/控制器/操作/[參數(shù)名/參數(shù)值…],控制器作為可控參數(shù),經(jīng)過(guò)library/think/APP.php文件進(jìn)行處理,我們跟蹤路由處理的邏輯,來(lái)完整看一下該漏洞的整體調(diào)用鏈:
首先在run()主函數(shù)中,url傳入后需要經(jīng)過(guò)路由檢查,如下代碼所示:
跟進(jìn) self::routeCheck 函數(shù)
在 620行中調(diào)用 $request->path() 函數(shù),該函數(shù)位于thinkphp/library/think/Request.php文件中,在該函數(shù)中跟進(jìn)到本文件的$this->pathinfo()函數(shù),在該函數(shù)中,就進(jìn)行url解析,獲取路由中的各個(gè)部分內(nèi)容。
其中var_pathinfo參數(shù)即為系統(tǒng)默認(rèn)參數(shù),默認(rèn)值為s,通過(guò)GET方法將獲取到的var_pathinfo的值,即s=/模塊/控制器/操作/[參數(shù)名/參數(shù)值…]的內(nèi)容送到routeCheck()函數(shù)中$path參數(shù)進(jìn)行路由檢查處理。
繼續(xù)回到routeCheck()函數(shù):
在初始化路由檢查配置之后,就進(jìn)行Route::check,由以上代碼看出,若路由尋不到對(duì)應(yīng)操作,即返回$result=false,且開(kāi)啟了強(qiáng)制路由$must的情況下,就會(huì)拋出異常,并最終進(jìn)入Route::parseUrl函數(shù),進(jìn)行$path解析,以上就進(jìn)入了我們的漏洞觸發(fā)點(diǎn):
首先,在該函數(shù)中進(jìn)行url解析,然后,進(jìn)入到parseUrlPath函數(shù),根據(jù)/進(jìn)行路由地址切割,通過(guò)數(shù)組返回:
最終在parseUrl函數(shù)中,將返回的$path提取出路由,即module、controller、action,然后封裝到$route后返回:
回到thinkphp/library/think/App.php文件的run()函數(shù):
在完成RouteCheck后,進(jìn)入到exec()函數(shù)中去:
在該函數(shù)中,首先路由信息首先進(jìn)入module()函數(shù)進(jìn)行檢驗(yàn),該函數(shù)首先查看該路由中的模塊信息是否存在且是否存在于禁止的模塊類(lèi)表中:
模塊存在的話,繼續(xù)往下跟蹤,分別將模塊中的controller、actionName經(jīng)過(guò)處理后賦值到$instance、$action,最終$instance、$action被賦值給了$call參數(shù)。
最終$call參數(shù)進(jìn)入了self::invokeMethod()進(jìn)行處理:
在函數(shù)中,通過(guò)反射ReflectionMethod獲取controller(method[0])和action(method[1])對(duì)象下的方法,然后通過(guò)$args = self::bindParams($reflect, $vars);獲取到傳入?yún)?shù)。以上即為漏洞調(diào)用鏈。
我們根據(jù)Payload來(lái)進(jìn)行最終攻擊鏈的總結(jié):
siteserver/public/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami
根據(jù)上面的分析,我們將路由解析為:
module:index
controller:think\app
action:invokefunction
通過(guò)上述的利用鏈,最終通過(guò)反射ReflectionMethod進(jìn)入到Think/app文件中的invokefunction方法中:
通過(guò)構(gòu)造參數(shù),最終即可執(zhí)行任意代碼。
4.3.1、漏洞概要
漏洞名稱(chēng):ThinkPHP 5.0.x-5.1.x遠(yuǎn)程代碼執(zhí)行漏洞
參考編號(hào):無(wú)
威脅等級(jí):嚴(yán)重
影響范圍:ThinkPHP v5.0.x < 5.0.23,ThinkPHP v5.1.x < 5.0.31
漏洞類(lèi)型:遠(yuǎn)程代碼執(zhí)行漏洞
利用難度:容易
4.3.2、漏洞描述
2019年1月11日,某安全團(tuán)隊(duì)公布了一篇ThinkPHP 5.0.遠(yuǎn)程代碼執(zhí)行漏洞文檔,公布了一個(gè)ThinkPHP 5.0.遠(yuǎn)程代碼執(zhí)行漏洞。文章中的該漏洞與2018年12月的ThinkPHP 5.0.*遠(yuǎn)程代碼執(zhí)行漏洞原理相似,攻擊者可利用該漏洞在一定條件下獲取目標(biāo)服務(wù)器的最高權(quán)限。后經(jīng)研究,在一定條件下,ThinkPHP 5.1.x版本也存在該漏洞,在滿足條件的情況下,攻擊者可利用該漏洞執(zhí)行任意代碼。
4.3.3、漏洞分析
該漏洞的漏洞關(guān)鍵點(diǎn)存在于thinkphp/library/think/Request.php文件中:
從代碼中可知:
method()函數(shù)主要用于請(qǐng)求方法的判斷,var_method沒(méi)有通過(guò),為可控參數(shù),通過(guò)外部傳入,thinkphp支持配置“表單偽裝變量”,var_method在在外部的可控參數(shù)表現(xiàn)為_(kāi)method:
由于var_method沒(méi)有做任何過(guò)濾,我們可以通過(guò)控制_method參數(shù)的值來(lái)動(dòng)態(tài)調(diào)用Request類(lèi)中的任意方法,通過(guò)控制$_POST的值來(lái)向調(diào)用的方法傳遞參數(shù)。由上可知,漏洞存在于method()函數(shù)中,我們就需要尋找該函數(shù)的調(diào)用鏈,來(lái)構(gòu)造POC。
第一個(gè)構(gòu)造鏈在__construct()構(gòu)造方法中,該方法如下:
函數(shù)中對(duì)$option數(shù)組進(jìn)行遍歷,當(dāng)$option的鍵名為該類(lèi)屬性時(shí),則將該類(lèi)同名的屬性賦值為$options中該鍵的對(duì)應(yīng)值。因此可以構(gòu)造請(qǐng)求如下,來(lái)實(shí)現(xiàn)對(duì)Request類(lèi)屬性值的覆蓋,例如覆蓋filter屬性。filter屬性保存了用于全局過(guò)濾的函數(shù)。
再上一個(gè)漏洞分析過(guò)程中,我們跟蹤到了路由檢查self::routeCheck 函數(shù),在過(guò)程中,會(huì)進(jìn)入到thinkphp/library/think/Route.php文件中的check()函數(shù),函數(shù)中調(diào)用了method()方法,并將函數(shù)執(zhí)行結(jié)果轉(zhuǎn)換為小寫(xiě)后保存在$method變量。在調(diào)用構(gòu)造函數(shù)覆蓋變量時(shí),可以直接覆蓋method,這樣上面的$method = strtolower($request->method()); 的$method最終的值就可以被控制了。
在該函數(shù)中,調(diào)用了method()函數(shù),在該函數(shù)中,就將進(jìn)行變量覆蓋:
通過(guò)調(diào)用構(gòu)造函數(shù)__construct(),最終將請(qǐng)求參數(shù)保存到input參數(shù)。
在進(jìn)行routecheck后,已完成了第一部分調(diào)用鏈,實(shí)現(xiàn)了變量覆蓋,接下來(lái)就是要實(shí)現(xiàn)變量覆蓋后的代碼執(zhí)行,具體調(diào)用鏈如下:
返回到App.php文件中的run()函數(shù),接著進(jìn)入到exec()函數(shù)中,然后進(jìn)入到module()函數(shù)中,最終進(jìn)入到了invokeMethod()函數(shù),
從invokeMethod()函數(shù)中進(jìn)入到bindParams()函數(shù),然后進(jìn)入到param()函數(shù):
然后最終調(diào)用到input()函數(shù):
最終我們根據(jù)array_walk_recursive()函數(shù),進(jìn)入到了filterValue()函數(shù):
最終,通過(guò)回調(diào)函數(shù)call_user_func執(zhí)行了代碼,整個(gè)調(diào)用鏈如上所示。
關(guān)于ThinkPHP漏洞分析以及用法就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。