44import re
55import time
66from nxc .paths import NXC_PATH
7+ from impacket .smbconnection import SessionError
8+
9+ smb_error_status = [
10+ "STATUS_ACCOUNT_DISABLED" ,
11+ "STATUS_ACCOUNT_EXPIRED" ,
12+ "STATUS_ACCOUNT_RESTRICTION" ,
13+ "STATUS_INVALID_LOGON_HOURS" ,
14+ "STATUS_INVALID_WORKSTATION" ,
15+ "STATUS_LOGON_TYPE_NOT_GRANTED" ,
16+ "STATUS_PASSWORD_EXPIRED" ,
17+ "STATUS_PASSWORD_MUST_CHANGE" ,
18+ "STATUS_ACCESS_DENIED" ,
19+ "STATUS_NO_SUCH_FILE" ,
20+ "KDC_ERR_CLIENT_REVOKED" ,
21+ "KDC_ERR_PREAUTH_FAILED" ,
22+ ]
723
824
925class NXCModule :
@@ -92,9 +108,11 @@ def read_and_decode_file(self, connection, context, file_path, user):
92108 buf .seek (0 )
93109 binary_data = buf .read ()
94110
95- # Extract meaningful strings
96- return [(encoding , string ) for encoding , string in self .extract_strings (binary_data )
97- if self .is_meaningful_content (string )]
111+ # Return only the meaningful strings
112+ return [
113+ string for _ , string in self .extract_strings (binary_data )
114+ if self .is_meaningful_content (string )
115+ ]
98116
99117 def on_admin_login (self , context , connection ):
100118 context .log .display ("Searching for Notepad cache..." )
@@ -106,56 +124,65 @@ def on_admin_login(self, context, connection):
106124 # Path for Windows Notepad tab state files
107125 notepad_dir = f"Users\\ { directory .get_longname ()} \\ AppData\\ Local\\ Packages\\ Microsoft.WindowsNotepad_8wekyb3d8bbwe\\ LocalState\\ TabState\\ "
108126 try :
109- if not connection .conn .listPath ("C$" , f"{ notepad_dir } \\ *" ):
110- continue
111- try :
112- for file in connection .conn .listPath ("C$" , f"{ notepad_dir } \\ *" ):
113- if file .get_longname () not in self .false_positive and file .get_longname ().endswith (".bin" ):
114- file_path = f"{ notepad_dir } { file .get_longname ()} "
115-
116- # Read the binary file
117- meaningful_strings = self .read_and_decode_file (connection , context , file_path , directory .get_longname ())
118-
119- if meaningful_strings :
120- found += 1
121- context .log .highlight (f"C:\\ { file_path } " )
122-
123- # Output content
124- content_lines = []
125-
126- # First loop to handle meaningful strings
127- for _ , string in meaningful_strings :
128- if bool (re .match (self .FILE_PATH_REGEX , string )): # Only needed if checking locally
129- # Read the file into a buffer
130- meaningful_strings = self .read_and_decode_file (connection , context , string [2 :], directory .get_longname ())
131-
132- # Second loop to handle content inside the file
133- for _ , string in meaningful_strings :
134- context .log .highlight (f"\t { string } " )
135- content_lines .append (string ) # Store the string value only
136- else :
137- context .log .highlight (f"\t { string } " )
138- content_lines .append (string ) # Store the string value only
127+ for file in connection .conn .listPath ("C$" , f"{ notepad_dir } \\ *" ):
128+ if file .get_longname () not in self .false_positive and file .get_longname ().endswith (".bin" ):
129+ file_path = f"{ notepad_dir } { file .get_longname ()} "
139130
140- # Save to file
141- filename = f"{ connection .host } _{ directory .get_longname ()} _notepad_tabstate_{ found } .txt"
142- export_path = join (NXC_PATH , "modules" , "notepad" )
143- path = abspath (join (export_path , filename ))
144- makedirs (export_path , exist_ok = True )
131+ # Read the binary file
132+ meaningful_strings = self .read_and_decode_file (connection , context , file_path , directory .get_longname ())
145133
146- try :
147- with open (path , "w+" ) as output_file :
148- output_file .write (f"Source: C:\\ { file_path } \n \n " )
149- output_file .write ("\n " .join (content_lines )) # Write strings line by line
150- context .log .highlight (f"Notepad tab state content written to: { path } " )
151- except Exception as e :
152- context .log .fail (f"Failed to write Notepad tab state to { filename } : { e } " )
134+ if meaningful_strings :
135+ found += 1
136+ context .log .highlight (f"C:\\ { file_path } " )
153137
154- except Exception as e :
155- context .log .fail (f"Failed on connection and reading bin files: { e } " )
156- context .log .debug (f"Failed: { e } " )
157- except Exception as e :
158- context .log .fail (f"{ directory .get_longname ()} User has no any notepad cache file" )
159- context .log .debug (f"Failed for user { directory .get_longname ()} : { e } " )
138+ # Output content
139+ content_lines = []
140+
141+ # First loop to handle meaningful strings
142+ for string in meaningful_strings :
143+ if bool (re .match (self .FILE_PATH_REGEX , string )): # Only needed if checking locally
144+ # Read the file into a buffer
145+ meaningful_strings = self .read_and_decode_file (connection , context , string [2 :], directory .get_longname ())
146+
147+ # Second loop to handle content inside the file
148+ for string in meaningful_strings :
149+ context .log .highlight (f"\t { string } " )
150+ content_lines .append (string ) # Store the string value only
151+ else :
152+ context .log .highlight (f"\t { string } " )
153+ content_lines .append (string ) # Store the string value only
154+
155+ # Save to file
156+ filename = f"{ connection .host } _{ directory .get_longname ()} _notepad_tabstate_{ found } .txt"
157+ export_path = join (NXC_PATH , "modules" , "notepad" )
158+ path = abspath (join (export_path , filename ))
159+ makedirs (export_path , exist_ok = True )
160+
161+ with open (path , "w+" ) as output_file :
162+ output_file .write (f"Source: C:\\ { file_path } \n \n " )
163+ output_file .write ("\n " .join (content_lines )) # Write strings line by line
164+ context .log .success (f"Notepad tab state content written to: { path } " )
165+ except SessionError as e :
166+ error = self .get_error_string (e )
167+ if error == "STATUS_OBJECT_NAME_NOT_FOUND" or error == "STATUS_OBJECT_PATH_NOT_FOUND" :
168+ context .log .debug (f"Failed for user { directory .get_longname ()} : { e } " )
169+ else :
170+ context .log .fail (
171+ f"Error enumerating shares: { error } " ,
172+ color = "magenta" if error in smb_error_status else "red" ,
173+ )
160174 if found == 0 :
161175 context .log .info ("No Notepad tab state files with meaningful content found" )
176+
177+ def get_error_string (self , exception ):
178+ if hasattr (exception , "getErrorString" ):
179+ try :
180+ es = exception .getErrorString ()
181+ except KeyError :
182+ return f"Could not get nt error code { exception .getErrorCode ()} from impacket: { exception } "
183+ if type (es ) is tuple :
184+ return es [0 ]
185+ else :
186+ return es
187+ else :
188+ return str (exception )
0 commit comments