Merge pull request #86 from rapid7/feature/MSP-9719/postgres_crack
Feature/msp 9719/postgres crack MSP-9719 #landbug/bundler_fix
commit
f5ea6db604
|
@ -52,16 +52,8 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_status line.chomp
|
||||
end
|
||||
|
||||
print_status "Cracking #{format} hashes in incremental mode (All4)..."
|
||||
cracker_instance.rules = nil
|
||||
cracker_instance.wordlist = nil
|
||||
cracker_instance.incremental = 'All4'
|
||||
cracker_instance.crack do |line|
|
||||
print_status line.chomp
|
||||
end
|
||||
|
||||
print_status "Cracking #{format} hashes in incremental mode (Digits5)..."
|
||||
cracker_instance.incremental = 'Digits5'
|
||||
print_status "Cracking #{format} hashes in incremental mode (Digits)..."
|
||||
cracker_instance.incremental = 'Digits'
|
||||
cracker_instance.crack do |line|
|
||||
print_status line.chomp
|
||||
end
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/auxiliary/jtr'
|
||||
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
#Included to grab the john.pot and use some utiltiy functions
|
||||
include Msf::Auxiliary::JohnTheRipper
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'John the Ripper Postgres SQL Password Cracker',
|
||||
'Description' => %Q{
|
||||
This module uses John the Ripper to attempt to crack Postgres password
|
||||
hashes, gathered by the postgres_hashdump module. It is slower than some of the other
|
||||
JtR modules because it has to do some wordlist manipulation to properly handle postgres'
|
||||
format.
|
||||
},
|
||||
'Author' => ['theLightCosine'],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
def run
|
||||
@username_set = Set.new
|
||||
|
||||
cracker = new_john_cracker
|
||||
|
||||
hash_list = hash_file
|
||||
|
||||
#generate our wordlist and close the file handle
|
||||
wordlist = wordlist_file
|
||||
wordlist.close
|
||||
|
||||
|
||||
print_status "Wordlist file written out to #{wordlist.path}"
|
||||
cracker.wordlist = wordlist.path
|
||||
cracker.hash_path = hash_list
|
||||
|
||||
['raw-md5'].each do |format|
|
||||
cracker_instance = cracker.dup
|
||||
cracker_instance.format = format
|
||||
print_status "Cracking #{format} hashes in normal wordlist mode..."
|
||||
cracker_instance.crack do |line|
|
||||
print_status line.chomp
|
||||
end
|
||||
|
||||
print_status "Cracking #{format} hashes in single mode..."
|
||||
cracker_instance.rules = 'single'
|
||||
cracker_instance.crack do |line|
|
||||
print_status line.chomp
|
||||
end
|
||||
|
||||
print_status "Cracking #{format} hashes in incremental mode (Digits)..."
|
||||
cracker_instance.incremental = 'Digits'
|
||||
cracker_instance.crack do |line|
|
||||
print_status line.chomp
|
||||
end
|
||||
|
||||
print_status "Cracked Passwords this run:"
|
||||
cracker_instance.each_cracked_password do |password_line|
|
||||
password_line.chomp!
|
||||
next if password_line.blank?
|
||||
fields = password_line.split(":")
|
||||
# If we don't have an expected minimum number of fields, this is probably not a hash line
|
||||
next unless fields.count >=3
|
||||
username = fields.shift
|
||||
core_id = fields.pop
|
||||
password = fields.join(':') # Anything left must be the password. This accounts for passwords with : in them
|
||||
|
||||
# Postgres hashes always prepend the username to the password before hashing. So we strip the username back off here.
|
||||
password.gsub!(/^#{username}/,'')
|
||||
print_good "#{username}:#{password}:#{core_id}"
|
||||
create_cracked_credential( username: username, password: password, core_id: core_id)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Override the mixin method to add prependers
|
||||
def wordlist_file
|
||||
return nil unless framework.db.active
|
||||
wordlist = Metasploit::Framework::JtR::Wordlist.new(
|
||||
prependers: @username_set,
|
||||
custom_wordlist: datastore['CUSTOM_WORDLIST'],
|
||||
mutate: datastore['MUTATE'],
|
||||
use_creds: datastore['USE_CREDS'],
|
||||
use_db_info: datastore['USE_DB_INFO'],
|
||||
use_default_wordlist: datastore['USE_DEFAULT_WORDLIST'],
|
||||
use_hostnames: datastore['USE_HOSTNAMES'],
|
||||
use_common_root: datastore['USE_ROOT_WORDS'],
|
||||
workspace: myworkspace
|
||||
)
|
||||
wordlist.to_file
|
||||
end
|
||||
|
||||
def hash_file
|
||||
hashlist = Rex::Quickfile.new("hashes_tmp")
|
||||
Metasploit::Credential::NonreplayableHash.joins(:cores).where(metasploit_credential_cores: { workspace_id: myworkspace.id }, jtr_format: 'raw-md5,postgres').each do |hash|
|
||||
hash.cores.each do |core|
|
||||
user = core.public.username
|
||||
@username_set << user
|
||||
hash_string = "#{hash.data}"
|
||||
id = core.id
|
||||
hashlist.puts "#{user}:#{hash_string}:#{id}:"
|
||||
end
|
||||
end
|
||||
hashlist.close
|
||||
print_status "Hashes Written out to #{hashlist.path}"
|
||||
hashlist.path
|
||||
end
|
||||
|
||||
end
|
|
@ -35,12 +35,42 @@ class Metasploit3 < Msf::Auxiliary
|
|||
#Query the Postgres Shadow table for username and password hashes and report them
|
||||
res = postgres_query('SELECT usename, passwd FROM pg_shadow',false)
|
||||
|
||||
service_data = {
|
||||
address: ip,
|
||||
port: rport,
|
||||
service_name: 'postgres',
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
module_fullname: self.fullname,
|
||||
origin_type: :service,
|
||||
private_data: datastore['PASSWORD'],
|
||||
private_type: :password,
|
||||
username: datastore['USERNAME'],
|
||||
realm_key: Metasploit::Credential::Realm::Key::POSTGRESQL_DATABASE,
|
||||
realm_value: datastore['DATABASE']
|
||||
}
|
||||
|
||||
credential_data.merge!(service_data)
|
||||
|
||||
#Error handling routine here, borrowed heavily from todb
|
||||
case res.keys[0]
|
||||
when :conn_error
|
||||
print_error("A Connection Error occured")
|
||||
return
|
||||
when :sql_error
|
||||
# We know the credentials worked but something else went wrong
|
||||
credential_core = create_credential(credential_data)
|
||||
login_data = {
|
||||
core: credential_core,
|
||||
last_attempted_at: DateTime.now,
|
||||
status: Metasploit::Credential::Login::Status::SUCCESSFUL
|
||||
}
|
||||
login_data.merge!(service_data)
|
||||
create_credential_login(login_data)
|
||||
|
||||
case res[:sql_error]
|
||||
when /^C42501/
|
||||
print_error "#{datastore['RHOST']}:#{datastore['RPORT']} Postgres - Insufficient permissions."
|
||||
|
@ -50,6 +80,16 @@ class Metasploit3 < Msf::Auxiliary
|
|||
return
|
||||
end
|
||||
when :complete
|
||||
credential_core = create_credential(credential_data)
|
||||
login_data = {
|
||||
core: credential_core,
|
||||
last_attempted_at: DateTime.now,
|
||||
status: Metasploit::Credential::Login::Status::SUCCESSFUL
|
||||
}
|
||||
login_data.merge!(service_data)
|
||||
# We know the credentials worked and have admin access because we got the hashes
|
||||
login_data[:access_level] = 'Admin'
|
||||
create_credential_login(login_data)
|
||||
print_status("Query appears to have run successfully")
|
||||
end
|
||||
|
||||
|
@ -70,7 +110,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
jtr_format: 'raw-md5',
|
||||
jtr_format: 'raw-md5,postgres',
|
||||
module_fullname: self.fullname,
|
||||
private_type: :nonreplayable_hash
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue