CVE-2020-7961:Liferay Portal 反序列化漏洞分析
2020-04-26 12:14

报告编号:B6-2020-042601

报告来源:360-CERT

报告作者:Hu3sky

更新日期:2020-04-26

0x01 漏洞背景

2020年03月20日,Code White发现了影响Liferay Portal版本6.1、6.2、7.0、7.1和7.2的多个关键级别的JSON反序列化漏洞。它们允许通过JSON web服务API执行未经身份验证的远程代码。修复的Liferay Portal版本有6.2 GA6、7.0 GA7、7.1 GA4和7.2 GA2。

Liferay(又称Liferay Portal)是一个开源门户项目,该项目包含了一个完整的J2EE应用,以创建Web站点、内部网,以此来向适当的客户群显示符合他们的文档和应用程序。

对此,360CERT建议广大用户及时安装最新补丁,做好资产自查/自检/预防工作,以免遭受黑客攻击。

0x02 影响版本

  • Liferay Portal: 6.1、6.2、7.0、7.1、7.2

0x03 漏洞详情

入口定位

首先对路由的调用进行分析,我们看到web.xmlurl-pattern/api/jsonws/*,对应的servlet-name如下。 然后看到servlet-name对应的servlet-class 我们在service函数进行下断。 首先获取path,为/api/jsonws/之后的部分,由于我们请求的不是单纯的/api/jsonws,所以进入最下面else的部分,跟入super.service,也就是JSONServlet.service,跟入_jsonAction.execute 调用getJson,即其子类JSONWebServiceServiceAction.getJson 跟入getJSONWebServiceAction 这里匹配到了invoke,即前面图中的第一种调用方式,return了一个实例化对象JSONWebServiceInvokerAction,传入request对象,继续跟入。 获取cmd参数,也就是api,左边的那一列名称。 这里我们api选择的是/expandocolumn/update-column 不为null,于是赋值给JSONWebServiceInvokerAction对象的_command,回到getJSON,调用JSONWebServiceInvokerAction.invoke 先调用JSONFactoryUtil.looseDeserialize处理_command 会先调用getJSONFactory,返回之前初始化过的JSONFactoryImpl 调用createJSONDeserializer返回JSONDeserializerImpl的实例。 调用parse解析_command

调用parseValue,之后就是jodd-json的解析器解析流程了。处理command成一个hashmap 回到invokethis._parseStatement->setMethod,将command设置为_method,存入statement 回到invoke,调用_executeStatement,传入statement JSONWebServiceActionsManagerUtil.getJSONWebServiceActioncollectAll函数里会把POST的参数赋值到jsonWebServiceActionParameters里,并存入上下文(com.liferay.portal.kernel.service.ServiceContext,里面有responserequest对象相关,可以用来做回显)。

Parameters key的处理细节

在collectAll里还有一个细节,就是对参数的处理,在_collectFromRequestParameters 这里会将keyvalue取出来put_jsonWebServiceActionParameters这个map里,不过在调用put函数时,还会进一步处理key 首先定位key里的:符号,然后去掉+/-符号,把:后面的部分赋值给typename,将key赋值成:符号前面的部分,这里就是defaultData 接着判断在key里是否有.符号,如果有.后面的将被赋值为innerName,当然我们这里没有.,接着执行HashMap.put

获取ActionConfig

之后调用_findJSONWebServiceAction,传入jsonWebServiceActionParameterspath,获取parameterNames,调用_getJSONWebServiceActionConfig 获取相关config 然后回到getJSONWebServiceAction,实例化JSONWebServiceActionImpl进行赋值。 回到_executeStatement,进入JSONWebServiceActionImpl.invokejsonRPCRequest变量为null,调用_invokeActionMethod 调用_prepareParameters方法。

defaultData的处理(关键部分)

跳过了中间的coulmnIdnametype,直接看到defalutDataparameterTypejava.lang.Object,而parameterTypeName是之前从key:后面部分提取出来的type,不为null,于是调用loadClass加载parameterTypeName 接着调用ReflectUtil.isTypeOf进行检测,检查指定的类型是否扩展要调用的方法的相应参数的类型,因为这里父类是java.lang.Object,一切类都继承了java.lang.Object类,这就然后我们可以通过parameterTypeName调用任意类。 接着是对value的处理,这里value有值,于是调用_convertValueToParameterValueparameterType进行类型判断,都不符合,于是进入else 调用_convertType,跟到TypeConverterManagerBean.convertType 会根据type去调用lookup函数寻找converter 如果寻找不到,进入下面的else,都不符合的话,那么就会抛出错误,找不到该converter 回到_convertType,进行catch,输入类型不为Map,继续返回。 _convertValueToParameterValue中进行catch 如果value是以{开头,就会进入下面的JSONFactoryUtil.looseDeserialize,传入parameterTypevalue jsonDeserializer.use函数会将我们的class赋值给rootType成员。 接着调用deserialize函数。 之后的内容就不深入分析了,大致列一下就行了。

类的实例化

类的实例化部分在jodd.json.JsonParserBase#newObjectInstancedeserialize开始的调用栈如下。

set方法调用

这里的setUserOverridesAsString方法是在我们传入的WrapperConnectionPoolDataSource方法的父类里进行调用的。 jodd.introspector.MethodDescriptor#invokeSetter deserialize开始的调用栈。

漏洞利用

出网回显

不出网回显

0x04 时间线

2020-03-20 漏洞细节被公开

2020-04-26 360-CERT 发布分析

0x05 参考链接

  1. Liferay Portal JSON Web Service RCE Vulnerabilities