Skip to content

Commit 8348fbd

Browse files
authored
Merge pull request #586 from Mauriceter/sccm-winreg
2 parents c13aba6 + c564a31 commit 8348fbd

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed

nxc/modules/sccm-recon6.py

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
2+
import time
3+
from impacket.dcerpc.v5 import transport, rrp
4+
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
5+
from impacket.dcerpc.v5.rpcrt import DCERPCException
6+
from impacket.smbconnection import SessionError
7+
from nxc.helpers.misc import CATEGORY
8+
from impacket.smbconnection import SMBConnection
9+
10+
11+
class NXCModule:
12+
# Mainly based on pssrecon:https://github.com/slygoo/pssrecon
13+
14+
name = "sccm-recon6"
15+
description = "Check if target is a Distribution point or Primary Site Server through winreg (RECON-6)"
16+
supported_protocols = ["smb"]
17+
category = CATEGORY.ENUMERATION
18+
19+
def options(self, context, module_options):
20+
"""No options available"""
21+
22+
def on_login(self, context, connection):
23+
self.context = context
24+
self.connection = connection
25+
26+
self.trigger_winreg(connection.conn, context)
27+
28+
rpc = transport.DCERPCTransportFactory(r"ncacn_np:445[\pipe\winreg]")
29+
rpc.set_smb_connection(connection.conn)
30+
31+
if connection.kerberos:
32+
rpc.set_kerberos(connection.kerberos, kdcHost=connection.kdcHost)
33+
dce = rpc.get_dce_rpc()
34+
if connection.kerberos:
35+
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
36+
dce.connect()
37+
dce.bind(rrp.MSRPC_UUID_RRP)
38+
39+
# Open HKEY_LOCAL_MACHINE
40+
ans = rrp.hOpenLocalMachine(dce)
41+
hRootKey = ans["phKey"]
42+
43+
try:
44+
self.EnumerateSS(dce, hRootKey)
45+
try:
46+
self.EnumerateDB(dce, hRootKey)
47+
except DCERPCException:
48+
self.context.log.fail("No site Database found")
49+
except DCERPCException as e:
50+
if "rpc_s_access_denied" in str(e):
51+
self.context.log.info(f"Probably not a primary site server or a distribution point: {e}")
52+
else:
53+
self.context.log.fail(f"Unexpected error: {e}")
54+
55+
dce.disconnect()
56+
57+
def EnumerateSS(self, dce, hRootKey):
58+
# Open the target registry key
59+
hkey = rrp.hBaseRegOpenKey(dce, hRootKey, "SOFTWARE\\Microsoft\\SMS")["phkResult"]
60+
num_keys = rrp.hBaseRegQueryInfoKey(dce, hkey)["lpcSubKeys"]
61+
subkeys = [rrp.hBaseRegEnumKey(dce, hkey, i)["lpNameOut"].rstrip("\x00") for i in range(num_keys)]
62+
63+
for subkey in subkeys:
64+
if subkey == "DP":
65+
self.context.log.success("Distribition Point Installed")
66+
self.EnumerateDP(dce, hRootKey)
67+
68+
if subkey == "MP":
69+
self.context.log.success("Management Point Installed")
70+
71+
# Check current logged user (probably sccm admin)
72+
hkey = rrp.hBaseRegOpenKey(dce, hRootKey, "SOFTWARE\\Microsoft\\SMS\\CurrentUser")["phkResult"]
73+
value = rrp.hBaseRegQueryValue(dce, hkey, "UserSID")
74+
self.context.log.success(f"Site Server Current User SID: {value[1][:-1]}")
75+
self.context.log.display(f" Resolve with: --query '(objectSid={value[1][:-1]})' sAMAccountName")
76+
77+
def EnumerateDP(self, dce, hRootKey):
78+
# Check distridution point info
79+
hkey = rrp.hBaseRegOpenKey(dce, hRootKey, "SOFTWARE\\Microsoft\\SMS\\DP")["phkResult"]
80+
81+
value = rrp.hBaseRegQueryValue(dce, hkey, "SiteCode")
82+
self.context.log.display(f" Site Code: {value[1][:-1]}")
83+
84+
value = rrp.hBaseRegQueryValue(dce, hkey, "SiteServer")
85+
self.context.log.display(f" Site Server: {value[1][:-1]}")
86+
87+
value = rrp.hBaseRegQueryValue(dce, hkey, "ManagementPoints")
88+
for mp in value[1][:-1].split("*"):
89+
self.context.log.display(f" Management Points: {mp}")
90+
91+
value = rrp.hBaseRegQueryValue(dce, hkey, "IsPXE")
92+
if value[1] == 1:
93+
self.context.log.highlight(" PXE is installed - CRED-1")
94+
95+
value = rrp.hBaseRegQueryValue(dce, hkey, "IsAnonymousAccessEnabled")
96+
if value[1] == 1:
97+
self.context.log.highlight(" Anonymous access to Distribution Point is enabled - CRED-6")
98+
self.context.log.display(f" http://{self.connection.host}/sms_dp_smspkg$/datalib")
99+
else:
100+
self.context.log.display(" Anonymous access to Distribution Point is disabled")
101+
102+
def EnumerateDB(self, dce, hRootKey):
103+
# Check for database site
104+
hkey = rrp.hBaseRegOpenKey(dce, hRootKey, "SOFTWARE\\Microsoft\\SMS\\COMPONENTS\\SMS_SITE_COMPONENT_MANAGER\\Multisite Component Servers")["phkResult"]
105+
num_keys = rrp.hBaseRegQueryInfoKey(dce, hkey)["lpcSubKeys"]
106+
subkeys = [rrp.hBaseRegEnumKey(dce, hkey, i)["lpNameOut"].rstrip("\x00") for i in range(num_keys)]
107+
108+
if num_keys == 0:
109+
self.context.log.success("Local Site Database")
110+
else:
111+
for subkey in subkeys:
112+
self.context.log.success(f"Site Database : {subkey}")
113+
114+
# Resolve site database name
115+
target = self.connection.resolver(subkey)
116+
if target is None:
117+
try:
118+
new_conn = SMBConnection(subkey, subkey)
119+
except Exception as e:
120+
self.context.log.fail(f"Connection error to {subkey}: {e}")
121+
continue
122+
else:
123+
try:
124+
new_conn = SMBConnection(subkey, target["host"])
125+
except Exception as e:
126+
self.context.log.fail(f"Connection error to {target['host']}: {e}")
127+
continue
128+
129+
if new_conn.isSigningRequired():
130+
self.context.log.display(f" SMB signing: {new_conn.isSigningRequired()}")
131+
else:
132+
self.context.log.highlight(f" SMB signing: {new_conn.isSigningRequired()} - TAKEOVER-2")
133+
134+
def trigger_winreg(self, connection, context):
135+
# Original idea from https://twitter.com/splinter_code/status/1715876413474025704
136+
# Basically triggers the RemoteRegistry to start without admin privs
137+
tid = connection.connectTree("IPC$")
138+
try:
139+
connection.openFile(
140+
tid,
141+
r"\winreg",
142+
0x12019F,
143+
creationOption=0x40,
144+
fileAttributes=0x80,
145+
)
146+
except SessionError as e:
147+
# STATUS_PIPE_NOT_AVAILABLE error is expected
148+
context.log.debug(str(e))
149+
# Give remote registry time to start
150+
time.sleep(1)

0 commit comments

Comments
 (0)