summaryrefslogtreecommitdiff
path: root/foreign/client_handling/lazagne/config/write_output.py
blob: fb9a32b5e97cabe41c1210feae0562f319df9a8e (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
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
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
# -*- coding: utf-8 -*-
import ctypes
import getpass
import json
import logging
import os
import socket
import sys
import traceback

from time import gmtime, strftime
from platform import uname

from foreign.client_handling.lazagne.config.users import get_username_winapi
from foreign.client_handling.lazagne.config.winstructure import string_to_unicode, char_to_int, chr_or_byte, python_version
from .constant import constant

# --------------------------- Standard output functions ---------------------------

STD_OUTPUT_HANDLE = -11
std_out_handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
tmp_user = None


class StandardOutput(object):
    def __init__(self):
        self.banner = '''
|====================================================================|
|                                                                    |
|                        The LaZagne Project                         |
|                                                                    |
|                          ! BANG BANG !                             |
|                                                                    |
|====================================================================|
'''
        self.FILTER = b''.join([((len(repr(chr_or_byte(x))) == 3 and python_version == 2) or
                                 (len(repr(chr_or_byte(x))) == 4 and python_version == 3))
                                and chr_or_byte(x) or b'.' for x in range(256)])

    def set_color(self, color='white', intensity=False):
        c = {'white': 0x07, 'red': 0x04, 'green': 0x02, 'cyan': 0x03}.get(color, None)

        if intensity:
            c |= 0x08

        ctypes.windll.kernel32.SetConsoleTextAttribute(std_out_handle, c)

    # print banner
    def first_title(self):
        self.do_print(message=self.banner, color='white', intensity=True)
        # Python 3.7.3 on Darwin x86_64: i386
        python_banner = 'Python {}.{}.{} on'.format(*sys.version_info) + " {0} {4}: {5}\n".format(*uname())
        self.print_logging(function=logging.debug, message=python_banner, prefix='[!]', color='white', intensity=True)

    # info option for the logging
    def print_title(self, title):
        t = u'------------------- ' + title + ' passwords -----------------\n'
        self.do_print(message=t, color='white', intensity=True)

    # debug option for the logging
    def title_info(self, title):
        t = u'------------------- ' + title + ' passwords -----------------\n'
        self.print_logging(function=logging.info, prefix='', message=t, color='white', intensity=True)

    def print_user(self, user, force_print=False):
        pass

    def print_footer(self, elapsed_time=None):
        footer = '\n[+] %s passwords have been found!\n' % str(constant.nb_password_found)
        if not logging.getLogger().isEnabledFor(logging.INFO):
            footer += 'For more information launch it again with the -v option\n'
        if elapsed_time:
            footer += '\nelapsed time = ' + str(elapsed_time)
        self.do_print(footer)

    def print_hex(self, src, length=8):
        N = 0
        result = b''
        while src:
            s, src = src[:length], src[length:]
            hexa = b' '.join([b"%02X" % char_to_int(x) for x in s])
            s = s.translate(self.FILTER)
            result += b"%04X   %-*s   %s\n" % (N, length * 3, hexa, s)
            N += length
        return result

    def try_unicode(self, obj, encoding='utf-8'):
        if python_version == 3:
            try:
                return obj.decode()
            except Exception:
                return obj
        try:
            if isinstance(obj, basestring):       # noqa: F821
                if not isinstance(obj, unicode):  # noqa: F821
                    obj = unicode(obj, encoding)  # noqa: F821
        except UnicodeDecodeError:
            return repr(obj)
        return obj

    # centralize print function
    def do_print(self, message='', color=False, intensity=False):
        # quiet mode => nothing is printed
        if constant.quiet_mode:
            return

        message = self.try_unicode(message)
        if color:
            self.set_color(color=color, intensity=intensity)
            self.print_without_error(message)
            self.set_color()
        else:
            self.print_without_error(message)

    def print_without_error(self, message):
        try:
            print(message.decode())
        except Exception:
            try:
                print(message)
            except Exception:
                print(repr(message))

    def print_logging(self, function, prefix='[!]', message='', color=False, intensity=False):
        if constant.quiet_mode:
            return

        try:
            msg = u'{prefix} {msg}'.format(prefix=prefix, msg=message)
        except Exception:
            msg = '{prefix} {msg}'.format(prefix=prefix, msg=str(message))

        if color:
            self.set_color(color, intensity)
            function(msg)
            self.set_color()
        else:
            function(msg)

    def print_output(self, software_name, pwd_found):
        if pwd_found:
            # if the debug logging level is not apply => print the title
            if not logging.getLogger().isEnabledFor(logging.INFO):
                # print the username only if password have been found
                user = constant.finalResults.get('User', '')
                global tmp_user
                if user != tmp_user:
                    tmp_user = user
                    self.print_user(user, force_print=True)

                # if not title1:
                self.print_title(software_name)

            # Particular passwords representation
            to_write = []
            if software_name in ('Hashdump', 'Lsa_secrets', 'Mscache'):
                pwds = pwd_found[1]
                for pwd in pwds:
                    self.do_print(pwd)
                    if software_name == 'Lsa_secrets':
                        hex_value = self.print_hex(pwds[pwd], length=16)
                        to_write.append([pwd.decode(), hex_value.decode()])
                        self.do_print(hex_value)
                    else:
                        to_write.append(pwd)
                self.do_print()

            # Other passwords
            else:
                # Remove duplicated password
                pwd_found = [dict(t) for t in set([tuple(d.items()) for d in pwd_found])]

                # Loop through all passwords found
                for pwd in pwd_found:

                    # Detect which kinds of password has been found
                    lower_list = [s.lower() for s in pwd]
                    for p in ('password', 'key', 'hash'):
                        pwd_category = [s for s in lower_list if p in s]
                        if pwd_category:
                            pwd_category = pwd_category[0]
                            break

                    write_it = False
                    passwd = None
                    try:
                        # Do not print empty passwords
                        if not pwd[pwd_category.capitalize()]:
                            continue

                        passwd = string_to_unicode(pwd[pwd_category.capitalize()])
                    except Exception:
                        pass

                    # No password found
                    if not passwd:
                        print_debug("FAILED", u'Password not found.')
                    else:
                        constant.nb_password_found += 1
                        write_it = True
                        print_debug("OK", u'{pwd_category} found!'.format(
                            pwd_category=pwd_category.title()))

                        # Store all passwords found on a table => for dictionary attack if master password set
                        if passwd not in constant.password_found:
                            constant.password_found.append(passwd)

                    pwd_info = []
                    for p in pwd:
                        try:
                            pwd_line = '%s: %s' % (p, pwd[p].decode())  # Manage bytes output (py 3)
                        except Exception:
                            pwd_line = '%s: %s' % (p, pwd[p])

                        pwd_info.append(pwd_line)
                        self.do_print(pwd_line)

                    self.do_print()

                    if write_it:
                        to_write.append(pwd_info)

            # write credentials into a text file
            self.checks_write(to_write, software_name)
        else:
            print_debug("INFO", "No passwords found.\n")

    def write_header(self):
        time = strftime("%Y-%m-%d %H:%M:%S", gmtime())
        try:
            hostname = socket.gethostname().decode(sys.getfilesystemencoding())
        except AttributeError:
            hostname = socket.gethostname()

        header = u'{banner}\r\n- Date: {date}\r\n- Username: {username}\r\n- Hostname:{hostname}\r\n\r\n'.format(
            banner=self.banner.replace('\n', '\r\n'),
            date=str(time),
            username=get_username_winapi(),
            hostname=hostname
        )
        with open(os.path.join(constant.folder_name, '{}.txt'.format(constant.file_name_results)), "ab+") as f:
            f.write(header.encode())

    def write_footer(self):
        footer = '\n[+] %s passwords have been found!\r\n\r\n' % str(constant.nb_password_found)
        open(os.path.join(constant.folder_name, '%s.txt' % constant.file_name_results), "a+").write(footer)

    def checks_write(self, values, category):
        if values:
            if 'Passwords' not in constant.finalResults:
                constant.finalResults['Passwords'] = []
            constant.finalResults['Passwords'].append((category, values))


def print_debug(error_level, message):
    # Quiet mode => nothing is printed
    if constant.quiet_mode:
        return

    # print when password is found
    if error_level == 'OK':
        constant.st.do_print(message='[+] {message}'.format(message=message), color='green')

    # print when password is not found
    elif error_level == 'FAILED':
        constant.st.do_print(message='[-] {message}'.format(message=message), color='red', intensity=True)

    elif error_level == 'CRITICAL' or error_level == 'ERROR':
        constant.st.print_logging(function=logging.error, prefix='[-]', message=message, color='red', intensity=True)

    elif error_level == 'WARNING':
        constant.st.print_logging(function=logging.warning, prefix='[!]', message=message, color='cyan')

    elif error_level == 'DEBUG':
        constant.st.print_logging(function=logging.debug, message=message, prefix='[!]')

    else:
        constant.st.print_logging(function=logging.info, message=message, prefix='[!]')

# --------------------------- End of output functions ---------------------------

def json_to_string(json_string):
    string = u''
    try:
        for json in json_string:
            if json:
                string += u'##################  User: {username} ################## \r\n'.format(username=json['User'])
                if 'Passwords' not in json:
                    string += u'\r\nNo passwords found for this user.\r\n\r\n'
                else:
                    for pwd_info in json['Passwords']:
                        category, pwds_tab = pwd_info

                        string += u'\r\n------------------- {category} -----------------\r\n'.format(
                            category=category)

                        if category.lower() in ('lsa_secrets', 'hashdump', 'cachedump'):
                            for pwds in pwds_tab:
                                if category.lower() == 'lsa_secrets':
                                    for d in pwds:
                                        string += u'%s\r\n' % (constant.st.try_unicode(d))
                                else:
                                    string += u'%s\r\n' % (constant.st.try_unicode(pwds))
                        else:
                            for pwds in pwds_tab:
                                string += u'\r\nPassword found!\r\n'
                                for pwd in pwds:
                                    try:
                                        name, value = pwd.split(':', 1)
                                        string += u'%s: %s\r\n' % (
                                            name.strip(), constant.st.try_unicode(value.strip()))
                                    except Exception:
                                        print_debug('DEBUG', traceback.format_exc())
                        string += u'\r\n'
    except Exception:
        print_debug('ERROR', u'Error parsing the json results: {error}'.format(error=traceback.format_exc()))

    return string


def write_in_file(result):
    """
    Write output to file (json and txt files)
    """
    if result:
        if constant.output in ('json', 'all'):
            try:
                # Human readable Json format
                pretty_json = json.dumps(result, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False)
                with open(os.path.join(constant.folder_name, constant.file_name_results + '.json'), 'ab+') as f:
                    f.write(pretty_json.encode())

                constant.st.do_print(u'[+] File written: {file}'.format(
                    file=os.path.join(constant.folder_name, constant.file_name_results + '.json'))
                )
            except Exception as e:
                print_debug('DEBUGG', traceback.format_exc())

        if constant.output in ('txt', 'all'):
            try:
                with open(os.path.join(constant.folder_name, constant.file_name_results + '.txt'), 'ab+') as f:
                    a = json_to_string(result)
                    f.write(a.encode())

                constant.st.write_footer()
                constant.st.do_print(u'[+] File written: {file}'.format(
                    file=os.path.join(constant.folder_name, constant.file_name_results + '.txt'))
                )
            except Exception as e:
                print_debug('DEBUG', traceback.format_exc())