一、前言
本次攻防演习期间,有人发现 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”。
来源:freebuf.com 2021-04-27 18:35:04 by: 奇安信代码卫士
请登录后发表评论
注册