JavaStruts2系列-S2-001
0x01 S2-001漏洞复现
漏洞影响范围
WebWork 2.1 (with altSyntax enabled)
WebWork 2.2.0 - WebWork 2.2.5
Struts 2.0.0 - Struts 2.0.8
Struts2对 OGNL 表达式的解析使用了开源组件opensymphony.xwork. 2.0.3
所以会有漏洞
流程分析
在org.apache.struts2.dispatcher.FilterDispatcher
下,在这一个类的doFilter()
方法中做了如下业务
设置编码和本地化信息
创建ActionContext
对象
分配当前线程的分发器
将request对象进行封装
获取 ActionMapping 对象,ActionMapping 对象对应一个 action 详细配置信息
执行 Action 请求,也就是 172 行的serviceAction()
方法
先在 Filter 的doFilter()
方法下个断点,开始调试
首先获取当前请求是否已经有ValueStack
对象,这样做的目的是在接收到 chain 跳转方式的请求时,可以直接接管上次请求的 action。如果没有 ValueStack 对象,获取当前线程的 ActionContext 对象;如果有 ValueStack 对象,将事先处理好的请求中的参数 put 到 ValueStack 中,获取 ActionMapping 中配置的 namespace,name,method 值
在187
行这里,跟进serviceAction()
方法
通过ActionProxyFactory
的createActionProxy()
类初始化一个ActionProxy
,在这过程中也会创建StrutsActionProxy
的实例。StrutsActionProxy
是继承自com.opensymphony.xwork2.DefaultActionProxy
的,在这个代理对象内部实际上就持有了DefaultActionInvocation
的一个实例
DefaultActionInvocation
对象中保存了Action
调用过程中需要的一切信息,继续往下走,跟进proxy.execute()
获取到了上下文环境,并且调用setter
方式赋值上下文,接着继续跟进invoke()
方法
在invoke()
方法中,首先会顺序的递归执行当前Action
中所配置的所有的拦截器,知道拦截器遍历完毕调用真正的Action
,此处是一个interceptor
迭代器再进行遍历操作,对应遍历的内容是struts2
包内的struts-default.xml
里面interceptors
标签中的内容
在众多迭代器中,param
这一个迭代器使用来处理我们输入的参数的,所以会想到,会不会对应的迭代器里就有所谓的 OGNL 表达式的处理呢?跟进去查看一下
这段的大致意思就是,最开始在登陆框中的username
和password
会被保存到stack 里面,通过ActionContext.getParameters()
方法将stack
里面的值拿出来,再通过ValueStack.setValue(String,Object)
方法把值set
进去
接着在后文,它表明了这个类能够处理 OGNL 表达式,并且已经考虑了安全性问题
而此处的迭代器调用栈如下
|
|
在一个类中进行了对应的迭代器判断
一直到迭代器为params
为止,跟进。跟进之后会到ParameterInterceptor.doIntercept()
方法处
跟进到setParameters()
方法里面,再从这里面进setValue()
方法,为什么要跟进setValue()
方法?因为上面的注释中写到了,OGNL 的语句会被送到OgnlValueStack#setValue
处进行处理
一直跟进
经历了一系列的迭代器之后,所有迭代器处理完毕了,执行了invokeActionOnly()
方法
通过反射调用执行了action
实现类里的execute
方法,开始处理用户的逻辑信息。
处理完毕用户的逻辑信息之后,我们继续往下走,跟进executeResult()
首先createResult()
这里创建了一个Result
对象,对应的方法com.opensymphony.xwork2.DefaultActionInvocation#createResult
如果当时调用了Action
返回了Result
对象,则直接返回;否则,通过proxy
对象获取配置信息,根据 resultCode 获取到Result
对象。
继续往下走,executeResult()
方法中调用了execute()
方法;跟进doExecute()
准备执行环境: request, pageContext
等等后,发送真正的响应信息,可以看到我们自己配置时候返回结果是 jsp
文件
之后调用了JspServlet
来处理请求,在解析标签的时候,在标签的开始和结束位置,会分别调用对应实现类如org.apache.struts2.views.jsp.ComponentTagSupport
中的doStartTag()
及doEndTag()
,这里会调用组件org.apache.struts2.components.UIBean
的end()
方法
跟进evaluateParams()
方法
由于altSyntax
默认开启了,接下来会调用findValue()
方法寻找参数值
继续跟进translateVariables()
方法
最终触发点是TextParseUtil#translateVariables
,在此处下了断点之后,可以看到依次进入了好几次,不同时候的expression
的值都会有所不同,我们找到值为password
时开始分析。
经过两次如下的代码之后,将生成 OGNL 表达式,返回了%{password}
|
|
然后这次判断不会直接走到return
,来到后面,取出%{password}
中间的值password
赋值给var
然后通过Object o = stack.findValue(var,asType)
获得到password
的值为%{1+1}
然后重新赋值给 expression,进行下一次循环
在这一次循环的时候,就再次解析了%{1+1}
这个OGNL
表达式,并将其赋值给了o
最后expression
的值就变成了2,不是 OGNL 表达式时就会直接进入
|
|
最后返回并显示在表达那种,这是含有 OGNL 表达式的处理,比正常的处理多了OGNL这一部分,所以 Struts2 的运行流程到此结束。
漏洞利用
exp
|
|
或者
|
|