ThinkPHP 5.0.0~5.0.23 RCE 漏洞分析
最近TP被爆出三個RCE,最近想跟著大佬們寫的文章,把這幾個洞分析一下,這裡是 5.0.0~5.0.23
的RCE。官方補丁地址: https://github.com/top-think/framework/commit/4a4b5e64fa4c46f851b4004005bff5f3196de003
exp:
http://127.0.0.1/index.php?s=captcha post_poc1:_method=__construct&filter[]=system&method=get&get[]=whoami post_poc2:_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=whoami
未開啟Debug

漏洞觸發流程
首先進入入口點 App.php
中的 run
方法,例項化了一個 $request
物件傳給了 routeCheck
方法。

Route::check

$method
由 $request->method()
獲得, $rules
會根據 $method
的不同而獲得不同的路由規則

跟進 $request->method()
,漏洞點在這裡,通過外部傳入 Config::get('var_method')
可以造成該類的任意方法呼叫。

在tp的預設中配置中設定了表單請求型別偽裝變數如下,POST一個 _method
引數即可進入判斷。

而 Request
類的構造方法中, $options
可控,因此我們可以覆蓋該類的任意屬性。其中 $this->filter
儲存著全域性過濾規則。

我們請求的路由是 ?s=captcha
,它對應的註冊規則為 \think\Route::get
。

因此我們需要讓 $this->method
返回值為get,所以payload中有個 method=get
,然後才會取出 self::$rules[$method]
的值給$rules。
路由檢測後接著會執行 self::exec
,並進入 method
分支

跟進 Request::instance()->param()
, $this->param
通過 array_merge
將當前請求引數和URL地址中的引數合併。

跟進 $this->input
,該方法用於對請求中的資料即接收到的引數進行過濾,而過濾器通過 $this->getFilter
獲得。


$this->filter
為 system
,回到input,因為data為陣列,因此可以進入if條件呼叫 array_walk_recursive($data, [$this, 'filterValue'], $filter)
,對 $data
中的每一個值呼叫 filterValue
函式。

即間接呼叫 call_user_func
,且兩個引數可控,造成RCE。

poc2
回到param方法,跟進method方法,此時引數為True。

$this->server

這裡也有一個 input
,

input
中的
data
對應於
server
的
$this->server
,
name
的值為
REQUEST_METHOD
。因此傳入
server[REQUEST_METHOD]=whoami
,因為此時
data
不是陣列,所以會直接進入
filterValue

結合之前的 __construct
覆蓋 $filter
屬性,同樣可以造成RCE。

補丁分析
對錶單請求型別偽裝變數添加了白名單,防止任意屬性覆蓋。
後記
這個洞的攻擊鏈很強,Orz!!。