漏洞分析環境:https://github.com/vulnspy/thinkphp-5.1.29與phpstudy php-7.0.12-nts+apache
Thinkphp框架中url的訪問為index.php/模塊/控制器/操作,index.php稱為入口文件(pathinfo方式的URL訪問),也可使用兼容模式,通過變量s傳參/模塊/控制器/操作。
根據參考連結中的關鍵代碼,追溯漏洞源文件/thinkphp/library/think/route/dispatch/Module.php,如下圖所示:
繼續跟蹤result數組,如下圖所示:
發現調用父類init函數,module類繼承於抽象類Dispatch,追蹤Dispatch如下圖所示:
觀察判斷語句,通過偽變量this對象引用到rule.php文件中,執行doAfter函數,獲取路由的後置操作。如下圖所示:
這裡稍微引入一下路由後置操作簡介,至於大佬可以加以補充,小弟就不獻醜了。
Thinkphp路由為三種模式:
1、普通模式(採用默認的pathinfo方式url):'url_route_on' => false
2、混合模式(該方式下面,只需要對需要定義路由規則的訪問地址定義路由規則,其它的仍然按照默認的PATH_INFO模式訪問URL):'url_route_on' => true
3、強制模式(必須定義路由才可訪問):'url_route_must' => true 'url_route_on' => true
Thinkphp 5.0以上版本對新的路由功能做了新的增強,支持路由到模塊(模塊/控制器/操作)、控制器(控制器類/操作)、類(任何類庫),也是此次漏洞的原因之一
根據以上描述,分析此次漏洞環境代碼,採用為混合模式,在採用s變量獲取,觸發漏洞。分析獲取pathifo的過程,如下圖所示:
第一步先判斷pathinfo是否有兼容模式的參數,第二步分析pathinfo信息,將ORIG_PATH_INFO,REDIRECT_PATH_INFO,REDIRECT_URL三種模式循環賦值給變量$type,使用server函數,將變量$type轉換為大寫存入 server數組中,當變量server數組已被賦值,則返回server數組,否則返回空,如下圖所示:
最終當變量pathinfo不為空時,$pathinfo為刪除/符號前後空白字符的字符串,追溯pathinfo函數,找到path函數,如下圖所示:
跟蹤path函數,找到App.php文件中的routeCheck函數,如下圖所示:
可以觀察到此函數返回一個Dispatch對象,而module文件中$result = $this->dispatch,繼續跟蹤Check函數,如下圖所示:
返回值為創建的新對象UrlDispatch,跟蹤UrlDispatch類,發現為Url的別名引用,如下圖所示:
跟蹤Url類,發現繼承於抽象類Dispatch,再由魔術方法__construct在方法被實現時調用,觀察此方法,如下圖所示:
此方法用偽變量this將dispatch對象賦值為$dispatch,返回觀察Url類,如下圖所示:
init方法返回到新對象Module的init方法中,也就是Module.php中parent::init(),其中dispatch通過parseUrl函數賦值給$result,觀察parseUrl,如下圖所示:
變量module由getConfig函數獲取app_multi_module的值,觀察得知app_multi_module為真,得到刪除第一個數組的變量$path,最後返回一個封裝路由route,包含變量module、controller和action,並傳遞給變量result。
通過以上分析得到變量result的生成結果,當使用explode分割字符串時,輸入/index/\think\request/cache,得到如圖所示:
通過上述刪除第一個數組,賦值給module.php中的變量module,如下圖所示:
此時變量module為index,繼續觀察找到控制器變量controller,如下圖所示:
此時變量controller為\think\request,繼續觀察找到操作名變量actionName,如下圖所示:
此時變量actionName為cache,最後進入請求操作,跟蹤到操作器controller文件中,找到cache方法,如下圖所示:
通過特殊構造達到執行phpinfo的效果,將變量key設置為1|phpinfo,跳過判斷是否存在於匿名類中,並通過true===1,進入list函數將變量key和fun分別賦值為1和phpinfo(根據php-7.0.12-nts環境,list賦值為從右向左),從而達到運行phpinfo函數。
第二個poc為:
/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=php%20-r%20'phpinfo();'
也等同於:
/index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=php%20-r%20'phpinfo();』
跟蹤到invokefunction函數,如下圖所示:
通過將function設置為call_user_func_array,vars[0]=system,即可生成system()函數,通過vars[1][]對上述call_user_func_array返回的回調函數system設置參數變量,達到運行系統命令。
參考連結:
http://www.vulnspy.com/cn-thinkphp-5.x-rce/thinkphp_5.x_(v5.0.23%E5%8F%8Av5.1.31%E4%BB%A5%E4%B8%8B%E7%89%88%E6%9C%AC)_%E8%BF%9C%E7%A8%8B%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8%EF%BC%88getshell%EF%BC%89/
https://www.jianshu.com/p/73ed6e42d389
↙↙↙ 點擊 」閱讀原文「 與作者展開話題探討,直面交流