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
7PriorityQueue.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
7PriorityQueue.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
8PriorityQueue.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
8HashMap.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
5BadAttributeValueExpException.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
7HashMap.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
9HashSet.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
3PoolBackedDataSourceBase.readObject()
ReferenceIndirector$ReferenceSerialized.getObject()
RegistryContext.lookup()