PHP代码审计-某cms逻辑漏洞导致getshell
发布日期:2024-05-19 浏览次数: 专利申请、商标注册、软件著作权、资质办理快速响应热线:4006-054-001 微信:15998557370
前言 如果存在exec进行拼接的漏洞,该如何绕过 <mark>一黑俩匹配 </mark>?当前如果是拼接和编码这种手法就不说了,现在在看的师傅您是审计大牛的话,这文章可以忽略不看。黑名单... 前言 如果存在exec进行拼接的漏洞,该如何绕过 一黑俩匹配 ? 当前如果是拼接和编码这种手法就不说了,现在在看的师傅您是审计大牛的话,这文章可以忽略不看。 黑名单 $_GET[ $_POST[ $_REQUEST[ $_COOKIE[ $_SESSION[file_put_contentsfile_get_contentsfwritephpinfobase64`shell_exec eval assertsystemexecpassthrupcntl_execpopenproc_openprint_r print urldecodechr include request __FILE__ __DIR__ copycall_user_preg_replacearray_maparray_reversearray_filtergetallheadersget_headersdecode_stringhtmlspecialcharssession_idstrrevsubstrphp.info 第一个匹配: /([w]+)([x00-x1Fx7F/*<>%ws\\]+)?(/i 第二个匹配: 这里不能有$符号,这里是重点 ,当然如果你想编码也可以,或者啥的手法都行,不过我在此之前没想到过,可以继续往下看 /{pboot:if(([^}^$]+))}([sS]*?){/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行的漏洞方法中。 目前新版本未发现有该处函数 文章首发在:奇安信攻防社区 https://forum.butian.net/share/2142 黑白之道发布、转载的文章中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途及盈利等目的,否则后果自行承担! 如侵权请私聊我们删文 END
- 上一篇:干货|一次非法站点rce实战
- 下一篇:针对某集团的一次渗透测试