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
POSTGRES_REGEX =

Matches lines taht contain MD5 hashes for PostgreSQL

/^[\s]*([\x21-\x7f]+):md5([0-9a-f]{32})$/
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_and_login, #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.



145
146
147
# File 'lib/metasploit/credential/importer/pwdump.rb', line 145

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)


62
63
64
65
66
67
68
69
70
71
72
# 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!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.



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
136
137
138
139
140
141
142
143
# 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)

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


154
155
156
157
158
159
160
# 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) ⇒ Hash

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

Parameters:

Returns:

  • (Hash)


166
167
168
169
170
171
172
173
174
175
176
177
# 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