Skip to content

Commit f18c442

Browse files
committed
Finish SCCM string enumeration
1 parent 74dcef7 commit f18c442

1 file changed

Lines changed: 79 additions & 8 deletions

File tree

nxc/modules/sccm.py

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
from ldap3.protocol.microsoft import security_descriptor_control
44
from nxc.parsers.ldap_results import parse_result_attributes
55

6+
SAM_USER_OBJECT = 0x30000000
67
SAM_MACHINE_ACCOUNT = 0x30000001
78
SAM_GROUP_OBJECT = 0x10000000
9+
LDAP_MATCHING_RULE_IN_CHAIN = "1.2.840.113556.1.4.1941"
810

911

1012
class NXCModule:
@@ -25,11 +27,21 @@ def __init__(self):
2527
self.sccm_site_servers = [] # List of dns host names of the SCCM site servers
2628
self.sccm_sites = {} # List of SCCM sites with their management points (Sorted by site code)
2729
self.base_dn = ""
30+
self.recursive_resolve = False
31+
32+
self.user_objects = []
33+
self.computer_objects = []
34+
self.group_objects = {}
2835

2936
def options(self, context, module_options):
30-
"""BASE_DN The base domain name for the LDAP query"""
37+
"""
38+
BASE_DN The base domain name for the LDAP query
39+
REC_RESOLVE Resolve members of groups recursively. Default: False
40+
"""
3141
if module_options and "BASE_DN" in module_options:
3242
self.base_dn = module_options["BASE_DN"]
43+
if module_options and "REC_RESOLVE" in module_options:
44+
self.recursive_resolve = bool(module_options["REC_RESOLVE"])
3345

3446
def on_login(self, context, connection):
3547
"""On a successful LDAP login we perform a search for all PKI Enrollment Server or Certificate Templates Names."""
@@ -76,30 +88,89 @@ def on_login(self, context, connection):
7688
self.context.log.highlight(f" CAS: {' ':<17}{False}")
7789
self.context.log.highlight(" Management Points:")
7890
for mp in self.sccm_sites[site]["ManagementPoints"]:
79-
self.context.log.highlight(f"\t CN:{' ':<12}{mp['cn']}")
80-
self.context.log.highlight(f"\t DNS Hostname:{' ':<2}{mp['dNSHostName']}")
81-
self.context.log.highlight(f"\t IP Address:{' ':<4}{mp['IPAddress']}")
82-
self.context.log.highlight(f"\t Default MP:{' ':<4}{mp['mSSMSDefaultMP']}")
91+
self.context.log.highlight(f" CN:{' ':<12}{mp['cn']}")
92+
self.context.log.highlight(f" DNS Hostname:{' ':<2}{mp['dNSHostName']}")
93+
self.context.log.highlight(f" IP Address:{' ':<4}{mp['IPAddress']}")
94+
self.context.log.highlight(f" Default MP:{' ':<4}{mp['mSSMSDefaultMP']}")
8395
else:
8496
self.context.log.highlight(f" CAS: {' ':<17}{True}")
8597
self.context.log.highlight("")
8698
except LDAPSearchError as e:
8799
context.log.fail(f"Got unexpected exception: {e}")
88100

89-
# Enumerate users/groups/computers with "SCCM" in their name
101+
# SCCM named objects enumeration
102+
self.get_sccm_named_objects(context, connection)
103+
if self.user_objects:
104+
context.log.success(f"Found {len(self.user_objects)} SCCM related user objects:")
105+
for user in self.user_objects:
106+
context.log.highlight(user)
107+
if self.computer_objects:
108+
context.log.success(f"Found {len(self.computer_objects)} SCCM related computer objects:")
109+
for computer in self.computer_objects:
110+
context.log.highlight(computer)
111+
if self.group_objects:
112+
context.log.success(f"Found {len(self.group_objects)} SCCM related group objects:")
113+
for group in self.group_objects:
114+
context.log.highlight(self.group_objects[group]["sAMAccountName"])
115+
for child in self.group_objects[group]["children"]:
116+
if int(child["sAMAccountType"]) == SAM_USER_OBJECT:
117+
context.log.highlight(f" {child['sAMAccountName']} -> User")
118+
elif int(child["sAMAccountType"]) == SAM_MACHINE_ACCOUNT:
119+
context.log.highlight(f" {child['sAMAccountName']} -> Computer")
120+
elif int(child["sAMAccountType"]) == SAM_GROUP_OBJECT:
121+
context.log.highlight(f" {child['sAMAccountName']} -> Group")
122+
123+
def get_sccm_named_objects(self, context, connection):
124+
"""Enumerate users/groups/computers with "SCCM" in their name"""
90125
# hippity hoppity your code is now my property, filter stolen from the awesome sccmhunter repository
91126
# https://github.com/garrettfoster13/sccmhunter
92127
try:
93-
yoinkers = '(|(samaccountname=*sccm*)(samaccountname=*mecm*)(description=*sccm*)(description=*mecm*)(name=*sccm*)(name=*mecm*))'
128+
yoinkers = "(|(samaccountname=*sccm*)(samaccountname=*mecm*)(description=*sccm*)(description=*mecm*)(name=*sccm*)(name=*mecm*))"
94129
context.log.display("Searching for SCCM related objects")
95130
result = connection.ldapConnection.search(
96131
searchFilter=yoinkers,
97132
searchBase=self.base_dn,
98-
attributes=["sAMAccountName", "distinguishedName"],
133+
attributes=["sAMAccountName", "distinguishedName", "sAMAccountType"],
99134
)
135+
136+
result = parse_result_attributes(result)
137+
for res in result:
138+
if "sAMAccountType" in res and int(res["sAMAccountType"]) == SAM_USER_OBJECT:
139+
self.user_objects.append(res["sAMAccountName"])
140+
elif "sAMAccountType" in res and int(res["sAMAccountType"]) == SAM_MACHINE_ACCOUNT:
141+
self.computer_objects.append(res["sAMAccountName"])
142+
elif "sAMAccountType" in res and int(res["sAMAccountType"]) == SAM_GROUP_OBJECT:
143+
self.group_objects[res["distinguishedName"]] = {
144+
"sAMAccountName": res["sAMAccountName"],
145+
"children": [],
146+
}
147+
if self.recursive_resolve:
148+
self.resolve_recursive(res["distinguishedName"])
149+
100150
except LDAPSearchError as e:
101151
context.log.fail(f"Got unexpected exception: {e}")
102152

153+
def resolve_recursive(self, dn):
154+
"""Recursively resolve members of a group."""
155+
try:
156+
self.context.log.debug(f"Resolving group members recursively for {dn}")
157+
# Somehow BaseDN is not working together with the LDAP_MATCHING_RULE_IN_CHAIN
158+
result = self.connection.ldapConnection.search(
159+
searchFilter=f"(memberOf:{LDAP_MATCHING_RULE_IN_CHAIN}:={dn})",
160+
attributes=["sAMAccountName", "distinguishedName", "sAMAccountType"],
161+
)
162+
163+
result = parse_result_attributes(result)
164+
for res in result:
165+
self.group_objects[dn]["children"].append({
166+
"sAMAccountName": res["sAMAccountName"],
167+
"distinguishedName": res["distinguishedName"],
168+
"sAMAccountType": res["sAMAccountType"],
169+
})
170+
171+
except LDAPSearchError as e:
172+
self.context.log.error(f"Error resolving group members: {e}")
173+
103174
def get_management_points(self):
104175
"""Searches for all SCCM management points in the Active Directory and maps them to their SCCM site via the site code."""
105176
try:

0 commit comments

Comments
 (0)