thinkphp 5.x漏洞分析
根據網上釋出關於thinkphp 5.x遠端程式碼執行漏洞預警,分析漏洞發生點,對比官方git更新版本,對照發現更新為request類,如下圖所示:
觀察發生更改的點為pathinfo()、method()、param()、post()、request()和cache()方法上,由此預測漏洞發生可能與這幾個方法有關,進入5.0.10版本request.php中,如下圖所示:
最後返回一個input方法,跟蹤input方法,如下圖所示: 安全脈搏:https://www.secpulse.com/archives/95012.html
觀察描述可得input方法可獲得變數,支援過濾和預設值,檢視如何獲得預設值,如下圖所示:
當輸入資料為陣列時採用array_walk_recursive函式,filterValue為返回函式,$filter為傳參,filterValue函式為下圖所示:
當$filter為可用函式時,就會進入call_user_func函式中,也就是當我們可控$value時,即可執行任意程式碼。目前清楚呼叫過程,考慮如何使用rop鏈,已知是post函式導致,檢視何處呼叫或使用了此函式,觀察request檔案呼叫過程。初始化魔術方法__construct,如下圖所示:
通過判斷是否存在類中,將陣列$options一次賦值給$item,此時如果可以獲取到$options,即可完成rop鏈,觀察可利用傳值的方法,如下圖所示:
發現傳送的資料為 config 檔案中 get 方法返回的資料,查詢官方手冊可得 安全脈搏:https://www.secpulse.com/archives/95012.html
在此版本中var_method=>_method,所以此時rop鏈已經形成,通過post方式傳送x=ipconfig&_method=__construct&filter[]=system,即可產生漏洞,如下圖所示:
接著分析5.0.23版本,發現request檔案中也使用了method函式,如下圖所示: 安全脈搏:https://www.secpulse.com/archives/95012.html
但是由於此版本在APP::run()初始化時對filter進行預設設定,無法進行直接覆蓋,所以觀察method函式,如下圖所示:
檢視server函式,如下圖所示:
返回一個input方法和之前input方法一樣,採用call_user_func函式執行任意程式碼。利用方式與5.0.10相同,post資料為:
_method=__construct&filter[]=system&server[REQUEST_METHOD]=dir
當開啟debug時,會將命令執行回顯在頁面上,如下圖所示: 安全脈搏:https://www.secpulse.com/archives/95012.html
觀察兩次exp,可以看出第一次命令執行前可使用任意字元=要執行的命令,第二次命令執行前必須為server[REQUEST_METHOD]=要執行的命令,觀察兩次版本關於method方法的不同,如下圖所示:
右邊為5.0.10,左邊為5.0.23,可得知5.0.10在非命令列下會呼叫server['REQUEST_METHOD'],此陣列只有在create方法中賦值,所以直接到$_SERVER['REQUEST_METHOD']賦值為post,5.0.23在server函式中當$name為陣列時,會將陣列中的字串賦值為$_SERVER['REQUEST_METHOD'],結果為post,達到與5.0.10同樣的效果。
當然此exp的使用必須為debug開啟時才有回顯,原因為當開啟debug時,會自動記錄路由和請求資訊,自動呼叫param方法,如下圖所示:
根據官方修改中還有pathinfo()現在暫無在rop鏈中使用,觀察此函式,如下圖所示:
瞭解過5.1版本的遠端命令執行的大佬,肯定對這個函式不陌生,這個函式是分析url字尾,關於路由協議的,由此猜測可能在路由協議中產生了遠端命令執行漏洞,追蹤pathino函式的呼叫情況,從入口函式觀察到,如下圖所示:
通過pathinfo的引數s匯入相關模組,進入路由檢測routeCheck方法,如下圖所示:
接著進入check方法,如下圖所示: 安全脈搏:https://www.secpulse.com/archives/95012.html
接下來進入路由規則檢測,為了大家理解方便,其中$request傳入引數的值為下圖所示:
進入到parseRule中,呼叫關鍵程式碼,如下圖所示:
最終定位到\thinkphp5.0.23\vendor\topthink\think-captcha\src\helper.php檔案中,程式碼為\think\Route::get('captcha/[:id]', "\\think\\captcha\\CaptchaController@index"),註冊GET路由,通過self::rule、self::setRule到
self::$rules[$type][$rule]=['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern],由於在base.php中已經宣告第三方類庫vendor,會自動載入helper檔案,當s=captcha時,rop鏈反射到helper檔案下,形成rce。
首先通過前面的所述通過路由檢測,到達app.php中,具體執行鏈如下圖所示:
進入exec方法中,如下圖所示:
進入到之前的param函式中,因為5.0.23中method方法要為get型別,所以在post資料時要加上method=get。
安全脈搏:https://www.secpulse.com/archives/95012.html
參考連結:
https://github.com/top-think/framework/commit/4a4b5e64fa4c46f851b4004005bff5f3196de003