Java反序列化漏洞二
CommonsBeanutils利用链
前置知识
PriorityQueue => getter
CB链使用PriorityQueue作为入口,在CC链中我们知道PriorityQueue.readObject()
会触发xxxComparator.compare()
,比如CC2中的TransformingComparator.compare()
。在CB链中触发的是BeanComparator.compare()
。
可以看到BeanComparator.compare()
会触发任意property的getter方法,而property就是BeanComparator的构造属性。
1 |
|
getter => ?
既然能够触发到任意getter方法,能够利用的getter有哪些呢?这里给出
TemplatesImpl.getoutputProperties()
TemplatesImpl.getoutputProperties()
不必多说,在CC链中我们也提到它可以触发newTransformer
。
JdbcRowSetImpl.getdatabaseMetaData()
JdbcRowSetImpl.getdatabaseMetaData()
则不同,它会调用到connect()
方法最终得到一个JNDI注入点InitialContext.lookup()
。
其中DataSourceName可以使用JdbcRowSetImpl.setDataSourceName(jndiURL)
来设置。
CB1
- DEP:commons-beanutils:1.9.2&&commons-collections:3.1
- IN:PriorityQueue.readObject()
- OUT:queueArray[i].getoutputProperties()
- SINK:Templates
1
2
3
4
5
6
7
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDownUsingComparator()
BeanComparator.compare()
PropertyUtils.getProperty(o, property) ==> two obj invoke getter: o1&&o2
o.getoutputProperties()
<Templates>
CB2(noCC)
剥离CC依赖
CBnoCC与CB1的区别就在于剥除了对于commons-collections的依赖,起因是CB1中的BeanComparator如果使用BeanComparator(String property)
这个重载就会使用ComparableComparator
这个类,而这个类是在CC里的:
因此换成下面那个重载即可,寻找jdk中Comparator的子类,发现String.CASE_INSENSITIVE_ORDER
也就是CaseInsensitiveComparator
这个java.lang.String中的静态内部类是符合要求的,基于此重构BeanComparator即可,其余不变。
1 |
|
- DEP:commons-beanutils:1.9.2
- IN:PriorityQueue.readObject()
- OUT:queueArray[i].getoutputProperties()
- SINK:Templates
1
2
3
4
5
6
7
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDownUsingComparator()
BeanComparator.compare()
PropertyUtils.getProperty(o, property) ==> two obj invoke getter: o1&&o2
o.getoutputProperties()
<Templates>
CB3
前面已经提到了如何剥离CC依赖,由于这不会对利用过程有任何影响,因此这条链也可以去除CC的依赖。
- DEP:commons-beanutils:1.9.2
- IN:PriorityQueue.readObject()
- OUT:queueArray[i].getdatabaseMetaData()
- SINK:InitialContext.lookup()
1
2
3
4
5
6
7
8
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDownUsingComparator()
BeanComparator.compare()
PropertyUtils.getProperty(o, property) ==>two obj invoke getter: o1.gettr()&&o2.getter()
JdbcRowSetImpl.getdatabaseMetaData()
JdbcRowSetImpl.connect()
InitialContext.lookup()
Rome利用链
有关Rome链的一些补充
前置知识
? => getter
调用到Rome中的BeanIntrospector.getPropertyDescriptors(this._beanClass)
即可任意getter,其中有两个类存在此调用:
ToStringBean.toString(String prefix)
EqualsBean.beanEquals()
? => obj.toString()
BadAttributeValueExpException.readObject()
CC链里已经讲过,BadAttributeValueExpException可以触发任意类的toString()。
EqualsBean.beanHashCode()
想要触发beanHashCode()就需要触发hashCode(),CC链中也提到**HashMap.readObject()**可以触发任意类的hashcode()方法。
1
2
3
4HashMap.readObject()
HashMap.hash()
ObjectBean.hashcode()
EqualsBean.beanHashCode()
? => obj.beanEquals()
HashMap.readObject()同样可以触发任意Map的equals()方法,借助AbstractMap即可触发任意类的equals()方法:
1 |
|
Rome1
- DEP:rome:1.0
- IN:HashMap.readObject()
- OUT:ToStringBean.toString()
- SINK:Templates
1
2
3
4
5
6
7
8
HashMap.readObject()
HashMap.hash()
ObjectBean.hashcode()
EqualsBean.beanHashCode()
ObjectBean.toString()
ToStringBean.toString()
BeanIntrospector.getPropertyDescriptors() ==> getter
<Templates>
Rome2
- DEP:rome:1.0
- IN:BadAttributeValueExpException.readObject()
- OUT:ToStringBean.toString()
- SINK:Templates
1
2
3
4
5
BadAttributeValueExpException.readObject()
ObjectBean.toString()
ToStringBean.toString()
BeanIntrospector.getPropertyDescriptors() ==> getter
<Templates>
Rome3
- DEP:rome:1.0
- IN:HashMap.readObject()
- OUT:EqualsBean.beanEquals()
- SINK:Templates
1
2
3
4
5
6
7
HashMap.readObject()
HashMap.putVal()
AbstractMap.equals()
EqualsBean.equals()
EqualsBean.beanEquals()
BeanIntrospector.getPropertyDescriptors() ==> getter
<Templates>
AspectJweaver利用链
前置知识
这条链前半部分依然是触发到LazyMap.get(),因此这部分可以用CC链中已有的内容随意改造。后半部分LazyMap.get()触发到的是一个位于org.aspectj.weaver.tools.cache
的特殊Map的put()方法,也就是静态内部类SimpleCache$StoreableCachingMap.put()
。这个内部类会继续调用writeToPath()
最终直接写入任意文件至任意路径。
AspectJweaver1
- DEP:aspectjweaver:1.9.2&&commons-collections:3.2.2
- IN:HashSet.readObject()
- OUT:LazyMap.get()
- SINK:FileOutputStream.write()
1
2
3
4
5
6
7
8
9
HashSet.readObject()
HashMap.put()
HashMap.hash()
TiedMapEntry.hashCode()
TiedMapEntry.getValue()
LazyMap.get()
SimpleCache$StorableCachingMap.put()
SimpleCache$StorableCachingMap.writeToPath()
FileOutputStream.write()
C3P0利用链
前置知识
观察类com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase
的序列化与反序列化过程。
PoolBackedDataSourceBase.writeObject()
如果捕捉到NotSerializableException
异常就会进入下方红框的处理逻辑,indirectForm(this.connectionPoolDataSource)
返回了ReferenceSerialized
对象:
因此序列化的过程可以将connectionPoolDataSource转换为ReferenceSerialized并写入ObjectOutputStream。
PoolBackedDataSourceBase.readObject()
首先会调用getObject,也就是我们写入的ReferenceSerialized.getObject()
。
然后直接调用到InitialContext.lookup(this.contextName)
。
C3P0-1
- DEP:c3p0:0.9.5.2 && mchange-commons-java:0.2.11
- IN:PoolBackedDataSourceBase.readObject()
- OUT:ReferenceSerialized.getObject()
- SINK:RegistryContext.lookup()
1
2
3
PoolBackedDataSourceBase.readObject()
ReferenceIndirector$ReferenceSerialized.getObject()
RegistryContext.lookup()