Lucene search
+L

Windows Registry Security Descriptor Utility

🗓️ 14 May 2024 19:53:55Reported by Christophe De La FuenteType 
metasploit
 metasploit
🔗 www.rapid7.com👁 208 Views

Read or write Windows registry security descriptor remotely. FILE option specifies where to write the security descriptor. For READ mode, specify registry key, security info, and security descriptor as a hex string. For WRITE mode, use FILE option to specify info for writing security descriptor.

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::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}")
      self.simple = session.simple_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

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