原创:struts2-045漏洞浅析

        这是小弟写的第一篇网文,文笔一般,请大家多包涵。近期发生了很多事,使我醒悟要回归自我的同时,要作些改变,所以开始写网文,且把这些网文分享至个人的微信公众号(公众号名:永记士多)和CSDN博客(博客账号:rossrocket,昵称:永记士多)。此微信公众号和CSDN博客现定位为:专门研究web应用的漏洞,性能,并发量,框架选择等问题,技术涵盖编程语言(java,php,python)、应用框架(sturts2,spring thinkphp,Laravel)、操作系统(主要是linux与windows),数据库(mysql,oracle,db2,redis,mongodb等),web service(apache、nginx、lighttpd、tomcat、jboss、weblogic等)等等,力求不做人云亦云的技术人员,要实践验证真理,做web应用里的“生活调查团”。

        下面是这一次的小研究:struts2-045漏洞分析。这个是近期比较严重涉及面比较广的一个漏洞,但这不是最新的漏洞,因为就在我分析的当天,另一个重大漏洞(struts2-046漏洞)被通报这了。以下为大家探讨,struts2-045漏洞是一个怎样的漏洞?黑客是怎么利用这个漏洞进行攻击。


1. 漏洞概述

        首先看看该漏洞的相关通报:安恒信息安全研究院WEBIN实验室高级安全研究员nike.zheng发现著名J2EE框架——Struts2存在远程代码执行的严重漏洞; 目前Struts2官方已经确认漏洞(漏洞编号S2-045,CVE编号:cve-2017-5638),并定级为高风险,影响版本范围为Struts 2.3.5 – Struts 2.3.31和Struts 2.5 – Struts 2.5.10。Struts2作为老著名的老牌的mvc框架,这里就不多说。这里介绍一下其危险性。


1.1漏洞的危害性

        有些网站把此漏洞定性为“strut2史上最大的漏洞”。是不是最大的漏洞的,不好说,但其危害性是巨大的,这是毋庸置疑的,因为这个漏洞能允许黑客远程代码执行,相当于整台服务器已托管给黑客,黑客可以利用此漏洞获取web应用的源程序,修改web应用内容,获取数据库密码并盗取数据库信息,更改系统代码,更改数据库密码等等。以下我们做个POC测试:

      (一).查看数据在库的地址与密码

      (1)先查看当前目录,从中我们可以知道使用tomcat的目录,那么就可以知道server.conf的:

                 原创:struts2-045漏洞浅析_第1张图片

      (2)能过脚本可以查找server.xml和jdbc.properties的路径分别为: F:\tmp\Apache Software Foundation\apache-tomcat-7.0.76\conf 和F:\tmp\Apache Software Foundation\apache-tomcat-7.0.76\webapps\ROOT\WEB-INF\classes。 

                  原创:struts2-045漏洞浅析_第2张图片

     (3)显示jdbc.properties的内容,从而得到数据库的地址与密码:

                         原创:struts2-045漏洞浅析_第3张图片

      (二)更改页面代码

        可以利用此漏洞修改任何的代码文件,如我要更改首页的页面,要其显示Diaoyu Islands are Chinese”,可进行如下操作:

                   原创:struts2-045漏洞浅析_第4张图片

        在浏览器输入地址http://192.168.10.170:8080/index,显示下信息表示修改成功:

                  原创:struts2-045漏洞浅析_第5张图片

1.2漏洞的条件

        此漏洞另一个危害性是其几乎无条件性。只要使用了struts2框架的应用,所有的url请求都会被此漏洞利用,即使应用中没有上传功能或使用第三方插件上传文件(如ueditor,kindeditor)也有此问题,除非修改了配置struts.multipart.parser为cospell。但这种情况非常少,因为大部分的struts2应用会使用apachecommons-fileupload为上传文件的解析器,而不配置struts.multipart.parser或配置为默认的jakarta,那么大部分的应用都会存在此漏洞。

 

2. 原因分析

        这样的漏洞,我一开始以为是struts2应用中存在调用cmdshell的代码需被黑客利用,但经过分析,发现struts2代码中不存在runtime.exe()ProcessBuilder.start() 等语句。后来经过我一轮的分析与派查,才发现此漏洞的本质是Ognl表达式注入,与SQL流入有异曲同式之秒。下面深入分析此漏洞的原因。


2.1 Ognl简述

        既然此漏洞是一个Ognl漏洞,那么先了解一下什么是Ognl,是用来干什么的。先看看网上截取的有关Ognl的描述。

                     原创:struts2-045漏洞浅析_第6张图片

        这段话我们只要知道两点就行了:1.Ognl是个一个表达式语言。2.可以用来获取和设置java对象的属性。其实针对此次漏洞我们可以理解Ognl有以下功能:通一定语法组装的字符串,经解释后,可以获取对象属性或调用对象的方式(与struts2标签库有点相似)。这是一个非常容给黑客攻击的特性来的。


2.2 Strtuts2代码分析

    现在我们分析一下Struts2的在什么情况下会调用Ognl表达式。下面的流程图是代码执行的过程。

                   原创:struts2-045漏洞浅析_第7张图片

        针对此流程图,作以下分析:

        a. 对序号(1)的方法,StrutsPrepareAndExecuteFilterdoFilter方法是Struts2框架的入口。所有web请求都会执行此方法后进入相应的Action类调用相应的方法。

         b. 对序号(2)的方法,PrepareOperationswrapRequest方法包装出Struts2自己的request对象

         c. 对序号(4)的判断,此判断为关键,下面的POC实例中的第一个表达式就是为了些判断为真,而调用JakartaMultiPartRequest类。

          d. 对序号(6)的方法,调JakartaMultiPartRequestparse方法对文件解码,而此方法捕获序号(7)和序号(8)方法的异常,发现异常则调用序号(9)的方法。具体代码如下图:

                  原创:struts2-045漏洞浅析_第8张图片

        e. 对序号(8)的方法,JakartaMultiPartRequestparse方法是一定会抛出异常的,因为这是POC代码里HEADER的内容根本不是multipart/form-data”编码的,而是一段Ognl表达式。所以是一定会走到序号(9)的

         f. 对序号(9)的方法,LocalizedTextUtil的findText是xwork框架上的工具库方法。LocalizedTextUtil是xwork解决我们前端国际化问题的一个方案。其中findText是搜索关键字的方法,里面是一个复杂的算法,但是其中有针对里面的参数defautlMessage(e.getMessage())调用了Ognl表达式解释方法进行解释,而此参数defautlMessage是就是前端Heaher里的Content-typre的值。

         g. 对序号(16)的方法,OgnlTextParser的evaluate方法就是最终解释Ognl表达式的语句。黑客就是向此处注入代码ProcessBuilderRuntime的代码,从而调用cmd或shell命令,从而操制操作系统。

 

2.3 POC代码研究

           以下网上的一段POC代码里的一段header内容,即使没有Ognl基础也很容易看懂。

                    原创:struts2-045漏洞浅析_第9张图片

         这个header内容只看Content-Type的内容,这Content-Type内容可以分为两部分。第一部分不用多说只是为了本文中《2.2Strtuts2流程分析》的流程图的序号(4)的判断为真而已;第二部分就是Ognl注入的代码,就是黑客用来控制系统的代码,其实细看没什么大了解,大部人都可以看得懂,不需要学习Ognl。大家再看的模块(3),这就是执行cmd命令的地方,相当于在服务器上执行了以下java代码:

                  

 

2.修复方式

        官方的解决方法是:升级到Apache Struts 2.3.32或2.5.10.1版。我们看看升级包怎么解决这个问题。其实很简单,就是更改类JakartaMultiPartRequest的buildErrorMessage(Throwable e, Object[] args)方法,改变调用LocalizedTextUtil.findText的调用的方式。原有的调用方式如下:

                  

         更改后的代码如下:

                      原创:struts2-045漏洞浅析_第10张图片

          方案比简解决,当e.getMessage()(这个值就是前端Header里Content-type内容)不作为而LocalizedTextUtil.findText的defatulMessage参数而是作为args参数传入。

     

 

 

附录1

POC测试境(不是性能问题,不列硬件信息)

1.攻击端

操作系统

Fedora 25 64(linux系统)

脚本解释器

Python 2.7.12


2.服务器端

操作系统

Windows 7  32

Web service

apache-tomcat-7.0.76

Struts2版本

2.3.15.1

Ognl版本

3.0.6

杀毒软件

360



 

附录2

        相关知识

        1. ProcessBuilder的redirectErrorStream 属性

        最初,此属性为 false,意思是子进程的标准输出和错误输出被发送给两个独立的流,这些流可以通过 Process.getInputStream() 和Process.getErrorStream() 方法来访问。如果将值设置为 true,标准错误将与标准输出合并。这使得关联错误消息和相应的输出变得更容易。在此情况下,合并的数据可从 Process.getInputStream() 返回的流读取,而从Process.getErrorStream() 返回的流读取将直接到达文件尾。

        2. STRUTS2中的OGNL

        个人认为如果想更深入的了解STRUTS与OGNL的关系,需要了解ValueStack的使用,其中涉及的“对象栈”的概念也比较重要。有兴趣的同志可以多读读以下这篇文章:《STRUTS2中的OGNL》(http://blog.csdn.net/v123411739/article/details/24052989) 


你可能感兴趣的:(漏洞)