@@ -22,8 +22,8 @@ class NXCModule:
2222 multiple_hosts = True
2323
2424 def __init__ (self ):
25- self .sccm_site_servers = [] # List of dns host names of the SCCM site servers
26- self .sccm_sites = {}
25+ self .sccm_site_servers = [] # List of dns host names of the SCCM site servers
26+ self .sccm_sites = {} # List of SCCM sites with their management points (Sorted by site code)
2727 self .base_dn = ""
2828
2929 def options (self , context , module_options ):
@@ -39,6 +39,7 @@ def on_login(self, context, connection):
3939 self .sc = ldap .SimplePagedResultsControl ()
4040
4141 try :
42+ # Search for SCCM root object
4243 search_filter = f"(distinguishedName=CN=System Management,CN=System,{ self .base_dn } )"
4344 controls = security_descriptor_control (sdflags = 0x04 )
4445 context .log .display (f"Looking for the SCCM container with filter: '{ search_filter } '" )
@@ -49,18 +50,21 @@ def on_login(self, context, connection):
4950 searchControls = controls ,
5051 searchBase = self .base_dn ,
5152 )
53+
54+ # There should be only one result
5255 for item in result :
5356 if isinstance (item , ldapasn1_impacket .SearchResultEntry ):
5457 self .context .log .success (f"Found SCCM object: { item [0 ]} " )
5558 self .get_site_servers (item )
5659 self .get_sites ()
5760 self .get_management_points ()
5861
59- self .context .log .success ("Site Servers:" )
62+ # Print results
63+ self .context .log .success (f"Found { len (self .sccm_site_servers )} Site Servers:" )
6064 for site in self .sccm_site_servers :
6165 ip = self .connection .resolver (site )
6266 self .context .log .highlight (f"{ site } - { ip ['host' ] if ip else 'unknown' } " )
63- self .context .log .success (" SCCM Sites:" )
67+ self .context .log .success (f"Found { len ( self . sccm_sites ) } SCCM Sites:" )
6468 for site in self .sccm_sites :
6569 self .context .log .highlight (f"{ self .sccm_sites [site ]['cn' ]} " )
6670 self .context .log .highlight (f" Site Code: { site .rjust (14 )} " )
@@ -72,19 +76,20 @@ def on_login(self, context, connection):
7276 self .context .log .highlight (f"\t IP Address:{ ' ' :<4} { mp ['IPAddress' ]} " )
7377 self .context .log .highlight (f"\t Default MP:{ ' ' :<4} { mp ['mSSMSDefaultMP' ]} " )
7478 self .context .log .highlight ("" )
75-
7679 except LDAPSearchError as e :
7780 context .log .fail (f"Got unexpected exception: { e } " )
7881
7982 def get_management_points (self ):
80- """Searches for all SCCM management points in the Active Directory and maps them to their SCCM site."""
83+ """Searches for all SCCM management points in the Active Directory and maps them to their SCCM site via the site code ."""
8184 try :
8285 response = self .connection .ldapConnection .search (
86+ searchBase = self .base_dn ,
8387 searchFilter = "(objectClass=mSSMSManagementPoint)" ,
84- attributes = "*" ,
88+ attributes = [ "cn" , "dNSHostName" , "mSSMSDefaultMP" , "mSSMSSiteCode" ] ,
8589 )
90+
8691 response_parsed = parse_result_attributes (response )
87- self . context . log . success ( "SCCM Management Points:" )
92+
8893 for mp in response_parsed :
8994 ip = self .connection .resolver (mp ["dNSHostName" ])
9095 self .sccm_sites [mp ["mSSMSSiteCode" ]]["ManagementPoints" ].append ({
@@ -98,13 +103,16 @@ def get_management_points(self):
98103 self .context .log .error (f"Error searching for management points: { e } " )
99104
100105 def get_sites (self ):
101- """Searches for all SCCM sites in the Active Directory."""
106+ """Searches for all SCCM sites in the Active Directory, sorted by site code ."""
102107 try :
103108 response = self .connection .ldapConnection .search (
109+ searchBase = self .base_dn ,
104110 searchFilter = "(objectClass=mSSMSSite)" ,
105111 attributes = ["cn" , "mSSMSSiteCode" , "mSSMSAssignmentSiteCode" ],
106112 )
113+
107114 response_parsed = parse_result_attributes (response )
115+
108116 for site in response_parsed :
109117 self .sccm_sites [site ["mSSMSSiteCode" ]] = {
110118 "cn" : site ["cn" ],
@@ -116,7 +124,7 @@ def get_sites(self):
116124 self .context .log .error (f"Error searching for sites: { e } " )
117125
118126 def get_site_servers (self , item ):
119- """Extracts the site servers from the SCCM object."""
127+ """Extracts the site servers from the root SCCM object."""
120128 raw_sec_descriptor = str (item [1 ][0 ][1 ][0 ]).encode ("latin-1" )
121129 principal_security_descriptor = ldaptypes .SR_SECURITY_DESCRIPTOR (data = raw_sec_descriptor )
122130 self .parse_dacl (principal_security_descriptor ["Dacl" ])
@@ -147,7 +155,9 @@ def resolve_SID(self, sid):
147155 searchFilter = f"(objectSid={ sid } )" ,
148156 attributes = ["sAMAccountName" , "sAMAccountType" , "member" , "dNSHostName" ],
149157 )
158+
150159 parsed_result = parse_result_attributes (result )
160+
151161 if not parsed_result :
152162 return None
153163 else :
@@ -179,5 +189,6 @@ def dn_to_sid(self, dn) -> str:
179189 attributes = ["sAMAccountName" , "objectSid" ],
180190 )
181191
192+ # Extract the SID of the object
182193 sid_raw = bytes (result [0 ][1 ][0 ][1 ].components [0 ])
183194 return ldaptypes .LDAP_SID (data = sid_raw ).formatCanonical ()
0 commit comments