class Metasploit::Credential::Importer::Pwdump

Implements importation behavior for pwdump files exported by Metasploit as well as files from the John the Ripper hash cracking suite: www.openwall.com/john/

Please note that in the case of data exported from Metasploit, the dataset will contain information on the ‘Mdm::Host` and `Mdm::Service` objects that are related to the credential. This means that Metasploit exports will be limited to containing {Metasploit::Credential::Login} objects, which is the legacy behavior of this export prior to the creation of this library.

Constants

COMMENT_LINE_START_REGEX

Matches a line starting with a ‘#’

JTR_NO_PASSWORD_STRING

The string that John the Ripper uses to designate a lack of password in a credentials entry

NONREPLAYABLE_REGEX

Matches lines that contain usernames and non-SMB hashes

PLAINTEXT_REGEX

Matches lines that contain usernames and plaintext passwords

POSTGRES_REGEX

Matches lines taht contain MD5 hashes for PostgreSQL

SERVICE_COMMENT_REGEX

Matches a line that we use to get information for creating ‘Mdm::Host` and `Mdm::Service` objects TODO: change to use named groups from 1.9+

SMB_WITH_HASH_REGEX

Matches LM/NTLM hash format

SMB_WITH_JTR_BLANK_PASSWORD_REGEX

Matches the way that John the Ripper exports SMB hashes with no password piece

SMB_WITH_PLAINTEXT_REGEX

Matches a line with free-form text - less restrictive than {SMB_WITH_HASH_REGEX}

WARNING_REGEX

Matches warning lines in legacy pwdump files

Public Class Methods

new(args={}) click to toggle source
# File lib/metasploit/credential/importer/pwdump.rb, line 145
def initialize(args={})
  super args
end

Public Instance Methods

blank_or_string(check_string, dehex=false) click to toggle source

Checks a string for matching {Metasploit::Credential::Exporter::Pwdump::BLANK_CRED_STRING} and returns blank string if it matches that constant. @param check_string [String] the string to check @param dehex [Boolean] convert hex to char if true @return [String]

# File lib/metasploit/credential/importer/pwdump.rb, line 62
def blank_or_string(check_string, dehex=false)
  if check_string.blank? || check_string ==  Metasploit::Credential::Exporter::Pwdump::BLANK_CRED_STRING || check_string == JTR_NO_PASSWORD_STRING
    ""
  else
    if dehex
      Metasploit::Credential::Text.dehex check_string
    else
      check_string
    end
  end
end
import!() click to toggle source

Perform the import of the credential data, creating ‘Mdm::Host` and `Mdm::Service` objects as needed, parsing out data by matching against regex constants that match the various kinds of valid lines found in the file. Ignore lines which match none of the REGEX constants. @return [void]

# File lib/metasploit/credential/importer/pwdump.rb, line 78
def import!
  service_info = nil
  Metasploit::Credential::Core.transaction do
    input.each_line do |line|
      case line
        when WARNING_REGEX
          next
        when COMMENT_LINE_START_REGEX
          service_info = service_info_from_comment_string(line)
        when SMB_WITH_HASH_REGEX
          info = parsed_regex_results($1, $2)
          username, private = info[:username], info[:private]
          creds_class = Metasploit::Credential::NTLMHash
        when SMB_WITH_JTR_BLANK_PASSWORD_REGEX
          info = parsed_regex_results($1, $2)
          username, private = info[:username], info[:private]
          creds_class = Metasploit::Credential::NTLMHash
        when SMB_WITH_PLAINTEXT_REGEX
          info = parsed_regex_results($1, $2)
          username, private = info[:username], info[:private]
          creds_class = Metasploit::Credential::NTLMHash
        when NONREPLAYABLE_REGEX
          info = parsed_regex_results($1, $2)
          username, private = info[:username], info[:private]
          creds_class = Metasploit::Credential::NonreplayableHash
        when POSTGRES_REGEX
          info = parsed_regex_results($1,"md5#{$2}")
          username, private = info[:username], info[:private]
          creds_class = Metasploit::Credential::PostgresMD5
        when PLAINTEXT_REGEX
          info = parsed_regex_results($1, $2, true)
          username, private = info[:username], info[:private]
          creds_class = Metasploit::Credential::Password
        else
          next
      end

      # Skip unless we have enough to make a Login
      if service_info.present?
        if [service_info[:host_address], service_info[:port], username, private].compact.size != 4
          next
        end
      else
        next
      end

      public_obj = create_credential_public(username: username)

      private_obj = creds_class.where(data: private).first_or_create

      core   = create_credential_core(origin: origin, private: private_obj, public: public_obj, workspace_id: workspace.id)

      login_opts = {
        address:      service_info[:host_address],
        port:         service_info[:port],
        protocol:     service_info[:protocol],
        service_name: service_info[:name],
        workspace_id: workspace.id,
        core:         core,
        status: Metasploit::Model::Login::Status::UNTRIED
      }

      create_credential_login(login_opts)
    end
  end
end
parsed_regex_results(username, private, dehex=false) click to toggle source

Break a line into user, hash @param username [String] @param private [String] @param dehex [Boolean] convert hex to char if true @return [Hash]

# File lib/metasploit/credential/importer/pwdump.rb, line 154
def parsed_regex_results(username, private, dehex=false)
  results = {}
  results[:username] = blank_or_string(username, dehex)
  results[:private]  = blank_or_string(private, dehex)

  results
end
service_info_from_comment_string(comment_string) click to toggle source

Take an msfpwdump comment string and parse it into information necessary for creating ‘Mdm::Host` and `Mdm::Service` objects. @param comment_string [String] a string starting with a ’#‘ that conforms to {SERVICE_COMMENT_REGEX} @return [Hash]

# File lib/metasploit/credential/importer/pwdump.rb, line 166
def service_info_from_comment_string(comment_string)
  service_info = {}
  if comment_string[SERVICE_COMMENT_REGEX]
    service_info[:host_address]  = $1
    service_info[:port]          = $2
    service_info[:protocol]      = $4.present? ? $4 : "tcp"
    service_info[:name]          = $6
    service_info
  else
    nil
  end
end