Skip to content

Commit 9cc44f6

Browse files
authored
Merge pull request Pennyw0rth#306 from jubeaz/module_wcc_add_defender
Module wcc added some defender checks
2 parents e14be33 + 35b4c4c commit 9cc44f6

1 file changed

Lines changed: 99 additions & 8 deletions

File tree

nxc/modules/wcc.py

Lines changed: 99 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from nxc.logger import nxc_logger
1010
from impacket.dcerpc.v5 import rrp, samr, scmr
1111
from impacket.dcerpc.v5.rrp import DCERPCSessionError
12+
from impacket.dcerpc.v5.rpcrt import DCERPCException
1213
from impacket.smbconnection import SessionError as SMBSessionError
1314
from 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+
638726
def le(reg_sz_string, number):
639727
return int(reg_sz_string[:-1]) <= number
640728

729+
641730
def in_(obj, seq):
642731
return obj in seq
643732

733+
644734
def startswith(string, start):
645735
return string.startswith(start)
646736

737+
647738
def not_(boolean_operator):
648739
def wrapper(*args, **kwargs):
649740
return not boolean_operator(*args, **kwargs)

0 commit comments

Comments
 (0)