Class: Metasploit::Credential::Importer::Pwdump

Inherits:
Object
  • Object
show all
Includes:
Creation, Base
Defined in:
lib/metasploit/credential/importer/pwdump.rb

Overview

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 Login objects, which is the legacy behavior of this export prior to the creation of this library.

Constant Summary collapse

COMMENT_LINE_START_REGEX =

Matches a line starting with a ‘#’

/^[\s]*#/
JTR_NO_PASSWORD_STRING =

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

"NO PASSWORD"
NONREPLAYABLE_REGEX =

Matches lines that contain usernames and non-SMB hashes

/^[\s]*([\x21-\x7f]+):([\x21-\x7f]+):::/n
PLAINTEXT_REGEX =

Matches lines that contain usernames and plaintext passwords

/^[\s]*([\x21-\x7f]+)[\s]+([\x21-\x7f]+)?/n
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+

/^#[\s]*([0-9.]+):([0-9]+)(\x2f(tcp|udp))?[\s]*(\x28([^\x29]*)\x29)?/n
SMB_WITH_JTR_BLANK_PASSWORD_REGEX =

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

/^[\s]*([^\s:]+):([0-9]+):NO PASSWORD\*+:NO PASSWORD\*+[^\s]*$/
SMB_WITH_HASH_REGEX =

Matches LM/NTLM hash format

/^[\s]*([^\s:]+):[0-9]+:([A-Fa-f0-9]+:[A-Fa-f0-9]+):[^\s]*$/
SMB_WITH_PLAINTEXT_REGEX =

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

/^[\s]*([^\s:]+):(.+):[A-Fa-f0-9]*:[A-Fa-f0-9]*:::$/
WARNING_REGEX =

Matches warning lines in legacy pwdump files

/^[\s]*Warning:/

Constants included from Base

Base::LONG_FORM_ALLOWED_PRIVATE_TYPE_NAMES, Base::SHORT_FORM_ALLOWED_PRIVATE_TYPE_NAMES

Instance Method Summary collapse

Methods included from Creation

#active_db?, #create_cracked_credential, #create_credential, #create_credential_core, #create_credential_login, #create_credential_origin, #create_credential_origin_cracked_password, #create_credential_origin_import, #create_credential_origin_manual, #create_credential_origin_service, #create_credential_origin_session, #create_credential_private, #create_credential_public, #create_credential_realm, #create_credential_service, #invalidate_login

Constructor Details

#initialize(args = {}) ⇒ Pwdump

Returns a new instance of Pwdump.



137
138
139
# File 'lib/metasploit/credential/importer/pwdump.rb', line 137

def initialize(args={})
  super args
end

Instance Method Details

#blank_or_string(check_string, dehex = false) ⇒ String

Checks a string for matching Exporter::Pwdump::BLANK_CRED_STRING and returns blank string if it matches that constant.

Parameters:

  • check_string (String)

    the string to check

  • dehex (Boolean) (defaults to: false)

    convert hex to char if true

Returns:

  • (String)


59
60
61
62
63
64
65
66
67
68
69
# File 'lib/metasploit/credential/importer/pwdump.rb', line 59

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!void

This method returns an undefined value.

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.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/metasploit/credential/importer/pwdump.rb', line 75

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 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  = Metasploit::Credential::Public.where(username: username).first_or_create
      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)

       = {
        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
      }

      ()
    end
  end
end

#parsed_regex_results(username, private, dehex = false) ⇒ Hash

Break a line into user, hash

Parameters:

  • username (String)
  • private (String)
  • dehex (Boolean) (defaults to: false)

    convert hex to char if true

Returns:

  • (Hash)


146
147
148
149
150
151
152
# File 'lib/metasploit/credential/importer/pwdump.rb', line 146

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) ⇒ Hash

Take an msfpwdump comment string and parse it into information necessary for creating Mdm::Host and Mdm::Service objects.

Parameters:

Returns:

  • (Hash)


158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/metasploit/credential/importer/pwdump.rb', line 158

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