Apache Struts2 作为世界上最流行的 Java Web 服务器框架之一,3 月 7 日带来了本年度第一个高危漏洞——CVE编号 CVE-2017-5638 。其原因是由于 Apache Struts2 的 Jakarta Multipart parser 插件存在远程代码执行漏洞,攻击者可以在使用该插件上传文件时,修改 HTTP 请求头中的 Content-Type 值来触发该漏洞,导致远程执行代码。
哪些网站已中招
Struts 作为一个“世界级”开源架构,它的一个高危漏洞危害有多大,下面两张图可以让大家对这个漏洞的影响范围有一个直观认识。
【全球互联网上开放的Apache Struts分布】
【中国互联网上开放的Apache Struts分布】
雷锋网从绿盟科技了解到,从 3 月 7 日漏洞曝出到 3 月 9 日不到 36 个小时的时间里,大量用户第一时间通过绿盟云的 Structs2 紧急漏洞检测服务对自己的网站进行检测,共计 22000 余次。
通过对这些数据进行分析,可以看到:
1、从检测数据来看,教育行业受Struts2漏洞影响最多,其次是政府、金融、互联网、通信等行业。
绿盟科技威胁情报中心( NTI ) 通过对检测出漏洞的页面逐一访问,去掉一些无法访问的页面后,按照行业进行了分类,其中,教育行业数量最多占23%,其次是政府占19%,金融占17%,互联网占10%,通信行业占3%以及其他行业领域占27%。
2、从地域来看,北、上、广、沿海城市等经济发达地区成为 Struts2 漏洞高发区,与此同时修复情况也最及时。
从检测页面的地域分布上看,北京最积极占22%,其次是广东9.8%,浙江8.2%,上海7.8%,福建4.9%。从最终的检测结果来看,这也符合“多检多得”的排序,北京检出漏洞页面数量最多占23.6%(符合首都政治教育中心的定位),广东13.7%,浙江10.4%,上海7.9%,福建7.3%。
3、从应对漏洞积极性来说,金融、政府、教育位列前三甲。
雷锋网了解到,应对本次 Struts2 漏洞,金融行业应急反应最为迅速,在漏洞爆发后采取行动也是最迅速的,无论是自行升级漏洞软件还是联系厂商升级防护设备都走在其他行业前列,很多金融行业站点在几个小时之内再次扫描时已经将漏洞修补完成。
检测与修复方案
如果设备已经检测出存在Struts2漏洞,绿盟科技提出了以下三种解决方式:
1.官方解决方案
官方已经发布版本更新,尽快升级到不受影响的版本(Struts 2.3.32或Struts 2.5.10.1),建议在升级前做好数据备份。
Struts 2.3.32 下载地址:https://cwiki.apache.org/confluence/display/WW/Version+Notes+2.3.32
Struts 2.5.10.1下载地址:
https://cwiki.apache.org/confluence/display/WW/Version+Notes+2.5.10.1
2. 临时修复方案
在用户不便进行升级的情况下,作为临时的解决方案,用户可以进行以下操作来规避风险:
在WEB-INF/classes目录下的struts.xml 中的struts 标签下添加<constant name=”struts.custom.i18n.resources” value=”global” />;在WEB-INF/classes/ 目录下添加 global.properties,文件内容如下:
struts.messages.upload.error.InvalidContentTypeException=1。
配置过滤器过滤Content-Type的内容,在web应用的web.xml中配置过滤器,在过滤器中对Content-Type内容的合法性进行检测:
3.技术解决方案
对于没有网络防护设备的企业,可以使用专业厂商的防护设备进行防护;或者使用专业安全厂商的针对性安全服务对已有业务进行漏洞排查和修复。正在使用安全防护设备的企业,目前各大安全厂商都已经推出针对该漏洞的紧急升级包,请及时升级已有防护设备的防护规则和检测规则。
1、strurts漏洞原理
Struts2的核心是使用的webwork框架,处理 action时通过调用底层的getter/setter方法来处理http的参数,它将每个http参数声明为一个ONGL(这里是ONGL的介绍)语句。当我们提交一个http参数:
?user.address.city=Bishkek&user[‘favoriteDrink‘]=kumys
ONGL将它转换为:
action.getUser().getAddress().setCity("Bishkek")
action.getUser().setFavoriteDrink("kumys")
这是通过ParametersInterceptor(参数过滤器)来执行的,使用用户提供的HTTP参数调用 ValueStack.setValue()。
为了防范篡改服务器端对象,XWork的ParametersInterceptor不允许参数名中出现“#”字符,但如果使用了Java的 unicode字符串表示\u0023,攻击者就可以绕过保护,修改保护Java方式执行的值:
此处代码有破坏性,请在测试环境执行,严禁用此种方法进行恶意攻击
?(‘\u0023_memberAccess[\‘allowStaticMethodAccess\‘]‘)(meh)=true&(aaa)((‘\u0023context[\‘xwork.MethodAccessor.denyMethodExecution\‘]\u003d\u0023foo‘)(\u0023foo\u003dnew%20java.lang.Boolean("false")))&(asdf)((‘\u0023rt.exit(1)‘)(\u0023rt\[email protected]@getRuntime()))=1
转义后是这样:
?(‘#_memberAccess[‘allowStaticMethodAccess‘]‘)(meh)=true&(aaa)((‘#context[‘xwork.MethodAccessor.denyMethodExecution‘]=#foo‘)(#foo=new%20java.lang.Boolean("false")))&(asdf)((‘#rt.exit(1)‘)(#[email protected]@getRuntime()))=1
OGNL处理时最终的结果就是
java.lang.Runtime.getRuntime().exit(1); //关闭程序,即将web程序关闭
类似的可以执行
java.lang.Runtime.getRuntime().exec("net user 用户名 密码 /add");//增加操作系统用户,在有权限的情况下能成功(在URL中用%20替换空格,%2F替换/)
只要有权限就可以执行任何DOS命令。
2、解决方法
网上很多文章都介绍了三种解决方法,个人觉得将struts2的jar包更新到最新版本最简单,不用更改任何程序代码,目前最新版本2.3.4
下载到的更新包中有很多jar包,我系统中主要用到以下几个替换掉旧版本的:
commons-lang3-3.1.jar (保留commons-lang-2.6.jar)
javassist-3.11.0.GA.jar (新加包)
ognl-3.0.5.jar (替换旧版本)
struts2-core-2.3.4.1.jar (替换旧版本)
xwork-core-2.3.4.1.jar (替换旧版本)
最近struts2的安全漏洞影响面甚广,此后门为可以在url中直接远程调用脚本的漏洞和一个重定向漏洞。大家可以在s2-016远程执行脚本漏洞和s2-017重定向开放漏洞中看到攻击的例子。第一个漏洞即远程执行脚本通过构造ProcessBuilder创建进程执行脚本,框架开放度太大,由外界用户输入的字符串可以被当成代码执行。第二个漏洞重定向开放则可被钓鱼网站利用,外链上伪造成如著名电子商务网站淘宝,京东,比如是<a href="http://www.taobao.com/xxx.action?redirect:http://hacker.com/getyourPassword">打折新款</a>, 用户点击后来到一个钓鱼网站,这网站可以跟淘宝或京东网站登陆界面做的一样,来欺骗用户进行获取密码。
struts2作为一个老牌的web开源框架,尚且存在如此严重的问题,我们作为应用程序的开发者,也经常会在某些地方由于对框架理解不深,或考虑的不够周到,而埋下安全或其他方面的隐患。下面我们就谈谈经常碰到的一些高危地段:
1.转弯制动失灵(forward之后没有及时return)
1: String para = req.getParameter("g"); 2: if (para != null) { 3: RequestDispatcher r = req.getRequestDispatcher("/goodbye/ww"); 4: r.forward(req, res); 5: } 6: 7: System.out.println("still in here.Logic continue.");
这段代码在forward之后,Response就已经发送给客户端,看起来后面的代码就没执行了,其实不是的。forward之后,只是网络交互已经完成,这时如果调用一个新的r.forward会掷出java.lang.IllegalStateException: Cannot forward after response has been committed 异常,而后续代码照样执行。如果此时调用了一个后台服务,如标记数据库或其他资源操作,就会造成严重的逻辑错误。
解决方案:在forward之后不需要执行其它后台逻辑时,请及时return.(手制动)
2.注入攻击(用户提交的字符串作为代码的一部分并可执行)
此类问题在论坛、博客等用户可输入html文本的网站中碰到的比较多。用户输入一段文字,这段文字需要以网页形式展现出来(作为html执行或作为js执行)或者需要生成一个后台脚本(如sql,shell等),这时用户可恶意的输入脚本并在其后的执行环境中执行,达到控制或破坏系统的目的。
基本解决思路:
将这段代码脚本进行处理,比如将其中有较大风险的字符进行转义处理,使其在执行环境中只有展示功能,而无执行功效。(限行)
- 对于html,javascript只需将其进行html转义即可。(考虑到展示效果的问题,最好的办法是创建标签白名单,只有在白名单中的标签才可以展示,否则就转义或剔除)。
- 标签语言中的变量生成的html,可以对其进行标签定制,提供转义属性,形如
<c:out value="${html}" escapeXml="true" /> 形式。
- 对于SQL尽量使用Preparestatement来生成sql,少用拼接方式;如果要用拼接,则注意用户输入的内容其中字符的转义。
3.十字路口(servlet的全局变量线程安全问题)
Servlet本身设计时刻允许多线程访问,提供web服务给客户端。其中容器中Servlet中的实例变量一般来说是只有一个的(跟随servlet只初始化一个这个特点),那如果有对此实例并发写的情况,很容易造成写丢失等同步问题。但是进行同步加锁过多,又会造成性能下降等问题。在一般情况下尽量少用实例变量来维持状态。如若要用,也尽量使用线程安全且性能高的容器,如ConcurrentHashMap,而要避免使用线程不安全的如HashMap(可能造成死循环),ArrayList,LinkedList等。
【转载】网络安全---Strurts2漏洞介绍