Smi1e@Pentes7eam
近日,Oracle官方发布安全更新,其中包含Weblogic的安全补丁,本着技术研究角度,和大家分享、探讨其中的一个JNDI注入漏洞。
在此前的CVE-2020-2883 将 extract 方法存在危险操作的 MvelExtractor 和 ReflectionExtractor 两个类加入到了黑名单中,因此我们只需要继续找一个 extract 方法存在危险操作的类即可绕过补丁,这里找到的是 Weblogic 12.2.1.4.0 Coherence 组件特有的类 com.tangosol.util.extractor.UniversalExtractor,因此只能影响 Weblogic 12.2.1.4.x。
影响范围
Oracle WebLogic Server 12.2.1.4.0
漏洞报告
入口同 CVE-2020-2883,利用 java.util.PriorityQueue 反序列化时会间接调用 com.tangosol.util.ValueExtractor 接口任意实现类的 extract 方法。
在 CVE-2020-2883 中我们可以使用 com.tangosol.coherence.rest.util.extractor.MvelExtractor 的 extract 方法执行mvel表达式或者使用 com.tangosol.util.extractor.ReflectionExtractor 的 extract 方法反射调用任意方法。但是补丁把他们都加入到了黑名单中,因此我们需要再找一个 extract 方法有危险操作且实现了 com.tangosol.util.ValueExtractor 接口的类。
我这里找到的是 com.tangosol.util.extractor.UniversalExtractor
public class UniversalExtractor<T, E> extends AbstractExtractor<T, E> implements ValueExtractor<T, E>, ExternalizableLite, PortableObject {
public static final String[] BEAN_ACCESSOR_PREFIXES;
public static final String METHOD_SUFFIX = "()";
@JsonbProperty("name")
protected String m_sName;
@JsonbProperty("params")
protected Object[] m_aoParam;
protected transient String m_sNameCanon;
private transient TargetReflectionDescriptor m_cacheTarget;
private transient boolean m_fMethod;
......
}
看其 extract 方法,虽然if条件中有一个 invoke 操作,但是 this.m_cacheTarget 使用了 transient 修饰导致无法被序列化。
跟进else条件中的 extractComplex 方法,该方法中也有一个 invoke 操作,虽然我们可以控制参数 oTarget 和 aoParam ,但是 method 对象的获取过程也有一个if条件。
ClassHelper.findMethod 方法通过类、方法名以及方法的参数类型数组来反射返回该类中的特定方法。因此只要我们进入else条件中,即可调用任意类的任意方法。
进入else条件的前提是 this.isPropertyExtractor() 返回false,也就是 this.m_fMethod 为true,但是该成员变量依然使用 transient 修饰,无法序列化,因此我们只能寄希望于if条件中。
public boolean isPropertyExtractor() {
return !this.m_fMethod;
}
看下if条件
protected E extractComplex(T oTarget) throws InvocationTargetException, IllegalAccessException {
......
String sCName = this.getCanonicalName();
boolean fProperty = this.isPropertyExtractor();
Method method = null;
if (fProperty) {
String sBeanAttribute = Character.toUpperCase(sCName.charAt(0)) + sCName.substring(1);
for(int cchPrefix = 0; cchPrefix < BEAN_ACCESSOR_PREFIXES.length && method == null; ++cchPrefix) {
method = ClassHelper.findMethod(clzTarget, BEAN_ACCESSOR_PREFIXES[cchPrefix] + sBeanAttribute, clzParam, false);
}else{
......
}
}
跟进 sCName 的赋值过程 this.getCanonicalName() ,因为this对象的原因,Lambdas.getValueExtractorCanonicalName 无论如何都会返回null。
接着看下 CanonicalNames.computeValueExtractorCanonicalName ,如果 aoParam 不为 null 且数组长度大于0就会返回 null ,因此我们可调用的方法必须是无参的。接着如果方法名 sName 不以 () 结尾,则直接返回方法名。否则会判断方法名是否以 VALUE_EXTRACTOR_BEAN_ACCESSOR_PREFIXES 数组中的前缀开头,是的话就会截取掉并返回。
回到 extractComplex 方法中,在if条件里会对上面返回的方法名做首字母大写处理,然后拼接 BEAN_ACCESSOR_PREFIXES 数组中的前缀判断 clzTarget 类中是否含有拼接后的方法,此时会发现我们现在无论如何只能调用任意类中 get 和 is 开头的方法,并且是无参的,条件有些苛刻。
不过仔细观察发现 UniversalExtractor#extract 方法可以调用两次,我们可以利用第一次调用改变 UniversalExtractor 对象的关键成员变量值,在第二次调用时完成利用。
接下来我想了三个思路:
- 直接去找所有类中 get 和 is 开头并且可利用的无参方法
- 想办法调用 init 方法对 this.m_fMethod 进行赋值,从而令 fProperty 的值为false并进入else条件中。
extractComplex 方法中对 this.m_cacheTarget 进行了赋值,因此我们第二次调用 UniversalExtractor#extract 方法时可以进入到if条件中执行 targetPrev.getMethod().invoke() 。
由于我们只能调用 get 和 is 开头的方法,因此思路2不行。由于我们在利用 targetPrev.getMethod().invoke() 调用任意方法时,传入的参数和 extractComplex 方法中 findMethod 的参数是同一个,导致如果我们调用非 get 和 is 开头的方法时,findMethod 会返回 null ,从而在 method.invoke 时会报错,这样的话就无法第二次调用 UniversalExtractor#extract 方法,因此思路3也不行。
最终只能用思路1去全局搜 get 和 is 开头且存在危险操作的无参方法。虽然只能调用无参方法,但是我们可以通过序列化控制对象的成员变量来获取一些可控点,当然该方法所在的类必须是可序列化的,另外该方法的危险操作涉及到的对象依然需要可序列化,否则无法利用。
这个寻找过程和fastjson有些相似,最终找到了 com.sun.rowset.JdbcRowSetImpl 这个jdk内置类,看下其 getDatabaseMetaData() 方法。
跟进 this.connect() ,可以发现只要 this.getDataSourceName() 可控,我们就能进行JNDI注入。
getDataSourceName() 调用的是父类 javax.sql.rowset.BaseRowSet 的,其 dataSource 属性是可序列化的,因此我们可以控制 this.getDataSourceName() 的返回值。
private String dataSource;
漏洞证明
由于Weblogic在JEP290机制下做的是全局反序列化过滤,而JNDI在jdk高版本依赖于本地Class的反序列化链,因此在Weblogic中JNDI注入只能打 JDK6u211、7u201、8u191 之前的版本。
补丁修复方式
分析Weblogic补丁p31537019_122140_Generic.zip
看到最新补丁的黑名单中不仅过滤了 com.tangosol.util.extractor.UniversalExtractor ,还过滤了 com.tangosol.util.extractor 包下的其他几个类,另外也把com.tangosol.coherence.rest.util.extractor、com.tangosol.internal.util.invoke.lambda 等一些包加入了黑名单中。