Red Hat JBoss EAP RichFaces 4.x RCE

 

前言

这是一个未公开的利用思路,仅仅做技术思路分享

在 jenkins 中我找到了绕过黑名单的方法,那么同样也有很多框架/程序用的是白名单,一定意义上说必须满足程序正常执行流程的一些类才能够通过白名单的限制,岂不是可以说白名单就绝对安全?

一般来说白名单的安全性大于黑名单的,不过我还是在 jboss 下对 jsf 的实现框架 richfaces 中学到了新姿势,在此基础上加以思考获得了一些新的操作

这篇文章主要分享白名单下反序列化的利用姿势和一些个人所想所得

( richfaces虽然早就不维护,但是在16年前后使用量还是不错的,也对实际网络环境做过简单测试,基本上无阻碍rce。在18年,PayPal 主站也因为 richfaces3.x 的 rce ,被拿下过 shell )

 

漏洞相关

先去看看 richfaces4.x 的反序列化漏洞相关

CVE-2015-0279:

漏洞详细:https://issues.jboss.org/browse/RF-13977

补丁diff:https://github.com/richfaces/richfaces/commit/4c5ddae4d6ddcea591fa949762c1c79ac11cac99

仔细查阅资料的话,会发现这个 cve 其实就是通过反序列化生成 el 相关,然后执行的 el 表达式,因为设计需要,所以刚好 el 相关的类是被写入了白名单的,就是一个 反序列化 + el 表达式执行的好姿势

官方的缓解措施也比较简单粗暴,你既然要执行 el ,那么我拦截恶意注入的 el 表达式不就好了嘛,直接拦截左括号 ( ,确实要是没有括号,就不能执行复杂的 el 表达式了,只能执行 foo.test 之类的,获取对象的属性值

我对 反序列化 + el 的这种姿势也是分析过,不过版本是 richfaces3.x 的,文章在:https://xz.aliyun.com/t/3264

CVE-2018-12532:

漏洞详细:https://codewhitesec.blogspot.com/2018/05/poor-richfaces.html

莫得补丁,因为 richfaces 项目官方不维护了

这个 cve 其实就是文章中所说的 RF-14309 ,然后思路很厉害,但是限制条件比较大,因为利用链中的类是在 4.5.3 版本开始才加入白名单的,也就是说更早的版本根本没法打

主要思路就是,作者对 el 解析相关了解的很深,之前的 cve-2015-0279 已经对 el 表达式进行了拦截,只能调用 foo.test 这类表达式。但是 el 设计中有一个 ValueMapper ,它的作用就是在解析 foo.test 的值的时候做一定的相关操作,在操作流程中可以执行 Mapper 中所指定的 el 表达式,注意这是两层,ValueMapper 和 MethodExpression 是分开的,cve-2015-0279 拦截的是 MethodExpression 中的 el 表达式,不会拦截 ValueMapper 中的表达式,所以成功在白名单+字符串黑名单拦截下拿到了 rce

Richfaces4.x 反序列化相关就了解的这么多,接下来去看看它的反序列化白名单

 

反序列化白名单

whitelist = com.sun.el.lang.VariableMapperImpl,
            javax.el.Expression,
            javax.faces.application.Resource,
            javax.faces.component.StateHolderSaver,
            javax.faces.view.Location,
            org.apache.el.lang.VariableMapperImpl,
            org.jboss.el.lang.VariableMapperImpl,
            org.jboss.el.MethodExpressionImpl,
            org.jboss.weld.bean.proxy.util.SerializableClientProxy,
            org.jboss.weld.bean.StringBeanIdentifier,
            org.richfaces.resource.SerializableResource

从命名就能看出基本上都是和资源、视图、el表达式相关的反序列化类

要不是 el 能够执行代码的话,想要拿到 rce 是非常困难的,这个时候我对 el 相关并不了解,甚至 ValueMapper 这些功能都没听说过,我自己的思路就是,能不能找到一个点,它在 cve-2015-0279 那样的 getExpressionString 调用返回的 el 表达式是满足要求的,但是又在解析 el 的时候,用的其他的表达式呢?

简单来说就是

getExpression -> normal-EL
  invokeExpression -> evil-EL

 

容错机制给机会

本来抱着找着试试看的心态去挖漏洞的,没想到在查看最后一个符合白名单的类的时候,就找到了符合心里所期望的那种解析流程,在总结过程中发现还是归功于程序设计的容错小机制

com.sun.faces.facelets.el.ContextualCompositeMethodExpression

这个类是继承于 MethodExpression 的,所以满足原有的触发流程

之前一直没放过图,我们还是先看看 richfaces4.x 的最终触发流程,如下图:

稍微解释一下,上图是最后 el 表达式执行的地方,成员变量 contentProducerexpiresExpression 都是反序列化得到的类,也就是说用户可控的,在 encode 函数中,先对 contentProducer.getExpressionString 做提取 el 表达式过滤操作,如果没有左括号 ( ,就调用 contentProducer.invoke ,我对 el 了解不深,只知道 getValueinvoke 等函数可以触发 MethodExpression 的 el 表达式执行操作

现在去看看 ContextualCompositeMethodExpression 中的 getExpressionString 函数流程:

public String getExpressionString() {
    return this.delegate.getExpressionString();
}

先不着急知道 delegate 是啥,反正此时 getExpressionString 是对 delegate 操作的,再去看看 ContextualCompositeMethodExpression 中的 invoke 函数流程:

上图中第一个框,确实是操作的 delegate ,调用他的 invoke 函数,但是我们稍微细心一点会发现在后续的抛错处理中,再次调用了 getValueinvoke 等操作。稍微梳理一下这个流程,如果 delegate 的 invoke 调用过程抛错,那么在 catch 代码块中,满足 source 成员变量不为 null 并且该异常是 MethodNotFoundException 的对象的话,就能执行 source.getValue

我们看看 source 和 delegate 分别是啥:

delegate 又是一个 MethodExpression ,而 source 是一个 ValueExpression 的,ValueExpression 的 getValue 函数也能执行 el 表达式,所以我们就已经绕过了这个限制

简单梳理一下:

用户发出恶意请求
->
contentProducer.getExpressionString
->
ContextualCompositeMethodExpression.delegate.getExpressionString
->
Delegate 中不含有 ( ,bypass
->
contentProducer.invoke
->
ContextualCompositeMethodExpression.delegate.invoke
->
执行过程中发生 MethodNotFoundException 异常,进入 catch 代码块
->
ContextualCompositeMethodExpression.source.getValue
->
El 表达式执行,造成 rce

流程大致如上所述,更前端的我就不写了,比如像路由控制,参数控制,反序列化流程处理等

目前我们还是面临一个问题,MethodNotFoundException 如何触发呢?

我们需要满足 delegate 中不带有 ( ,还要触发 MethodNotFoundException 才行

经过 google ,发现只要类似 ${request.getClass } 形式的表达式就能够引起解析错误,大致是解析的 bug ,当调用函数的时候,不带括号并且结尾多一个空格就会引发那个异常

 

效果

说了那么多,稍微展示一下

(tomcat7.0.65、richfaces-showcase4.3.7)

本地初始化环境的时候,先要访问一下相关功能,如下图:

然后就直接发送 payload

Ps:

构造 el 的过程异常艰辛23333,发现有诸多限制,比如 el 的底层实现就有 jboss 、tomcat、 glassfish 等,再说 jsf 的实现也有 sun 、myfaces、facelets ,里面涉及到 el 的解析方式不同,接口类的 serialVersionUID 不同等问题233333

经过长时间的研究,jdk、web容器、jsf依赖相关的问题一一解决了,在实验实际环境的时候,稍微测试下基本还是能 rce 的

测试的思路见:https://tint0.com/when-el-injection-meets-java-deserialization/

也有相当nice的翻译:https://www.anquanke.com/post/id/160338

 

总结

首先这个方法可以在 richfaces4.x 所有版本通用,而 cve-2018-12532 是需要 4.5.3 版本以上才行的

回头想了想,ContextualCompositeMethodExpression 中 invoke 函数的 catch 代码块,其实更主要的功能是为了在 delegate 成员执行失败的情况下,计算 source 中 el 表示的值,最后根据其值再次调用 invoke 函数,简言之就是一个小小的容错处理,delegate 和 source 两个不同类型的双保障让正常流程能够尽力的解析完成

并没有对 el 整个设计有很深入的了解,不像 cve-2018-12532 那样有很高的技术含量,这仅仅是从设计思路触发挖掘而已,我理解的是 getExpressionString 和 invoke 之间看似是对同一个 MethodExpression 对象操作,但是在框架设计中,肯定会有一些中转操作,一些容错处理,毕竟是底层解析,它需要尽可能的为各种情况作出合理的解析和判断。看似严防死守的白名单机制,在这种设计下给了我们更多的可利用空间

(完)