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
<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 | "/remember"}) ({ |
首先需要把需要注入的class通过defineClass方法加载到线程的内存中,这里需要注意的可能因为classLoader不同的缘故导致在使用Class.forName的时候找不到已经加载到内存中的类。原因如下:
查看java8的文档可以发现,Class.forName(String name)其实是Class.forName(String name,true,currentLoader)的简写,如果直接使用雷神众测上的代码,有可能报class not found,我们需要手动指定classLoader或者直接使用方法调用类的ClassLoader,也就是xxxclass.class.getClassLoader()的结果。
第二个就是使用registerMapping注册一个Controller,这里首先需要获取当前的上下文,直接使用WebApplicationContextUtils.getWebApplicationContext方法获取即可。然后需要设置RequestMappingInfo,主要是设置可以访问控制器的方法和访问controller的路径,最后调用registerMapping方法即可。
tomcat filter内存shell复现
这种方法主要的思路就是通过反射修改WRAP_SAME_OBJECT,然后将lastServicedResponse和lastServicedRequest初始化,使得下一次请求能够获取request和response对象,然后就可以向上下文中注册了一个filter,并且把这个filter放到首位,使得filter一定能够被访问到。我把shell injection的内容一起放在了req和resp的初始化这里。
分析的文章大家直接看原作者的吧,我觉得讲的还是很好的。
有几个断点大家可以参考下,
org.apache.catalina.core.ApplicationFilterChain的104和78行
org.apache.catalina.core.ApplicationContext 的495行
因为这里还考虑了shiro漏洞优化的问题,所以需要把这个注册的filter放到所有filter之前执行。
1 | "/test"}) ({ |
ysomap的改造
- 主要就是修改TemplatesImplBullet,使这边创建不同的继承abstractTranslet类,代码直接放在构造器里面就好了。这里改的时候有个坑,就是直接用StubTransletPayload中添加静态块的时候总是出现各种问题,最后就把request的部分放在单独的类的构造器里面。
- 写了个TemplatesImplBulletTest测试,生成的.ser直接 cat xx.ser|处理|pbcopy到剪切板很方便。
最后代码放在自己的仓库里面了 https://github.com/stArl23/ysomap
参考
- Java代码执行漏洞中类动态加载的应用
- 奇安信的java回显综述
- 冰蝎内存shell,以后可以搞下
- 观星的通杀spring的思路
- threedr3am的文章
- 雷神众测的spring boot内存shell
- ysomap原作者的文章
后续的计划
- 争取把哥斯拉和冰蝎的内存shell注册给写了
- 继续复现漏洞