freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

一步一步回顾分析攻防演习中的 WebLogic T3 反序列化 0day 漏洞
奇安信代码卫士 2021-04-27 18:35:04 337267

一、前言

本次攻防演习期间,有人发现 Weblogic T3反序列化0day漏洞。攻击者可在原Jdk7u21 POC 基础上,加上 java.rmi.MarshalledObject 绕过黑名单,达到入侵目的,具体如下。

二、Jdk7u21的PoC分析

gadget 如下:

1、分析第一部分利用链

Jdk7u21的第一部分利用链如下。

在对 LinkedHashSet 反序列化过程中,会进入 HashSet.readObject() 函数,关键代码块如下。

根据 POC 信息,可知该函数会依次读取 LinkedHashSet 的 templates 和proxy对象,并将它们加入 map中。为什么需要加载两个对象呢?继续进入map.put() 函数,如下。

该函数会计算存储进map的第一个对象Templates的Hash值,进入hash(key),如下。

程序会执行常规hash运算,注意,其中的 k.hashCode() 调用了系统函数;而如果构造动态代理,编写或寻找到合适代理类,则极有可能使多个存储进map的对象的hash值一致。返回至HashMap.put() 函数,继续分析,进入至indexFor(hash, table.length) 函数,如下。

table对象的原定义代码是table[bucketIndex] = new Entry<>(hash, key, value, e),负责保存 map 中每个对象及 hash 值等信息。此函数没有保存对象信息功能,仅仅负责统计返回当前对象在 table 中的索引值。随后返回至HashMap.put() 函数。

随后,如当前对象不在 table 中,则不进入循环,而是进入 addEntry(hash, key, value, i); 且在此期间进入父类方法,最终当前对象被保存至 table 中,关键代码如下。

保存完第一个对象后,程序返回到 HashSet.readObject() 方法,并在 map 中添加第二个对象。如第二个对象是代理对象,则其 hash 值可能与前一个对象的hash 值碰撞,从而有利于绕过代码的验证逻辑,便于未来开展入侵测试;如POC中存在第二个代理对象,则会继续进入 map.put()。

进入 hash(key),计算第二个代理对象的hash值,进入 HashMap.hash() 函数。

在 POC 中,第二个代理对象的代理类是 AnnotationInvocationHandler,因此执行 k.hashCode() 时,会首先进入 AnnotationInvocationHandler.invoke() 方法,如下。

根据方法名进入this.hashCodeImpl();,相关的代码块如下。

var3信息来自AnnotationInvocationHandler的memberValues成员变量:如var3 中只有一条map信息,且该map的key为"f5a5a608"而value是加入map的第一个对象,则经调试分析发现,此时会发生两个对象hash值碰撞的情况。返回至 HashMap.put() 继续分析。

在循环代码块中,存在关键比较逻辑,如下。

该逻辑首先会比较两个对象的hash值是否相等:根据 PoC 可知两值相等,并且会执行至 key.equals(k)。k是第一个对象,key是第二个代理对象,则会执行代理类invoke()方法。POC中的代理类是AnnotationInvocationHandler,则进入AnnotationInvocationHandler.invoke(),随后再进入this.equalsImpl(var3[0]),如下。

Var5表示代理类的第一个方法,var1 表示前述的第一个对象,type 成员变量必须是第一个成员变量。根据代码 var5.invoke(var1) 可知,此方法必须是无参的,而POC中代理类 AnnotationInvocationHandler 的第一个方法getOutputProperties() 符合要求,因此进入TemplatesImpl.getOutputProperties(),如下。

总体而言,第一部分链需同时满足如下关键条件:

LinkedHashSet 需要依次加入类对象和一个代理对象,设法使两个对象存在hash 碰撞情况;

代理对象的代理类 AnnotationInvocationHandler 的第二个成员变量仅存储一个 map 结构数据,key 为"f5a5a608",value 是第一个类对象;

代理类 AnnotationInvocationHandler 的 type 成员变量必须是第一个类对象可转化的类;以及

代理类的待利用方法需要是无参的。

2、分析第二部分利用链

Jdk7u21的第二部分利用链如下。

会在创建TransformerImpl 类过程中调用 getTransletInstance() 函数,函数信息如下。

从中可知 _name 变量不能为 null,否则程序中断;那么当 _class 变量为 null时,会调用defineTransletClasses()执行什么重要指令呢?进入defineTransletClasses(),如下。

由此可知在当前情况下,_bytecodes 不能为 null,否则程序报错终止;随后调用 new TransletClassLoader(ObjectFactory.findClassLoader()) 加载信息,但具体可以加载什么呢?继续分析进入下面代码块。

TransletClassLoader 继承自ClassLoader,主要功能包括根据字节码文件加载类的defineClass函数,因此这里成功创建并返回了TransletClassLoader类加载器。返回到defineTransletClasses()函数继续分析。

loader 即为创建的 TransletClassLoader 类加载器;_class 变量是自发创建的类数组,_auxClasses 变量是 hash 表;后续关键代码如下。

在循环代码块中,采用loader加载 _bytecodes[i],说明 _bytecodes 变量是一个包含多个类的字节码数组;随后会将 _bytecodes[i] 的父类与ABSTRACT_TRANSLET(ABSTRACT_TRANSLET="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet") 比较,相等则 _transletIndex 为i,否则将类信息保存至 _auxClasses 变量。循环完毕后,根据语句可知_transletIndex 不能小于0,否则程序报错中断。因此,_bytecodes 中至少有一个类的父类是 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet。最终返回至 getTransletInstance() 函数继续分析,如下。

_class 变量包含多个加载的类,如果_transletIndex 变量是这些类中父类为ABSTRACT_TRANSLET 的类索引号,则会顺利实例化此类对象;然而,如果在此类构造函数中加入恶意代码则达到恶意利用的目的。

一言以蔽之,攻击者可以构造一个TemplatesImpl类对象,关键要求:

其 _name 变量不能为 null;

_class=null;以及

_bytecodes 变量是字节码类数组,包含一个父类为com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet的恶意类。

当前,Jdk7u21 的 POC 符合要求。

三、MarshalledObject 分析

Weblogic 将上述调用链中的com.sun.org.apache.xalan.internal.xsltc.trax 加入黑名单后,库中的 TemplatesImpl 类等会被检测到,从而成功阻止 Jdk7u21的 PoC 遭入侵利用。新 0day 的 PoC主要应用了 MarshalledObject 类绕过黑名单的检测,利用过程和 Jdk7u21 PoC 类似:将 MarshalledObject 对象作为恶意类,在程序执行过程中调用到其 get() 方法,反序列化 objBytes 成员变量,而这一切只需预先在此成员变量中保存 Jdk7u21 PoC 序列化数据即可。

分析 MarshalledObject 类。符合要求的无参函数 get() 相关代码如下。

objBytes 变量不能为 null,程序可保存序列化后数据的 objBytes 变量 private byte[] objBytes = null; 至字节数组缓冲区bin变量,随后采用读入MarshalledObjectInputStream 对象,最后进入 in.reaObject() 方法。

因此,可将序列化恶意类对象保存至 objBytes 变量,当程序执行至 get() 方法时,即会调用 readObject() 解析执行恶意类对象。

四、POC构造

团队成员设计实现的 PoC 如下。

五、实验复现

在 JDK7u21、Weblogic12.1.3.0 上通过T3协议发送攻击脚本,成功在 tmp 文件夹创建文件,如下。

EXP 如下:

六、补丁分析

最新的补丁从侧面修复了这一漏洞。补丁没有检测拦截MashalledObject类,而是在wlclient.jar中加入拦截类FilteringObjectInputStream并设置了白名单,从而拦截包含恶意类的LinkedHashSet类型数据,如下。

白名单:

调用链:

可知,在 FilteringObjectInputStream.validateReturnType 方法处中断程序,此方法如下。

当输入的数据类型不在 expectedTypes 白名单列表中时,会抛出异常,并打印出当前输入数据类型不符的信息,如下。

题图:Pixabay License

转载请注明“转自奇安信代码卫士 https://codesafe.qianxin.com”。

# 漏洞分析 # weblogic # 攻防演习
本文为 奇安信代码卫士 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
奇安信代码卫士
奇安信代码卫士 LV.8
国内第一家专注于软件开发安全的产品 https://codesafe.qianxin.com
  • 275 文章数
  • 257 关注者
GitHub Actions 供应链攻击因受陷的 SpotBugs 令牌引起
2025-04-07
奇安信发布《2024中国软件供应链安全分析报告》
2024-09-09
存疑 CVE 漏洞带来无谓压力 热门开源项目开发者归档 GitHub 仓库
2024-07-05
文章目录