2018-12-21 15:14:1721857人阅读
漏洞简介
JBoss RichFaces 3.1.0 through 3.3.4 allows unauthenticated remote attackers to inject expression language (EL) expressions and execute arbitrary Java code via a /DATA/ substring in a path with an org.richfaces.renderkit.html.Paint2DResource$ImageData object, aka RF-14310.
利用流程分析
首先理一遍利用流程(3.3.4版本,相关分析文章在:https://xz.aliyun.com/t/3264)
1. readObject 反序列化入口点,进行了白名单限制,除了一些基本类型,仅仅允许以下类型及其子类反序列化
2. 反序列化完成后,再进行 FacesContext 的注册操作,随后进行 restoreState 操作得到一个 MethodBinding
3. 进行 MethodBinding.invoke 的调用,MethodBinding 就和他名字一样,一般实现类中都会有绑定一个 MethodExpression,MethodBinding.invoke 实际上调用的是 MethodExpression.invoke
绕过一个修复方式
在授权测试相关网站时候,发现直接使用 RF4310 打过去报错如下:
java.lang.IllegalArgumentException: Expression contains parentheses
表达式中包含有括号,稍微思考了下,如果不允许el表达式中有括号的话,几乎没什么危害,只能调用一点无参函数,所以我们不在 el 表达式上进行绕过。查看了下 richfaces4 相关的绕过办法是 CVE-2018-12532 ,它主要是利用了白名单中的 VariableMapper 进行绕过对 MethodExpression 中 el 表达式的检查,这里涉及到 el 表达式的实现机制,不做深入分析,因为在 richfaces3.x 中,这个白名单根本没有 VariableMapper ...
仔细考虑整个利用过程,分三层
1. readObject 反序列化层
2. restoreState 恢复数据层
3. invoke el表达式执行层
黑盒思路,首先他服务器肯定拿到了我们的el表达式字符串,在看了各个 MethodBinding 和 MethodExpression 后,可以确定,能拿到 el 表达式字符串的只有一个函数:getExpressionString ,而这恰好和 cve-2015-0279 的richfaces4.x反序列化的修补吻合,猜测用了类似的手法进行修补
漏洞报告:https://issues.jboss.org/browse/RF-13977
官方补丁:https://github.com/richfaces/richfaces/commit/4c5ddae4d6ddcea591fa949762c1c79ac11cac99
如上图,expr 变量就是 contentProducer 中的 el 表达式字符串,对他做了正则检测是否含有括号...这样的话,就没法直接通过注入 el 表达式造成 rce 了,想办法能不能绕过?
在上文中讲了整个利用流程分三层,每一层的处理方式都不相同,我们一一开始尝试
第一层:
那么问题来了,如果从反序列化层去进行触发漏洞的话,相当于自己重新从0开始找一条完整的触发链,太麻烦了,更主要的是还没有思路,因为richfaces3.3.4在处理反序列化的时候,使用的是白名单校验,也就那几种类型可以正常通过,基本上构不成什么触发链
其二:
restoreState 我翻了下所有实现类,基本上都是简单的赋值操作,几乎没有函数调用(当然里面有进行递归式的 restoreState 调用),不过在 javax.faces.componenet.StateHolderSaver 中的 restore 函数里发现,似乎能对一些类通过 ClassLoader 进行加载操作,也在一些实现了 restore 函数相关的类中发现了近似的情况,但是他们的 ClassLoader 是被写死了的,无法利用。那么这一层就放弃不研究,继续向下
第三层:
Invoke ,首先在 richfaces3.4 里,Paint2DResources 里调用 invoke 的是 MethodBinding ,同时猜测获取 el 表达式的也是 MethodBinding,那么这里会不会出现一个数据不对等的情况呢???
如果,我通过 getExpressionString 获取到的 el 和 invoke 时候执行的 el 不对等,那不是就达到了绕过的效果?
那么这里从设计层面出发,作为框架设计者,要考虑到整个框架的健壮性,是的,错误机制处理问题
如果我在 invoke 执行的时候,对于原始目标执行错误,抛出了异常怎么办?
如果健壮性够好,应该考虑到各种各样的情况,执行单一目标的健壮性肯定不够好,那么一般都会在错误处理机制中进行优化,或处理一些目标之外的情况,或更人性化的报错之类
此外,再通过浏览源码发现,MethodBinding 和 MethodExpression 的实现类中,存在一个互相包容的情况,简单来说就是,可以通过 MethodBinding 调用 MethodExpression,也可以通过 MethodExpression 调用 MethodBinding ,那么这进一步增加了攻击利用的可能性,至于为啥后文会讲到
Richfaces3.x中 MethodBinding 的实现类并没有出现之前所说的那种信息不对等的情况,那么中转一下,看看 MethodExpression 的实现类,我发现了:
org.jboss.seam.el.OptionalParameterMethodExpression
它有两个类型为 MethodExpression 的属性,分别是 withParam 和 withNoParam ,在 getExpressionString 的时候,使用的是 withParam ,在 invoke 的时候,先使用 withParam 抛错后再使用一次 withNoParam
OptionalParameterMethodExpression#nvoke函数:
OptionalParameterMethodExpression#getExpressionString函数:
我们可以通过构造,让 withParam 的 el 表达式通过检查,然后在 withNoParam 中加入真正的 el 利用语句
我简单搜索了下,需要抛错的办法就是加空格,比如 #{request.getClass }
这样就不带有括号了,并且能够成功触发 el 的利用
但是这个的父类是 MethodExpression ,而我们的利用类却是 MethodBinding 的,就无法直接利用,需要使用一个 MethodBinding 的实现类对 OptionalParameterMethodExpression 进行一个包装,在 invoke 还有 getExpressionString 的时候真正调用的还是 OptionalParameterMethodExpression.invoke() 和 OptionalParameterMethodExpression.getExpressionString()。我选的是一个简单的 MethodBinding 实现类: com.sun.facelets.el.LegacyMethodBinding
绕过结果
不过可惜的是,服务器似乎不认账,在 fuzz 它到底拦截了啥字符串的时候发现……什么都拦截233333