漏洞概要
可参考官方安全公告:https://cwiki.apache.org/confluence/display/WW/S2-007
漏洞分析
S2-007的利用场景比较苛刻,要求对提交的参数配置了验证规则并对提交的参数进行类型转换的时候会造成OGNL
表达式的执行。
这个漏洞的成因在于,在Struts2中,关于表单我们可以设置每个字段的规则验证,如果类型转换错误时,就会进行错误的字符串拼接,通过闭合引号导致OGNL
的语法解析。
简易POC
1 | '+(#application)+' |
在 Struts2 中,可以将 HTTP 请求数据注入到实际业务 Action 的属性中。而这些属性可以是任意类型的数据,通过 HTTP 只能获取到 String 类型数据,所以这里存在类型转换。我们可以通过 xml 文件,来定义转换规则。例如,我这里定义了一个 UserAction
类,其有一个 Integer
类型的 age
属性,这里我们让其数值范围在1-150
。
如果此时我们将 age
属性值设置成一个字符串,那么就会引发类型转换错误。Struts2 会将用户输入的数据经过处理再次返回给用户。
而在这个处理的过程中,就存在 OGNL
表达式注入,我们先在ConversionErrorInterceptor:intercept()
方法中打上断点(ConversionErrorInterceptor
类是专门用来处理类型转换失败的拦截器),当类型出现错误的时候,就会进入这里
当发生类型转换错误时,程序会将用户输入的值存入 fakie
变量。在存入之前,会先将值用 getOverrideExpr
方法处理,我们跟进该方法。
在 getOverrideExpr
方法中,会在用户输入的值两边拼接上单引号,然后再将值存入刚刚的 fakie
变量。这里把我们的payload用单引号阔起来了,这也就解释了为什么我们的payload是形如 ' + (*) + '
的形式,就是为了逃逸这个单引号。
接着程序会把fakie
变量通过setExprOverrides
将其放入OgnlValueStack.overrides
中
然后在解析到 Struts2的 />
标签时,会将用户输入值经过OGNL
执行并返回。如果先前 OgnlValueStack.overrides
存储过相关字段,则会先从OgnlValueStack.overrides
中取出相关值,然后再通过OGNL
执行。
1 | # 弹计算器 |
修复
使用 org.apache.commons.lang.StringEscapeUtils.escapeJava()
来做了一下escape,防止再从引号里面逃逸出来。