【重大漏洞预警】Struts2 远程代码执行漏洞 S2-045 原理初步分析

北京时间3月6日晚,Apache基金会发布安全公告称,某旗下知名开源项目Apache Struts2 存在高危安全漏洞,允许攻击者远程执行代码,甚至获取服务器权限。目前已有漏洞利用流出。


        1.漏洞公告:

          请看: https://cwiki.apache.org/confluence/display/WW/S2-045

    首先,仔细阅读漏洞描述:

    Problem

    It is possible to perform a RCE attack with a malicious Content-Type value. If the Content-Type value isn't valid an exception is thrown which is then used to display an error message to a user.

    描述中明确了两点:

   通过 Content-Type 这个header头,注入OGNL语言,进而执行命令。

    漏洞的点在于,由于Strus2对错误消息处理时,出现了纰漏。


   2.关于Struts2上传机制:      

    部分网上描述为:基于 Jakarta plugin插件。这种描述是不对的,Struts2有其插件机制,如之前爆过S2-037漏洞的REST插件。但Struts2上传默认使用的是org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest 类,对上传数据进行解析。不存在插件这个说法,只不过它最终调用了第三方组件 common upload 完成上传操作。

    注:以下Struts2源码版本均是2.3.20。

   具体可以看看源码流程,首先进入 StrutsPrepareAndExecuteFilter 类,这是Struts2默认配置的入口过滤器。在里面可以看到,Struts2首先对输入请求对象 request 的进行封装:

    request = prepare.wrapRequest(request);

   跟进这条语句,可以看到封装为StrutsRequestWrapper的过程:

   if (request instanceof StrutsRequestWrapper) { return request;}String content_type = request.getContentType(); if (content_type != null && content_type.contains("multipart/form-data")) {//判断是不是post表单 MultiPartRequest mpr = getMultiPartRequest();//默认返回JakartaMultiPartRequest类 LocaleProvider provider = getContainer().getInstance(LocaleProvider.class); request = new MultiPartRequestWrapper(mpr, request, getSaveDir(), provider);} else { request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup);}return request;

 上面我注释的两个地方便是关键。

 multipart/form-data

 网上流传的POC中有这么一部分:

 #nike='multipart/form-data'

 就是使content_type.contains("multipart/form-data")判断为true。 当然,完全可以在其他地方添加multipart/form-data这个字符串。

 getMultiPartRequest()

这个方法可以继续追踪下去。通过配置struts.multipart.parser属性,可以指定不同的解析类,而默认的就是上面说的org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest类。

 网上可以查阅得到这样的解释:

 struts.multipart.parser:该属性指定处理multipart/form-data的MIME类型(文件上传)请求的框架,该属性支持cos、pell和jakarta等属性值,即分别对应使用cos的文件上传框架、pell上传及common-fileupload文件上传框架。该属性的默认值为jakarta。

更进一步的官方说明: https://cwiki.apache.org/confluence/display/WW/File+Upload#FileUpload-AlternateLibraries


3.漏洞补丁对比

漏洞分析必然要补丁对比了。查看struts2在git上的commit,发现描述为Uses default error key if specified key doesn't exist的修改:

2.5.10.1版本修改:

https://github.com/apache/struts/commit/b06dd50af2a3319dd896bf5c2f4972d2b772cf2b

【漏洞分析】S2-045 原理初步分析(CVE-2017-5638) S2-045漏洞 漏洞分析 漏洞原理分析 ...

2.3.32版本修改:

https://github.com/apache/struts/commit/352306493971e7d5a756d61780d57a76eb1f519a

【漏洞分析】S2-045 原理初步分析(CVE-2017-5638) S2-045漏洞 漏洞分析 漏洞原理分析 ...

       

 可以清晰的看到,都去掉了这样的一个方法:

 LocalizedTextUtil.findText(......);

 然后,就得到了第三个关键:

 sink点

后面通过动态调试追踪可以发现:就是通过这个方法LocalizedTextUtil.findText,最终到达执行命令的地方。这里暂时可以看做是一个sink点。

当payload进入这里后,就可以通过OGNL执行命令了。同时,直观感觉功能是在处理error消息。


4.漏洞重现及调试分析

1. 简单重现

环境配置:

tomcat7

struts2.3.20

这里说一下,通上面的原理分析。可以猜到,并不需要找个上传的地方。只需要模拟上传发包即可,危害巨大啊......

所以,我使用Struts2.3.20版本的struts2-blankwar包,直接测试漏洞:

【漏洞分析】S2-045 原理初步分析(CVE-2017-5638) S2-045漏洞 漏洞分析 漏洞原理分析 ...


我用的POC,是之前版本的。单纯测试并验证我的想法:

Content-Type: haha~multipart/form-data %{#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,@java.lang.Runtime@getRuntime().exec('calc')};

得到的结论是:

直接在Content-Type注入OGNL语句,即可执行命令。当然,包含multipart/form-data字符串。


2. 调试分析

接下来就看看调试关键地方了,能够更进一步了解原理。通过上面补丁对比,以及对流程的掌握。在JakartaMultiPartRequest的parse和buildErrorMessage方法下断点:

【漏洞分析】S2-045 原理初步分析(CVE-2017-5638) S2-045漏洞 漏洞分析 漏洞原理分析 ...

     可以看到,OGNL语句注入进去了。执行完上面的语句,就弹出计算器了。整个过程,有兴趣可以走一下。

 

    总结

漏洞的原理就是:Struts2默认解析上传文件的Content-Type头,存在问题。在解析错误的情况下,会执行错误信息中的OGNL代码。

以上是个人分析,期待官方解析~ 研究原理很有趣~


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值