PHP代碼審計-某cms邏輯漏洞導致getshell
前言 如果存在exec進行拼接的漏洞,該如何繞過 一黑倆匹配 ?當前如果是拼接和編碼這種手法就不說了,現在在看的師傅您是審計大牛的話,這文章可以忽略不看。黑名單...
前言
如果存在exec進行拼接的漏洞,該如何繞過 一黑倆匹配 ?
當前如果是拼接和編碼這種手法就不說了,現在在看的師傅您是審計大牛的話,這文章可以忽略不看。
黑名單
$_GET[ $_POST[ $_REQUEST[ $_COOKIE[ $_SESSION[ file_put_contents file_get_contents fwrite phpinfo base64 ` shell_exec eval assert system exec passthru pcntl_exec popen proc_open print_r print urldecode chr include request __FILE__ __DIR__ copy call_user_ preg_replace array_map array_reverse array_filter getallheaders get_headers decode_string htmlspecialchars session_id strrev substr php.info
第一個匹配:
/([\w]+)([\x00-\x1F\x7F\/\*\<\>\%\w\s\\\\]+)?\(/i
第二個匹配:
這里不能有$符號,這里是重點 ,當然如果你想編碼也可以,或者啥的手法都行,不過我在此之前沒想到過,可以繼續往下看
/\{pboot:if\(([^}^\$]+)\)\}([\s\S]*?)\{\/pboot:if\}/
正文
先看效果

審計流程
通過審計工具半自動篩選出漏洞點。

跟進該漏洞點文件:apps/home/controller/ParserController.php
最終是通過了$matches[1][$i]進入到eval函數中。
第一個黑名單
接著往上看,這里有黑名單,如果$matches[1][$i]有黑名單就會跳出解析
這里的\\是社區編輯器默認加上防止轉義,太多就懶得改了
// 過濾特殊字符串
if (preg\_match('/(\\(\[\\w\\s\\.\]+\\))|(\\$\_GET\\\[)|(\\$\_POST\\\[)|(\\$\_REQUEST\\\[)|(\\$\_COOKIE\\\[)|(\\$\_SESSION\\\[)|(file\_put\_contents)|(file\_get\_contents)|(fwrite)|(phpinfo)|(base64)|(\`)|(shell\_exec)|(eval)|(assert)|(system)|(exec)|(passthru)|(pcntl\_exec)|(popen)|(proc\_open)|(print\_r)|(print)|(urldecode)|(chr)|(include)|(request)|(\_\_FILE\_\_)|(\_\_DIR\_\_)|(copy)|(call\_user\_)|(preg\_replace)|(array\_map)|(array\_reverse)|(array\_filter)|(getallheaders)|(get\_headers)|(decode\_string)|(htmlspecialchars)|(session\_id)|(strrev)|(substr)|(php.info)/i', $matches\[1\]\[$i\])) {
$danger = true;
}
// 如果有危險函數,則不解析該IF
if ($danger) {
continue;
}
黑名單分別是攔截以下內容:
$\_GET\[ $\_POST\[ $\_REQUEST\[ $\_COOKIE\[ $\_SESSION\[ file\_put\_contents file\_get\_contents fwrite phpinfo base64 \` shell\_exec eval assert system exec passthru pcntl\_exec popen proc\_open print\_r print urldecode chr include request \_\_FILE\_\_ \_\_DIR\_\_ copy call\_user\_ preg\_replace array\_map array\_reverse array\_filter getallheaders get\_headers decode\_string htmlspecialchars session\_id strrev substr php.info
這可以看出過濾了好多函數,當然既然是黑名單就有繞過的方式,這里可以是加密形式繞過,不過加密后的密文做成payload就逆向解密不了了,因為是由特殊不可見數據流存在就會導致反解密會不到原來的明文。
這里可以用file和fputs函數繞過
第一個過濾
繼續往上看,看到這個if判斷,這里也是將$matches[1][$i]進行過濾,保證用戶輸入的字符串是無危害的,簡單來說就是‘括號前面不能有字母、數字字符串’。
// 帶有函數的條件語句進行安全校驗
if (preg\_match\_all('/(\[\\w\]+)(\[\\x00-\\x1F\\x7F\\/\\\*\\<\\>\\%\\w\\s\\\\\\\\\]+)?\\(/i', $matches\[1\]\[$i\], $matches2)) {
foreach ($matches2\[1\] as $value) {
if (function\_exists(trim($value)) && ! in\_array($value, $white\_fun)) {
$danger = true;
break;
}
}
foreach ($matches2\[2\] as $value) {
if (function\_exists(trim($value)) && ! in\_array($value, $white\_fun)) {
$danger = true;
break;
}
}
}

當然這里也是黑名單,直接/*--*/繞過

4.3. 第三個過濾
這個就比較好過了就是指定的標簽語法,使用這個{pboot:if(312313)}(13123){/pboot:if}
4.3.1. 注意:
這里不能有$符號,這里是重點
$pattern = '/\\{pboot:if\\((\[^}^\\$\]+)\\)\\}(\[\\s\\S\]\*?)\\{\\/pboot:if\\}/';
if (preg\_match\_all($pattern, $content, $matches)) {
}


構建payload
由于這里不能用美元符號”$“,前面第一個過濾說過,可用file函數繞過,如下圖:

通過上面file函數獲取的美元符號,并且通過fputs進行寫文件,當然需要絕對路徑才能讀取美元符,這里就比較簡單了,直接讓cms報錯就好了。

調用鏈
ParserController.php:84, app\\home\\controller\\ParserController->parserAfter()
TagController.php:47, app\\home\\controller\\TagController->index()
IndexController.php:50, app\\home\\controller\\IndexController->\_empty()
2:2, core\\basic\\Kernel::axqjlxzuuxaapu328937ae1368b88e8bf79cb6b342866a()
2:2, core\\basic\\Kernel::run()
start.php:17, require()
index.php:23, {main}()
訪問首頁就會進入到apps/home/controller/IndexController.php的_empty()方法,需要get的參數帶有tag就可進入該判斷

跟進到apps/home/controller/TagController.php的inde()方法,跟進第47行并跟進到apps/home/controller/ParserController.phpparserAfter()方法,最后就會到84行的漏洞方法中。

