前言

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利用过程了,重点看下以下几个问题

疑问

  1. 上图为Hashset put的源码,为了要执行euqals,就要保证map的两个key hash一致,且两个key不想等,key分别为templates和proxy,是两个不同的对象,两个hash是如何保持一致的?
  2. hash为0的字符串的作用是啥?
  3. 为什么要用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 中增加了对 传入对象可执行方法对判断,不能像过去一样执行任意方法:

公众号

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

最新文章

  1. 变通实现微服务的per request以提高IO效率(二)
  2. 自定义RadioButton样式
  3. Hadoop中Combiner的使用
  4. memcache命中统计
  5. javascript_core_04之数组API
  6. javascript学习笔记1-document.write
  7. linux screen 命令详解[转]
  8. Windows下使用Redis(一)安装使用
  9. UESTC 878 温泉旅馆 --性质+枚举
  10. form表单用ge方式提交时ie显示中文参数乱码
  11. [实变函数]5.3 非负可测函数的 Lebesgue 积分
  12. linux嵌入式系统驱动程序的阻塞与异步
  13. CentOS7配置VNC Server
  14. 程序员买房指南——LZ的三次买房和一次卖房经历
  15. 《转》完美解决微信video视频隐藏控件和内联播放问题
  16. PyQuery库
  17. MyEclipse 修改项目部署路径
  18. Jumpserver双机高可用环境部署笔记
  19. Linux 重启网卡失败 Job for network.service failed because the control process exited with error code. See &quot;systemctl status network.service&quot; and &quot;journalctl -xe&quot; for details.
  20. 华为笔试——C++字符串四则运算的实现

热门文章

  1. git忽略文件夹提交以及gitignore修改后不生效的解决办法
  2. 10、二进制安装K8s之部署CoreDNS 和Dashboard
  3. leaflet 动态线渲染
  4. Java静态内部类——重点是精妙的demo
  5. JFrame显示刷新
  6. 《redis 5设计与源码分析》:第二章 简单动态字符串
  7. tensorflow models flags 初步使用
  8. 笔记:如何使用postgresql做顺序扣减库存
  9. 安全强化机制——SELinux
  10. Linux档案权限篇之一