Lucene search

K
metasploitChristophe De La FuenteMSF:AUXILIARY-ADMIN-REGISTRY_SECURITY_DESCRIPTOR-
HistoryApr 30, 2024 - 6:57 p.m.

Windows Registry Security Descriptor Utility

2024-04-3018:57:32
Christophe De La Fuente
www.rapid7.com
16
windows registry security
remote access
authentication
hex string
security information
smb
metasploit

7.2 High

AI Score

Confidence

Low

Read or write a Windows registry security descriptor remotely. In READ mode, the FILE option can be set to specify where the security descriptor should be written to. The following format is used: key: security_info: sd: In WRITE mode, the FILE option can be used to specify the information needed to write the security descriptor to the remote registry. The file must follow the same format as described above.

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

class MetasploitModule < Msf::Auxiliary
  include Msf::Exploit::Remote::SMB::Client::Authenticated
  include Msf::OptionalSession::SMB
  include Msf::Util::WindowsRegistry

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Windows Registry Security Descriptor Utility',
        'Description' => %q{
          Read or write a Windows registry security descriptor remotely.

          In READ mode, the `FILE` option can be set to specify where the
          security descriptor should be written to.

          The following format is used:
          ```
          key: <registry key>
          security_info: <security information>
          sd: <security descriptor as a hex string>
          ```

          In WRITE mode, the `FILE` option can be used to specify the information
          needed to write the security descriptor to the remote registry. The file must
          follow the same format as described above.
        },
        'Author' => [
          'Christophe De La Fuente'
        ],
        'License' => MSF_LICENSE,
        'Actions' => [
          [ 'READ', { 'Description' => 'Read a Windows registry security descriptor' } ],
          [ 'WRITE', { 'Description' => 'Write a Windows registry security descriptor' } ]
        ],
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [],
          'SideEffects' => [CONFIG_CHANGES]
        },
        'DefaultAction' => 'READ'
      )
    )

    register_options(
      [
        OptString.new('KEY', [ false, 'Registry key to read or write' ]),
        OptString.new('SD', [ false, 'Security Descriptor to write as a hex string' ], conditions: %w[ACTION == WRITE], regex: /^([a-fA-F0-9]{2})+$/),
        OptInt.new('SECURITY_INFORMATION', [
          true,
          'Security Information to read or write (see '\
          'https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/23e75ca3-98fd-4396-84e5-86cd9d40d343 '\
          '(default: OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION)',
          RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION |
            RubySMB::Field::SecurityDescriptor::GROUP_SECURITY_INFORMATION |
            RubySMB::Field::SecurityDescriptor::DACL_SECURITY_INFORMATION
        ]),
        OptString.new('FILE', [
          false,
          'File path to store the security descriptor when reading or source file path used to write the security descriptor when writing'
        ])
      ]
    )
  end

  def do_connect
    if session
      print_status("Using existing session #{session.sid}")
      client = session.client
      self.simple = ::Rex::Proto::SMB::SimpleClient.new(client.dispatcher.tcp_socket, client: client)
      simple.connect("\\\\#{simple.address}\\IPC$")
    else
      connect
      begin
        smb_login
      rescue Rex::Proto::SMB::Exceptions::Error, RubySMB::Error::RubySMBError => e
        fail_with(Module::Failure::NoAccess, "Unable to authenticate ([#{e.class}] #{e}).")
      end
    end

    report_service(
      host: simple.address,
      port: simple.port,
      host_name: simple.client.default_name,
      proto: 'tcp',
      name: 'smb',
      info: "Module: #{fullname}, last negotiated version: SMBv#{simple.client.negotiated_smb_version} (dialect = #{simple.client.dialect})"
    )

    begin
      @tree = simple.client.tree_connect("\\\\#{simple.address}\\IPC$")
    rescue RubySMB::Error::RubySMBError => e
      fail_with(Module::Failure::Unreachable, "Unable to connect to the remote IPC$ share ([#{e.class}] #{e}).")
    end

    begin
      @winreg = @tree.open_file(filename: 'winreg', write: true, read: true)
      @winreg.bind(endpoint: RubySMB::Dcerpc::Winreg)
    rescue RubySMB::Error::RubySMBError => e
      fail_with(Module::Failure::Unreachable, "Error when connecting to 'winreg' interface ([#{e.class}] #{e}).")
    end
  end

  def run
    do_connect

    case action.name
    when 'READ'
      action_read
    when 'WRITE'
      action_write
    else
      print_error("Unknown action #{action.name}")
    end
  ensure
    @winreg.close if @winreg
    @tree.disconnect! if @tree
    # Don't disconnect the client if it's coming from the session so it can be reused
    unless session
      simple.client.disconnect! if simple&.client.is_a?(RubySMB::Client)
      disconnect
    end
  end

  def action_read
    fail_with(Failure::BadConfig, 'Unknown registry key, please set the `KEY` option') if datastore['KEY'].blank?

    sd = @winreg.get_key_security_descriptor(datastore['KEY'], datastore['SECURITY_INFORMATION'], bind: false)
    print_good("Raw security descriptor for #{datastore['KEY']}: #{sd.bytes.map { |c| '%02x' % c.ord }.join}")

    unless datastore['FILE'].blank?
      remote_reg = Msf::Util::WindowsRegistry::RemoteRegistry.new(@winreg, name: :sam)
      remote_reg.save_to_file(datastore['KEY'], sd, datastore['SECURITY_INFORMATION'], datastore['FILE'])
      print_good("Saved to file #{datastore['FILE']}")
    end
  end

  def action_write
    if datastore['FILE'].blank?
      fail_with(Failure::BadConfig, 'Unknown security descriptor, please set the `SD` option') if datastore['SD'].blank?
      fail_with(Failure::BadConfig, 'Unknown registry key, please set the `KEY` option') if datastore['KEY'].blank?
      sd = datastore['SD']
      key = datastore['KEY']
      security_info = datastore['SECURITY_INFORMATION']
    else
      print_status("Getting security descriptor info from file #{datastore['FILE']}")
      remote_reg = Msf::Util::WindowsRegistry::RemoteRegistry.new(@winreg, name: :sam)
      sd_info = remote_reg.read_from_file(datastore['FILE'])
      sd = sd_info['sd']
      key = sd_info['key']
      security_info = sd_info['security_info']
      vprint_line("  key: #{key}")
      vprint_line("  security information: #{security_info}")
      vprint_line("  security descriptor: #{sd}")
    end

    sd = sd.chars.each_slice(2).map { |c| c.join.to_i(16).chr }.join
    @winreg.set_key_security_descriptor(key, sd, security_info, bind: false)
    print_good("Security descriptor set for #{key}")
  rescue RubySMB::Dcerpc::Error::WinregError => e
    fail_with(Failure::Unknown, "Unable to set the security descriptor for #{key}: #{e}")
  end
end

7.2 High

AI Score

Confidence

Low