99from nxc .logger import nxc_logger
1010from impacket .dcerpc .v5 import rrp , samr , scmr
1111from impacket .dcerpc .v5 .rrp import DCERPCSessionError
12+ from impacket .dcerpc .v5 .rpcrt import DCERPCException
1213from impacket .smbconnection import SessionError as SMBSessionError
1314from impacket .examples .secretsdump import RemoteOperations
1415
@@ -88,11 +89,11 @@ class NXCModule:
8889 supported_protocols = ["smb" ]
8990 opsec_safe = True
9091 multiple_hosts = True
91-
92+
9293 def __init__ (self ):
9394 self .context = None
9495 self .module_options = None
95-
96+
9697 self .wcc_logger = logging .getLogger ("WCC" )
9798 self .wcc_logger .propagate = False
9899 log_filename = nxc_logger .init_log_file ()
@@ -160,7 +161,7 @@ def __init__(self, context, connection):
160161 def run (self ):
161162 self .init_checks ()
162163 self .check_config ()
163-
164+
164165 def init_checks (self ):
165166 # Declare the checks to do and how to do them
166167 self .checks = [
@@ -190,7 +191,15 @@ def init_checks(self):
190191 ConfigCheck ("BitLocker configuration" , "Checks the BitLocker configuration (based on https://www.stigviewer.com/stig/windows_10/2020-06-15/finding/V-94859)" , checker_args = [[self , ("HKLM\\ SOFTWARE\\ Policies\\ Microsoft\\ FVE" , "UseAdvancedStartup" , 1 ), ("HKLM\\ SOFTWARE\\ Policies\\ Microsoft\\ FVE" , "UseTPMPIN" , 1 )]]),
191192 ConfigCheck ("Guest account disabled" , "Checks if the guest account is disabled" , checkers = [self .check_guest_account_disabled ]),
192193 ConfigCheck ("Automatic session lock enabled" , "Checks if the session is automatically locked on after a period of inactivity" , checker_args = [[self , ("HKCU\\ Control Panel\\ Desktop" , "ScreenSaverIsSecure" , 1 ), ("HKCU\\ Control Panel\\ Desktop" , "ScreenSaveTimeOut" , 300 , le )]]),
193- ConfigCheck ('Powershell Execution Policy == "Restricted"' , 'Checks if the Powershell execution policy is set to "Restricted"' , checker_args = [[self , ("HKLM\\ SOFTWARE\\ Microsoft\\ PowerShell\\ 1\\ ShellIds\\ Microsoft.Powershell" , "ExecutionPolicy" , "Restricted\x00 " ), ("HKCU\\ SOFTWARE\\ Microsoft\\ PowerShell\\ 1\\ ShellIds\\ Microsoft.Powershell" , "ExecutionPolicy" , "Restricted\x00 " )]], checker_kwargs = [{"options" : {"KOIfMissing" : False , "lastWins" : True }}])
194+ ConfigCheck ('Powershell Execution Policy == "Restricted"' , 'Checks if the Powershell execution policy is set to "Restricted"' , checker_args = [[self , ("HKLM\\ SOFTWARE\\ Microsoft\\ PowerShell\\ 1\\ ShellIds\\ Microsoft.Powershell" , "ExecutionPolicy" , "Restricted\x00 " ), ("HKCU\\ SOFTWARE\\ Microsoft\\ PowerShell\\ 1\\ ShellIds\\ Microsoft.Powershell" , "ExecutionPolicy" , "Restricted\x00 " )]], checker_kwargs = [{"options" : {"KOIfMissing" : False , "lastWins" : True }}]),
195+ ConfigCheck ("Defender service running" , "Checks if defender service is enabled" , checkers = [self .check_defender_service ]),
196+ ConfigCheck ("Defender Tamper Protection enabled" , "Check if Defender Tamper Protection is enabled" , checker_args = [[self , ("HKLM\\ Software\\ Microsoft\\ Windows Defender\\ Features" , "TamperProtection" , 5 )]]),
197+ ConfigCheck ("Defender RealTime Monitoring enabled" , "Check if Defender RealTime Monitoring is enabled" , checker_args = [[self , ("HKLM\\ Software\\ Policies\\ Microsoft\\ Windows Defender\\ Real-Time Protection" , "DisableRealtimeMonitoring" , 0 ), ("HKLM\\ Software\\ Microsoft\\ Windows Defender\\ Real-Time Protection" , "DisableRealtimeMonitoring" , 0 )]], checker_kwargs = [{"options" : {"lastWins" : True , "stopOnOK" : True }}]),
198+ ConfigCheck ("Defender IOAV Protection enabled" , "Check if Defender IOAV Protection is enabled" , checker_args = [[self , ("HKLM\\ Software\\ Policies\\ Microsoft\\ Windows Defender\\ Real-Time Protection" , "DisableIOAVProtection" , 0 ), ("HKLM\\ Software\\ Microsoft\\ Windows Defender\\ Real-Time Protection" , "DisableIOAVProtection" , 0 )]], checker_kwargs = [{"options" : {"lastWins" : True , "stopOnOK" : True }}]),
199+ ConfigCheck ("Defender Behaviour Monitoring enabled" , "Check if Defender Behaviour Monitoring is enabled" , checker_args = [[self , ("HKLM\\ Software\\ Policies\\ Microsoft\\ Windows Defender\\ Real-Time Protection" , "DisableBehaviourMonitoring" , 0 ), ("HKLM\\ Software\\ Microsoft\\ Windows Defender\\ Real-Time Protection" , "DisableBehaviourMonitoring" , 0 )]], checker_kwargs = [{"options" : {"lastWins" : True , "stopOnOK" : True }}]),
200+ ConfigCheck ("Defender Script Scanning enabled" , "Check if Defender Script Scanning is enabled" , checker_args = [[self , ("HKLM\\ Software\\ Policies\\ Microsoft\\ Windows Defender\\ Real-Time Protection" , "DisableScriptScanning" , 0 ), ("HKLM\\ Software\\ Microsoft\\ Windows Defender\\ Real-Time Protection" , "DisableScriptScanning" , 0 )]], checker_kwargs = [{"options" : {"lastWins" : True , "stopOnOK" : True }}]),
201+ ConfigCheck ("Defender no path exlusions" , "Checks Defender path exlusion" , checkers = [self .check_defender_exclusion ], checker_args = [("HKLM\\ SOFTWARE\\ Policies\\ Microsoft\\ Windows Defender\\ Exclusions\\ Paths" , "HKLM\\ SOFTWARE\\ Microsoft\\ Windows Defender\\ Exclusions\\ Paths" )]),
202+ ConfigCheck ("Defender no extension exclusions" , "Checks Defender extension exlusion" , checkers = [self .check_defender_exclusion ], checker_args = [("HKLM\\ SOFTWARE\\ Policies\\ Microsoft\\ Windows Defender\\ Exclusions\\ Extensions" , "HKLM\\ SOFTWARE\\ Microsoft\\ Windows Defender\\ Exclusions\\ Extensions" )])
194203 ]
195204
196205 # Add check to conf_checks table if missing
@@ -243,7 +252,7 @@ def check_config(self):
243252 if host_id is not None :
244253 self .connection .db .add_check_result (host_id , check .check_id , check .ok , ", " .join (check .reasons ).replace ("\x00 " , "" ))
245254
246- def check_registry (self , * specs , options = None ):
255+ def check_registry (self , * specs , options = None , stop_on_error = False ):
247256 """
248257 Perform checks that only require to compare values in the registry with expected values, according to the specs
249258 a spec may be either a 3-tuple: (key name, value name, expected value), or a 4-tuple (key name, value name, expected value, operation), where operation is a function that implements a comparison operator
@@ -261,14 +270,15 @@ def check_registry(self, *specs, options=None):
261270 try :
262271 if len (spec ) == 3 :
263272 (key , value_name , expected_value ) = spec
273+ op = operator .eq
264274 elif len (spec ) == 4 :
265275 (key , value_name , expected_value , op ) = spec
266276 else :
267277 ok = False
268278 reasons = ["Check could not be performed (invalid specification provided)" ]
269279 return ok , reasons
270280 except Exception as e :
271- self .module .log .error (f"Check could not be performed. Details: specs={ specs } , dce={ self .dce } , error: { e } " )
281+ self .context .log .error (f"Check could not be performed. Details: specs={ specs } , dce={ self .dce } , error: { e } " )
272282 return ok , reasons
273283
274284 if op == operator .eq :
@@ -308,6 +318,9 @@ def check_registry(self, *specs, options=None):
308318 else :
309319 ok = False
310320 reasons .append (f"Error while retrieving value of { key } \\ { value_name } : { value } " )
321+ if stop_on_error :
322+ ok = None
323+ return ok , reasons
311324 continue
312325
313326 if op (value , expected_value ):
@@ -479,6 +492,78 @@ def check_applocker(self):
479492
480493 return success , reasons
481494
495+ def get_exclusions (self , key_name ):
496+ exclusions = []
497+ try :
498+ values = self .reg_query_value (self .dce , self .connection , key_name , valueName = None , all_value = True )
499+ for _ , value_name , _ in values :
500+ exclusions .append (value_name )
501+ except Exception :
502+ self .context .log .debug ("No defender exclusion policies" )
503+
504+ return len (exclusions ), exclusions
505+
506+ def check_defender_exclusion (self , * spec , options = None ):
507+ try :
508+ if len (spec ) == 2 :
509+ (policy_key_name , key_name ) = spec
510+ else :
511+ ok = False
512+ reasons = ["Check could not be performed (invalid specification provided)" ]
513+ return ok , reasons
514+ except Exception as e :
515+ self .context .log .error (f"Check could not be performed. Details: spec={ spec } , dce={ self .dce } , error: { e } " )
516+ return ok , reasons
517+
518+ reasons = []
519+ success = True
520+
521+ count , exclusions_p = self .get_exclusions (policy_key_name )
522+ reasons = [f"Policy: [{ ', ' .join (exclusions_p )} ]" ]
523+ count_k , exclusions_k = self .get_exclusions (key_name )
524+ reasons .append (f"Manual: [{ ', ' .join (exclusions_k )} ]" )
525+ count += count_k
526+
527+ if count > 0 :
528+ success = False
529+
530+ return success , reasons
531+
532+ def check_defender_service (self ):
533+ ok = True
534+ raised = False
535+ reasons = []
536+ try :
537+ service_config , service_status = self .get_service ("windefend" , self .connection )
538+ if service_status == scmr .SERVICE_RUNNING :
539+ reasons .append ("windefend service running" )
540+ elif service_status == scmr .SERVICE_STOPPED :
541+ ok = False
542+ reasons .append ("windefend service not running" )
543+ except DCERPCException as e :
544+ ok = True
545+ raised = True
546+ reasons = [f"windefend service check error({ e } )" ]
547+ if ok is False or raised is True :
548+ try :
549+ service_config , service_status = self .get_service ("sense" , self .connection )
550+ if service_status == scmr .SERVICE_RUNNING :
551+ reasons .append ("sense service running" )
552+ elif service_status == scmr .SERVICE_STOPPED :
553+ ok = False
554+ reasons .append ("sense service not running" )
555+ except DCERPCException as e :
556+ ok = True
557+ raised = True
558+ reasons .append (f"sense service check error({ e } )" )
559+ if raised is True :
560+ reasons_save = reasons
561+ args = ("HKLM\\ SOFTWARE\\ Microsoft\\ Windows Defender" , "IsServiceRunning" , 1 )
562+ ok , reasons = self .check_registry (args )
563+ reasons .extend (reasons_save )
564+
565+ return ok , reasons
566+
482567 def _open_root_key (self , dce , connection , root_key ):
483568 ans = None
484569 retries = 1
@@ -527,7 +612,7 @@ def reg_get_subkeys(self, dce, connection, key_name):
527612 break
528613 return subkeys
529614
530- def reg_query_value (self , dce , connection , keyName , valueName = None ):
615+ def reg_query_value (self , dce , connection , keyName , valueName = None , all_value = False ):
531616 """Query remote registry data for a given registry value"""
532617
533618 def subkey_values (subkey_handle ):
@@ -580,8 +665,10 @@ def get_value(subkey_handle, dwIndex=0):
580665
581666 subkey_handle = ans ["phkResult" ]
582667
583- if valueName is None :
668+ if valueName is None and all_value is False :
584669 return get_value (subkey_handle )[2 ]
670+ elif valueName is None and all_value is True :
671+ return subkey_values (subkey_handle )
585672 else :
586673 for _ , name , data in subkey_values (subkey_handle ):
587674 if name .upper () == valueName .upper ():
@@ -635,15 +722,19 @@ def ls(self, smb, path="\\", share="C$"):
635722 self .context .log .error (f"ls(): C:\\ { path } { e } \n " )
636723 return file_listing
637724
725+
638726def le (reg_sz_string , number ):
639727 return int (reg_sz_string [:- 1 ]) <= number
640728
729+
641730def in_ (obj , seq ):
642731 return obj in seq
643732
733+
644734def startswith (string , start ):
645735 return string .startswith (start )
646736
737+
647738def not_ (boolean_operator ):
648739 def wrapper (* args , ** kwargs ):
649740 return not boolean_operator (* args , ** kwargs )
0 commit comments