diff --git a/doc/scapy/build_dissect.rst b/doc/scapy/build_dissect.rst index 26c0148ea14..2f53c349933 100644 --- a/doc/scapy/build_dissect.rst +++ b/doc/scapy/build_dissect.rst @@ -649,7 +649,7 @@ look to its building process:: def post_build(self, p, pay): if self.len is None and pay: l = len(pay) - p = p[:1] + hex(l)[2:]+ p[2:] + p = p[:1] + struct.pack("!B", l) + p[2:] return p+pay When ``post_build()`` is called, ``p`` is the current layer, ``pay`` the payload, diff --git a/doc/scapy/layers/dcerpc.rst b/doc/scapy/layers/dcerpc.rst index aaac3af3429..7e08b59202e 100644 --- a/doc/scapy/layers/dcerpc.rst +++ b/doc/scapy/layers/dcerpc.rst @@ -1,7 +1,7 @@ DCE/RPC & [MS-RPCE] =================== -.. note:: DCE/RPC per `DCE/RPC 1.1 `_ with the `[MS-RPCE] `_ additions +.. note:: DCE/RPC per `DCE/RPC 1.1 `_ with the `[MS-RPCE] `_ additions. Scapy provides support for dissecting and building Microsoft's Windows DCE/RPC calls. diff --git a/doc/scapy/layers/dcom.rst b/doc/scapy/layers/dcom.rst new file mode 100644 index 00000000000..ada9a56a0b8 --- /dev/null +++ b/doc/scapy/layers/dcom.rst @@ -0,0 +1,128 @@ +[MS-DCOM] +========= + +DCOM is a mechanism to manipulate COM objects remotely. It is in many ways just an extension over normal DCE/RPC, so understanding DCE/RPC concepts beforehand can be very useful. +Before reading this, have a look at Scapy's `DCE/RPC `_ documentation page. + +Terminology +----------- + +- In DCOM one instantiates 'classes' to get 'object references'. A class implements one or several 'interfaces', each of which has methods. +- ``CLSID``: the UIID of a **class**, used to instantiate it. This is typically chosen by whoever implements the COM object. +- ``IID``: the UIID of an **interface**, used to request an IPID. This is chosen by whoever defines the COM interface (mostly Microsoft). +- ``IPID``: a UIID that uniquely references an **interface on an object**. This allows to tell DCOM on which object to run the request we send. + +There are other IDs such as the OID (a 64bit number that uniquely references each object), and the OXID (a 64bit number that uniquely references each object exporter), but we won't get into the details. + +Per the spec, a DCOM client is supposed to keep track of the IPID, OID and OXID ids. In this regard, Scapy abstracts their usage. +On the other hand, the calling application is supposed to know the ``CLSID`` of the class it wishes to instantiate, and the various ``IID`` of the interfaces it wishes to use. + +General behavior of a DCOM client +--------------------------------- + +1. Setup the DCOM client (endpoint, SSP, etc.) +2. Get an object reference: Instantiate a class to get an object reference of the instance (``RemoteCreateInstance``), **OR**, get an object reference towards the class itself (``RemoteGetClassObject``). +3. Acquire the IPID of an interface of the object. +4. Call a method of that interface. +5. Release the reference counts on the interface (delete the IPID). + +Step 3 can be done manually through the ``AcquireInterface()`` method, but Scapy will also automatically call it if you try to use an interface that you haven't acquired on an object. + +Using the DCOM client +--------------------- + +General usage +~~~~~~~~~~~~~ + +1. Setup the DCOM client and connect to the object resolver (which is by default on port 135). + +.. code:: python + + from scapy.layers.msrpce.msdcom import DCOM_Client + from scapy.layers.ntlm import NTLMSSP + + client = DCOM_Client( + ssp=NTLMSSP(UPN="Administrator@domain.local", PASSWORD="Scapy1111@"), + ) + client.connect("server1.domain.local") + +.. note:: See the examples in `DCE/RPC `_ to connect with SPNEGO/Kerberos. + +2. Instantiate a class to get an object reference + +.. code:: python + + import uuid + from scapy.layers.dcerpc import find_com_interface + import scapy.layers.msrpce.raw.ms_pla + + CLSID_TraceSessionCollection = uuid.UUID("03837530-098b-11d8-9414-505054503030") + # The COM interface must have been compiled by scapy-rpc (midl-to-scapy) + IDataCollectorSetCollection = find_com_interface("IDataCollectorSetCollection") + + # Get new object reference + objref = client.RemoteCreateInstance( + # The CLSID we're instantiating + clsid=CLSID_TraceSessionCollection, + iids=[ + # An initial list of interfaces to ask for. There must be at least 1. + IDataCollectorSetCollection, + ] + ) + +3. Call a method on that object reference + +.. code:: python + + result = objref.sr1_req( + # The request message (here from [MS-PLA]) + pkt=GetDataCollectorSets_Request( + server=None, + filter=NDRPointer( + referent_id=0x72657355, + value=FLAGGED_WORD_BLOB( + cBytes=18, + asData=r"session\*".encode("utf-16le"), + ) + ), + ), + # The interface to send it on + iface=IDataCollectorSetCollection, + ) + +4. Release all the requested interfaces on the object reference + +.. code:: python + + objref.release() + +5. Close the client + +.. code:: python + + client.close() + + +Unmarshalling object references +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some methods return a reference to an object that is created by the remote server. On the network, +those are typically marshalled as a ``MInterfacePointer`` structure. Such a structure can be "unmarshalled" into a local object reference that can be used in Scapy to call methods on that object. + +.. code:: python + + # For instance, let's assume we're calling Next() of the IEnumVARIANT + resp = enum.sr1_req( + pkt=Next_Request(celt=1), + iface=IEnumVARIANT, + ) + + # Get the MInterfacePointer value + value = resp.valueof("rgVar")[0].valueof("_varUnion") + assert isinstance(value, MInterfacePointer) + + # Unmarshall it and acquire an initial interface on it. + objref = client.UnmarshallObjectReference( + value, + iid=IDataCollectorSet, + ) diff --git a/scapy/config.py b/scapy/config.py index 2a385c2f579..be3cb25b278 100755 --- a/scapy/config.py +++ b/scapy/config.py @@ -1146,6 +1146,8 @@ class Conf(ConfClass): ) #: Dictionary containing parsed NSS Keys tls_nss_keys: Dict[str, bytes] = None + #: Whether to use NDR64 by default instead of NDR 32 + ndr64: bool = True #: When TCPSession is used, parse DCE/RPC sessions automatically. #: This should be used for passive sniffing. dcerpc_session_enable = False diff --git a/scapy/layers/dcerpc.py b/scapy/layers/dcerpc.py index 6110fe1273c..28dfa6f97a0 100644 --- a/scapy/layers/dcerpc.py +++ b/scapy/layers/dcerpc.py @@ -21,12 +21,15 @@ `DCE/RPC `_ """ -from functools import partial - import collections +import importlib +import inspect import struct + from enum import IntEnum +from functools import partial from uuid import UUID + from scapy.base_classes import Packet_metaclass from scapy.config import conf @@ -116,6 +119,7 @@ # Typing imports from typing import ( Optional, + Union, ) # the alignment of auth_pad @@ -179,11 +183,45 @@ class DCERPC_Transport(IntEnum): - NCACN_IP_TCP = 1 - NCACN_NP = 2 + """ + Protocols identifiers currently supported by Scapy + """ + + NCACN_IP_TCP = 0x07 + NCACN_NP = 0x0F # TODO: add more.. if people use them? +# [C706] Appendix I with names from Appendix B +DCE_RPC_PROTOCOL_IDENTIFIERS = { + 0x0: "OSI OID", # Special + 0x0D: "UUID", # Special + # Transports + # 0x2: "DNA Session Control", + # 0x3: "DNA Session Control V3", + # 0x4: "DNA NSP Transport", + # 0x5: "OSI TP4", + 0x06: "NCADG_OSI_CLSN", # [C706] + 0x07: "NCACN_IP_TCP", # [C706] + 0x08: "NCADG_IP_UDP", # [C706] + 0x09: "IP", # [C706] + 0x0A: "RPC connectionless protocol", # [C706] + 0x0B: "RPC connection-oriented protocol", # [C706] + 0x0C: "NCALRPC", + 0x0F: "NCACN_NP", # [MS-RPCE] + 0x11: "NCACN_NB", # [C706] + 0x12: "NCACN_NB_NB", # [MS-RPCE] + 0x13: "NCACN_SPX", # [C706] + 0x14: "NCADG_IPX", # [C706] + 0x16: "NCACN_AT_DSP", # [C706] + 0x17: "NCADG_AT_DSP", # [C706] + 0x19: "NCADG_NB", # [C706] + 0x1A: "NCACN_VNS_SPP", # [C706] + 0x1B: "NCADG_VNS_IPC", # [C706] + 0x1F: "NCACN_HTTP", # [MS-RPCE] +} + + def _dce_rpc_endianness(pkt): """ Determine the right endianness sign for a given DCE/RPC packet @@ -585,18 +623,9 @@ class DceRpcSecVTCommand(Packet): }, end_tot_size=-2, ), - LEShortField("Length", None), + LenField("Length", None, fmt=" DceRpcInterface: """ Find an interface object through the name in the IDL """ @@ -1255,6 +1316,8 @@ def find_dcerpc_interface(name): class ComInterface: + if_version = 0 + def __init__(self, name, uuid, opnums): self.name = name self.uuid = uuid @@ -1273,6 +1336,19 @@ def register_com_interface(name, uuid, opnums): uuid, opnums, ) + # bind for build + for opnum, operations in opnums.items(): + bind_top_down(DceRpc5Request, operations.request, opnum=opnum) + + +def find_com_interface(name) -> ComInterface: + """ + Find an interface object through the name in the IDL + """ + try: + return next(x for x in COM_INTERFACES.values() if x.name == name) + except StopIteration: + raise AttributeError("Unknown interface !") # --- NDR fields - [C706] chap 14 @@ -1297,7 +1373,7 @@ class _NDRPacket(Packet): __slots__ = ["ndr64", "ndrendian", "deferred_pointers", "request_packet"] def __init__(self, *args, **kwargs): - self.ndr64 = kwargs.pop("ndr64", False) + self.ndr64 = kwargs.pop("ndr64", conf.ndr64) self.ndrendian = kwargs.pop("ndrendian", "little") # request_packet is used in the session, so that a response packet # can resolve union arms if the case parameter is in the request. @@ -1806,7 +1882,16 @@ def read_deferred_pointers(self, pkt, s): return s def addfield(self, pkt, s, val): - s = super(NDRConstructedType, self).addfield(pkt, s, val) + try: + s = super(NDRConstructedType, self).addfield(pkt, s, val) + except Exception as ex: + try: + ex.args = ( + "While building field '%s': " % self.name + ex.args[0], + ) + ex.args[1:] + except (AttributeError, IndexError): + pass + raise ex if isinstance(val, _NDRPacket): # If a sub-packet we just dissected has deferred pointers, # pass it to parent packet to propagate. @@ -1916,6 +2001,8 @@ def __init__(self, name, default, pkt_cls, **kwargs): def m2i(self, pkt, s): remain, val = self.fld.getfield(pkt, s) + if val is None: + val = NDRNone() # A mistake here would be to use / instead of add_payload. It adds a copy # which breaks pointer defferal. Same applies elsewhere val.add_payload(conf.padding_layer(remain)) @@ -1934,7 +2021,9 @@ def i2len(self, pkt, x): return len(x) def valueof(self, pkt, x): - return [self.fld.valueof(pkt, y) for y in x] + return [ + self.fld.valueof(pkt, y if not isinstance(y, NDRNone) else None) for y in x + ] class NDRFieldListField(NDRConstructedType, FieldListField): @@ -2113,6 +2202,9 @@ class _NDRConfField: COUNT_FROM = False def __init__(self, *args, **kwargs): + # when conformant_in_struct is True, we remove the level of abstraction + # provided by NDRConformantString / NDRConformantArray because max_count + # is a proper field in the parent packet. self.conformant_in_struct = kwargs.pop("conformant_in_struct", False) # size_is/max_is end up here, and is what defines a conformant field. if "size_is" in kwargs: @@ -2265,7 +2357,8 @@ class NDRConfStrLenField(_NDRConfField, _NDRValueOf, StrLenField): NDR Conformant StrLenField. This is not a "string" per NDR, but an a conformant byte array - (e.g. tower_octet_string) + (e.g. tower_octet_string). For ease of use, we implicitly convert + it in specific cases. """ CONFORMANT_STRING = True @@ -2276,7 +2369,7 @@ class NDRConfStrLenFieldUtf16(_NDRConfField, _NDRValueOf, StrLenFieldUtf16, _NDR """ NDR Conformant StrLenFieldUtf16. - See NDRConfLenStrField for comment. + See NDRConfStrLenField for comment. """ CONFORMANT_STRING = True @@ -2415,23 +2508,60 @@ def any2i(self, pkt, x): # Misc -class NDRRecursiveField(Field): +class _ProxyArray: + # Hack for recursive fields DEPORTED_CONFORMANTS field + __slots__ = ["getfld"] + + def __init__(self, getfld): + self.getfld = getfld + + def __len__(self): + try: + return len(self.getfld()) + except AttributeError: + return 0 + + def __iter__(self): + try: + return iter(self.getfld()) + except AttributeError: + return iter([]) + + +class _ProxyTuple: + # Hack for recursive fields ALIGNMENT field + __slots__ = ["getfld"] + + def __init__(self, getfld): + self.getfld = getfld + + def __getitem__(self, name): + try: + return self.getfld()[name] + except AttributeError: + raise KeyError + + +def NDRRecursiveClass(clsname): """ - A special Field that is used for pointer recursion + Return a special class that is used for pointer recursion """ + # Get module where this is called + frame = inspect.currentframe().f_back + mod = frame.f_globals["__loader__"].name + getcls = lambda: getattr(importlib.import_module(mod), clsname) - def __init__(self, name, fmt="I"): - super(NDRRecursiveField, self).__init__(name, None, fmt=fmt) - - def getfield(self, pkt, s): - return NDRFullEmbPointerField(NDRPacketField("", None, pkt.__class__)).getfield( - pkt, s + class _REC(NDRPacket): + ALIGNMENT = _ProxyTuple(lambda: getattr(getcls(), "ALIGNMENT")) + DEPORTED_CONFORMANTS = _ProxyArray( + lambda: getattr(getcls(), "DEPORTED_CONFORMANTS") ) - def addfield(self, pkt, s, val): - return NDRFullEmbPointerField(NDRPacketField("", None, pkt.__class__)).addfield( - pkt, s, val - ) + @classmethod + def dispatch_hook(cls, _pkt=None, *args, **kargs): + return getcls() + + return _REC # The very few NDR-specific structures @@ -2448,6 +2578,16 @@ def guess_payload_class(self, payload): return conf.padding_layer +class NDRNone(NDRPacket): + # This is only used in NDRPacketListField to act as a "None" pointer, and is + # a workaround because the field doesn't support None as a value in the list. + name = "None" + ALIGNMENT = (4, 8) + fields_desc = [ + NDRInt3264Field("ptr", 0), + ] + + # --- Type Serialization Version 1 - [MSRPCE] sect 2.2.6 @@ -2465,6 +2605,25 @@ class NDRSerialization1Header(Packet): XLEIntField("Filler", 0xCCCCCCCC), ] + # Add a bit of goo so that valueof() goes through the header + + def _ndrlayer(self): + cur = self + while cur and not isinstance(cur, _NDRPacket) and cur.payload: + cur = cur.payload + if isinstance(cur, NDRPointer): + cur = cur.value + return cur + + def getfield_and_val(self, attr): + try: + return Packet.getfield_and_val(self, attr) + except ValueError: + return self._ndrlayer().getfield_and_val(attr) + + def valueof(self, name): + return self._ndrlayer().valueof(name) + class NDRSerialization1PrivateHeader(Packet): fields_desc = [ @@ -2475,18 +2634,27 @@ class NDRSerialization1PrivateHeader(Packet): ] -def ndr_deserialize1(b, cls, ndr64=False): +def ndr_deserialize1(b, cls, ptr_pack=False): """ - Deserialize Type Serialization Version 1 according to [MS-RPCE] sect 2.2.6 + Deserialize Type Serialization Version 1 + [MS-RPCE] sect 2.2.6 + + :param ptr_pack: pack in a pointer to the structure. """ if issubclass(cls, NDRPacket): - # We use an intermediary class for two reasons: - # - it properly sets deferred pointers - # - it uses NDRPacketField which handles deported conformant fields - class _cls(NDRPacket): - fields_desc = [ - NDRFullPointerField(NDRPacketField("pkt", None, cls)), - ] + # We use an intermediary class because it uses NDRPacketField which handles + # deported conformant fields + if ptr_pack: + hdrlen = 20 + + class _cls(NDRPacket): + fields_desc = [NDRFullPointerField(NDRPacketField("pkt", None, cls))] + + else: + hdrlen = 16 + + class _cls(NDRPacket): + fields_desc = [NDRPacketField("pkt", None, cls)] hdr = NDRSerialization1Header(b[:8]) / NDRSerialization1PrivateHeader(b[8:16]) endian = {0x00: "big", 0x10: "little"}[hdr.Endianness] @@ -2496,18 +2664,21 @@ class _cls(NDRPacket): return ( hdr / _cls( - b[16 : 20 + hdr.ObjectBufferLength], - ndr64=ndr64, + b[16 : hdrlen + hdr.ObjectBufferLength], + ndr64=False, # Only NDR32 is supported in Type 1 ndrendian=endian, ).pkt - / conf.padding_layer(b[20 + padlen + hdr.ObjectBufferLength :]) + / conf.padding_layer(b[hdrlen + padlen + hdr.ObjectBufferLength :]) ) return NDRSerialization1Header(b[:8]) / cls(b[8:]) -def ndr_serialize1(pkt): +def ndr_serialize1(pkt, ptr_pack=False): """ Serialize Type Serialization Version 1 + [MS-RPCE] sect 2.2.6 + + :param ptr_pack: pack in a pointer to the structure. """ pkt = pkt.copy() endian = getattr(pkt, "ndrendian", "little") @@ -2533,39 +2704,48 @@ def ndr_serialize1(pkt): pkt.payload.remove_payload() # See above about why we need an intermediary class - class _cls(NDRPacket): - fields_desc = [ - NDRFullPointerField(NDRPacketField("pkt", None, cls)), - ] + if ptr_pack: + + class _cls(NDRPacket): + fields_desc = [NDRFullPointerField(NDRPacketField("pkt", None, cls))] + + else: - ret = bytes(pkt / _cls(pkt=val)) + class _cls(NDRPacket): + fields_desc = [NDRPacketField("pkt", None, cls)] + + ret = bytes(pkt / _cls(pkt=val, ndr64=False, ndrendian=endian)) return ret + (-len(ret) % _TYPE1_S_PAD) * b"\x00" class _NDRSerializeType1: def __init__(self, *args, **kwargs): + self.ptr_pack = kwargs.pop("ptr_pack", False) super(_NDRSerializeType1, self).__init__(*args, **kwargs) def i2m(self, pkt, val): - return ndr_serialize1(val) + return ndr_serialize1(val, ptr_pack=self.ptr_pack) def m2i(self, pkt, s): - return ndr_deserialize1(s, self.cls, ndr64=False) + return ndr_deserialize1(s, self.cls, ptr_pack=self.ptr_pack) def i2len(self, pkt, val): return len(self.i2m(pkt, val)) class NDRSerializeType1PacketField(_NDRSerializeType1, PacketField): - __slots__ = ["ptr"] + __slots__ = ["ptr_pack"] class NDRSerializeType1PacketLenField(_NDRSerializeType1, PacketLenField): - __slots__ = ["ptr"] + __slots__ = ["ptr_pack"] class NDRSerializeType1PacketListField(_NDRSerializeType1, PacketListField): - __slots__ = ["ptr"] + __slots__ = ["ptr_pack"] + + def i2len(self, pkt, val): + return sum(len(self.i2m(pkt, p)) for p in val) # --- DCE/RPC session @@ -2577,7 +2757,8 @@ class DceRpcSession(DefaultSession): """ def __init__(self, *args, **kwargs): - self.rpc_bind_interface = None + self.rpc_bind_interface: Union[DceRpcInterface, ComInterface] = None + self.rpc_bind_is_com: bool = False self.ndr64 = False self.ndrendian = "little" self.support_header_signing = kwargs.pop("support_header_signing", True) @@ -2586,6 +2767,8 @@ def __init__(self, *args, **kwargs): self.sspcontext = kwargs.pop("sspcontext", None) self.auth_level = kwargs.pop("auth_level", None) self.auth_context_id = kwargs.pop("auth_context_id", 0) + self.sent_cont_ids = [] + self.cont_id = 0 # Currently selected context self.map_callid_opnum = {} self.frags = collections.defaultdict(lambda: b"") self.sniffsspcontexts = {} # Unfinished contexts for passive @@ -2603,27 +2786,50 @@ def _up_pkt(self, pkt): opts = {} if DceRpc5Bind in pkt or DceRpc5AlterContext in pkt: # bind => get which RPC interface + self.sent_cont_ids = [x.cont_id for x in pkt.context_elem] for ctx in pkt.context_elem: if_uuid = ctx.abstract_syntax.if_uuid if_version = ctx.abstract_syntax.if_version try: self.rpc_bind_interface = DCE_RPC_INTERFACES[(if_uuid, if_version)] + self.rpc_bind_is_com = False except KeyError: - self.rpc_bind_interface = None - log_runtime.warning( - "Unknown RPC interface %s. Try loading the IDL" % if_uuid - ) + try: + self.rpc_bind_interface = COM_INTERFACES[if_uuid] + self.rpc_bind_is_com = True + except KeyError: + self.rpc_bind_interface = None + log_runtime.warning( + "Unknown RPC interface %s. Try loading the IDL" % if_uuid + ) elif DceRpc5BindAck in pkt or DceRpc5AlterContextResp in pkt: # bind ack => is it NDR64 - for res in pkt.results: + for i, res in enumerate(pkt.results): if res.result == 0: # Accepted + # Context + try: + self.cont_id = self.sent_cont_ids[i] + except IndexError: + self.cont_id = 0 + finally: + self.sent_cont_ids = [] + + # Endianness self.ndrendian = {0: "big", 1: "little"}[pkt[DceRpc5].endian] + + # Transfer syntax if res.transfer_syntax.sprintf("%if_uuid%") == "NDR64": self.ndr64 = True elif DceRpc5Request in pkt: # request => match opnum with callID opnum = pkt.opnum - self.map_callid_opnum[pkt.call_id] = opnum, pkt[DceRpc5Request].payload + if self.rpc_bind_is_com: + self.map_callid_opnum[pkt.call_id] = ( + opnum, + pkt[DceRpc5Request].payload.payload, + ) + else: + self.map_callid_opnum[pkt.call_id] = opnum, pkt[DceRpc5Request].payload elif DceRpc5Response in pkt: # response => get opnum from table try: @@ -2862,6 +3068,21 @@ def in_pkt(self, pkt): pkt.payload[conf.raw_layer].load = body return pkt if body: + orpc = None + if self.rpc_bind_is_com: + # If interface is a COM interface, start off by dissecting the + # ORPCTHIS / ORPCTHAT argument + from scapy.layers.msrpce.raw.ms_dcom import ORPCTHAT, ORPCTHIS + + # [MS-DCOM] sect 2.2.13 + # "ORPCTHIS and ORPCTHAT structures MUST be marshaled using + # the NDR (32) Transfer Syntax" + if is_response: + orpc = ORPCTHAT(body, ndr64=False) + else: + orpc = ORPCTHIS(body, ndr64=False) + body = orpc.load + orpc.remove_payload() # Dissect payload using class try: payload = cls( @@ -2881,6 +3102,8 @@ def in_pkt(self, pkt): ) pad = payload[conf.padding_layer] pad.underlayer.payload = conf.raw_layer(load=pad.load) + if orpc is not None: + pkt /= orpc pkt /= payload # If a request was encrypted, we need to re-register it once re-parsed. if not is_response and self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY: @@ -2917,15 +3140,15 @@ def out_pkt(self, pkt): RPC_C_AUTHN_LEVEL.PKT_INTEGRITY, RPC_C_AUTHN_LEVEL.PKT_PRIVACY, ): + # Remember that vt_trailer is included in the PDU + if pkt.vt_trailer: + body += bytes(pkt.vt_trailer) # Account for padding when computing checksum/encryption if pkt.auth_padding is None: padlen = (-len(body)) % _COMMON_AUTH_PAD # authdata padding pkt.auth_padding = b"\x00" * padlen else: padlen = len(pkt.auth_padding) - # Remember that vt_trailer is included in the PDU - if pkt.vt_trailer: - body += bytes(pkt.vt_trailer) # Remember that padding IS SIGNED & ENCRYPTED body += pkt.auth_padding # Add the auth_verifier diff --git a/scapy/layers/msrpce/ept.py b/scapy/layers/msrpce/ept.py index d024e2c336a..1f51993f311 100644 --- a/scapy/layers/msrpce/ept.py +++ b/scapy/layers/msrpce/ept.py @@ -24,8 +24,9 @@ ) from scapy.packet import Packet from scapy.layers.dcerpc import ( - DCE_RPC_INTERFACES_NAMES, DCE_RPC_INTERFACES_NAMES_rev, + DCE_RPC_INTERFACES_NAMES, + DCE_RPC_PROTOCOL_IDENTIFIERS, DCE_RPC_TRANSFER_SYNTAXES, ) @@ -70,37 +71,10 @@ class prot_and_addr_t(Packet): "lhs_length", 0, ), - # [C706] Appendix I with names from Appendix B ByteEnumField( "protocol_identifier", 0, - { - 0x0: "OSI OID", # Special - 0x0D: "UUID", # Special - # Transports - # 0x2: "DNA Session Control", - # 0x3: "DNA Session Control V3", - # 0x4: "DNA NSP Transport", - # 0x5: "OSI TP4", - 0x06: "NCADG_OSI_CLSN", # [C706] - 0x07: "NCACN_IP_TCP", # [C706] - 0x08: "NCADG_IP_UDP", # [C706] - 0x09: "IP", # [C706] - 0x0A: "RPC connectionless protocol", # [C706] - 0x0B: "RPC connection-oriented protocol", # [C706] - 0x0C: "NCALRPC", - 0x0F: "NCACN_NP", # [MS-RPCE] - 0x11: "NCACN_NB", # [C706] - 0x12: "NCACN_NB_NB", # [MS-RPCE] - 0x13: "NCACN_SPX", # [C706] - 0x14: "NCADG_IPX", # [C706] - 0x16: "NCACN_AT_DSP", # [C706] - 0x17: "NCADG_AT_DSP", # [C706] - 0x19: "NCADG_NB", # [C706] - 0x1A: "NCACN_VNS_SPP", # [C706] - 0x1B: "NCADG_VNS_IPC", # [C706] - 0x1F: "NCACN_HTTP", # [MS-RPCE] - }, + DCE_RPC_PROTOCOL_IDENTIFIERS, ), # 0x0 ConditionalField( diff --git a/scapy/layers/msrpce/msdcom.py b/scapy/layers/msrpce/msdcom.py index 4ed6cd221fb..cc4208acc4a 100644 --- a/scapy/layers/msrpce/msdcom.py +++ b/scapy/layers/msrpce/msdcom.py @@ -9,58 +9,95 @@ https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dcom/4a893f3d-bd29-48cd-9f43-d9777a4415b0 """ -import collections +import enum +import hashlib +import re +import socket import uuid from scapy.config import conf from scapy.packet import Packet, bind_layers from scapy.fields import ( ConditionalField, + FieldLenField, + FlagsField, LEIntField, + LELongField, LEShortEnumField, LEShortField, PacketField, PacketListField, + PadField, + StrLenField, StrNullFieldUtf16, UUIDField, - XStrFixedLenField, XShortField, + XStrFixedLenField, ) +from scapy.volatile import RandUUID + from scapy.layers.dcerpc import ( + ComInterface, + DCE_C_AUTHN_LEVEL, + DCE_RPC_PROTOCOL_IDENTIFIERS, + DceRpc5Request, + find_com_interface, + find_dcerpc_interface, + ndr_deserialize1, + NDRConfFieldListField, + NDRConfPacketListField, + NDRConfVarStrNullFieldUtf16, NDRFieldListField, + NDRFullEmbPointerField, + NDRFullPointerField, + NDRIntEnumField, NDRIntField, NDRLongField, NDRPacket, NDRPacketField, - NDRFullEmbPointerField, - NDRFullPointerField, - NDRConfPacketListField, - NDRConfFieldListField, - NDRConfStrLenFieldUtf16, - NDRConfVarStrNullFieldUtf16, - NDRShortField, - NDRSignedIntField, NDRSerializeType1PacketField, NDRSerializeType1PacketListField, - ndr_deserialize1, - find_dcerpc_interface, + NDRShortField, + NDRSignedIntField, RPC_C_AUTHN, ) +from scapy.utils import valid_ip6, valid_ip from scapy.layers.msrpce.rpcclient import DCERPC_Client, DCERPC_Transport from scapy.layers.msrpce.raw.ms_dcom import ( COMVERSION, + DUALSTRINGARRAY, GUID, - ServerAlive2_Request, MInterfacePointer, + ORPCTHAT, + ORPCTHIS, + REMINTERFACEREF, + RemoteCreateInstance_Request, + RemoteCreateInstance_Response, + RemoteGetClassObject_Request, + RemoteGetClassObject_Response, + RemQueryInterface_Request, + RemRelease_Request, + ResolveOxid2_Request, + ServerAlive2_Request, + tagCPFLAGS, +) + +# Typing +from typing import ( + Any, + List, + Dict, + Optional, + Tuple, ) def _uid_to_bytes(x, ndrendian="little"): if ndrendian == "little": - return uuid.UUID(x).bytes_le + return x.bytes_le elif ndrendian == "big": - return uuid.UUID(x).bytes + return x.bytes else: raise ValueError("bad ndrendian") @@ -101,6 +138,13 @@ def _uid_from_bytes(x, ndrendian="little"): # [MS-DCOM] 2.2.22.2.1 +class ACTVFLAGS(enum.IntEnum): + DISABLE_AAA = 0x00000002 + ACTIVATE_32_BIT_SERVER = 0x00000004 + ACTIVATE_64_BIT_SERVER = 0x00000008 + NO_FAILURE_LOG = 0x00000020 + + class InstantiationInfoData(NDRPacket): ALIGNMENT = (4, 8) fields_desc = [ @@ -108,7 +152,7 @@ class InstantiationInfoData(NDRPacket): NDRIntField("classCtx", 0), NDRIntField("actvflags", 0), NDRSignedIntField("fIsSurrogate", 0), - NDRIntField("cIID", 0), + NDRIntField("cIID", None, size_of="pIID"), NDRIntField("instFlag", 0), NDRFullEmbPointerField( NDRConfPacketListField( @@ -116,7 +160,11 @@ class InstantiationInfoData(NDRPacket): ), ), NDRIntField("thisSize", 0), - NDRPacketField("clientCOMVersion", COMVERSION(), COMVERSION), + NDRPacketField( + "clientCOMVersion", + COMVERSION(), + COMVERSION, + ), ] @@ -126,18 +174,28 @@ class InstantiationInfoData(NDRPacket): class SpecialPropertiesData(NDRPacket): ALIGNMENT = (8, 8) fields_desc = [ - NDRIntField("dwSessionId", 0), + NDRIntField("dwSessionId", 0xFFFFFFFF), NDRSignedIntField("fRemoteThisSessionId", 0), NDRSignedIntField("fClientImpersonating", 0), NDRSignedIntField("fPartitionIDPresent", 0), - NDRIntField("dwDefaultAuthnLvl", 0), + NDRIntField( + "dwDefaultAuthnLvl", DCE_C_AUTHN_LEVEL.PKT_INTEGRITY + ), # Same than Windows NDRPacketField("guidPartition", GUID(), GUID), NDRIntField("dwPRTFlags", 0), NDRIntField("dwOrigClsctx", 0), - NDRIntField("dwFlags", 0), + NDRIntEnumField( + "dwFlags", + 0, + { + 0x00000001: "SPD_FLAG_USE_CONSOLE_SESSION", + }, + ), NDRIntField("Reserved1", 0), NDRLongField("Reserved2", 0), - NDRFieldListField("Reserved3", [], NDRIntField("", 0), count_from=lambda _: 5), + NDRFieldListField( + "Reserved3", [0, 0, 0, 0, 0], NDRIntField("", 0), count_from=lambda _: 5 + ), ] @@ -164,11 +222,14 @@ class InstanceInfoData(NDRPacket): class customREMOTE_REQUEST_SCM_INFO(NDRPacket): ALIGNMENT = (4, 8) fields_desc = [ - NDRIntField("ClientImpLevel", 0), - NDRShortField("cRequestedProtseqs", 0), + NDRIntField("ClientImpLevel", 2), # note <33> + NDRShortField("cRequestedProtseqs", None, size_of="pRequestedProtseqs"), NDRFullEmbPointerField( - NDRConfStrLenFieldUtf16( - "pRequestedProtseqs", "", length_from=lambda pkt: pkt.cRequestedProtseqs + NDRConfFieldListField( + "pRequestedProtseqs", + [], + NDRShortField("", 0), + size_is=lambda pkt: pkt.cRequestedProtseqs, ), ), ] @@ -214,7 +275,7 @@ class LocationInfoData(NDRPacket): ALIGNMENT = (4, 8) fields_desc = [ NDRFullEmbPointerField( - NDRConfVarStrNullFieldUtf16("machineName", ""), + NDRConfVarStrNullFieldUtf16("machineName", None), ), NDRIntField("processId", 0), NDRIntField("apartmentId", 0), @@ -242,36 +303,10 @@ class SecurityInfoData(NDRPacket): NDRFullEmbPointerField( NDRPacketField("pServerInfo", COSERVERINFO(), COSERVERINFO), ), - NDRFullPointerField(NDRIntField("pdwReserved", 0)), + NDRFullPointerField(NDRIntField("pdwReserved", None)), ] -# [MS-DCOM] 2.2.22.2.8 - - -class DUALSTRINGARRAY(NDRPacket): - ALIGNMENT = (4, 8) - CONFORMANT_COUNT = 1 - fields_desc = [ - NDRShortField("wNumEntries", 0), - NDRShortField("wSecurityOffset", 0), - NDRConfStrLenFieldUtf16( - "aStringArray", "", length_from=lambda pkt: pkt.wNumEntries - ), - ] - - -def _parseStringArray(self): - """ - Process aStringArray - """ - str_fld = PacketListField("", [], STRINGBINDING) - sec_fld = PacketListField("", [], SECURITYBINDING) - string = str_fld.getfield(self, self.aStringArray[: self.wSecurityOffset * 2])[1] - secs = sec_fld.getfield(self, self.aStringArray[self.wSecurityOffset * 2 :])[1] - return string, secs - - class customREMOTE_REPLY_SCM_INFO(NDRPacket): ALIGNMENT = (8, 8) fields_desc = [ @@ -305,27 +340,26 @@ class ScmReplyInfoData(NDRPacket): class PropsOutInfo(NDRPacket): ALIGNMENT = (4, 8) fields_desc = [ - NDRIntField("cIfs", 0), + NDRIntField("cIfs", None, size_of="ppIntfData"), NDRFullEmbPointerField( - NDRConfPacketListField( - "piid", [GUID()], GUID, count_from=lambda pkt: pkt.cIfs - ), + NDRConfPacketListField("piid", [], GUID, size_is=lambda pkt: pkt.cIfs) ), NDRFullEmbPointerField( NDRConfFieldListField( "phresults", [], - NDRSignedIntField("", 0), - count_from=lambda pkt: pkt.cIfs, - ), + NDRSignedIntField("phresults", 0), + size_is=lambda pkt: pkt.cIfs, + ) ), NDRFullEmbPointerField( NDRConfPacketListField( "ppIntfData", - [MInterfacePointer()], + [], MInterfacePointer, - count_from=lambda pkt: pkt.cIfs, - ), + size_is=lambda pkt: pkt.cIfs, + ptr_pack=True, + ) ), ] @@ -339,8 +373,8 @@ class CustomHeader(NDRPacket): NDRIntField("totalSize", 0), NDRIntField("headerSize", 0), NDRIntField("dwReserved", 0), - NDRIntField("destCtx", 0), - NDRIntField("cIfs", 0), + NDRIntEnumField("destCtx", 2, {2: "MSHCTX_DIFFERENTMACHINE"}), + NDRIntField("cIfs", None, size_of="pSizes"), NDRPacketField("classInfoClsid", GUID(), GUID), NDRFullEmbPointerField( NDRConfPacketListField( @@ -349,26 +383,27 @@ class CustomHeader(NDRPacket): ), NDRFullEmbPointerField( NDRConfFieldListField( - "pSizes", [], NDRIntField("", 0), count_from=lambda pkt: pkt.cIfs + "pSizes", None, NDRIntField("", 0), count_from=lambda pkt: pkt.cIfs ), ), - NDRFullEmbPointerField(NDRIntField("pdwReserved", 0)), + NDRFullEmbPointerField(NDRIntField("pdwReserved", None)), ] class _ActivationPropertiesField(NDRSerializeType1PacketListField): def __init__(self, *args, **kwargs): kwargs["next_cls_cb"] = self._get_cls_activation - # kwargs["ptr"] = False super(_ActivationPropertiesField, self).__init__(*args, **kwargs) def _get_cls_activation(self, pkt, lst, cur, remain): - pclsid = pkt.CustomHeader.data.pclsid.value.value - ndrendian = pkt.CustomHeader.data.ndrendian + # Get all the pcslsid + pclsid = pkt.CustomHeader[CustomHeader].valueof("pclsid") + ndrendian = pkt.CustomHeader[CustomHeader].ndrendian i = len(lst) + int(bool(cur)) if i >= len(pclsid): return - next_uid = _uid_from_bytes(pclsid[i], ndrendian=ndrendian) + # Get the next pclsid we need to process + next_uid = _uid_from_bytes(bytes(pclsid[i]), ndrendian=ndrendian) # [MS-DCOM] 1.9 cls = { CLSID_ActivationContextInfo: ActivationContextInfoData, @@ -381,12 +416,19 @@ def _get_cls_activation(self, pkt, lst, cur, remain): CLSID_ServerLocationInfo: LocationInfoData, CLSID_SpecialSystemProperties: SpecialPropertiesData, }[next_uid] - return lambda x: ndr_deserialize1(x, cls, ndr64=False) + return lambda x: ndr_deserialize1(x, cls) class ActivationPropertiesBlob(Packet): fields_desc = [ - LEIntField("dwSize", 0), + FieldLenField( + "dwSize", + None, + fmt=" Tuple[List[STRINGBINDING], List[SECURITYBINDING]]: + """ + Process aStringArray in a DUALSTRINGARRAY to extract string bindings and + security bindings. + """ + str_fld = PacketListField("", [], STRINGBINDING) + sec_fld = PacketListField("", [], SECURITYBINDING) + string = str_fld.getfield(dual, dual.aStringArray[: dual.wSecurityOffset * 2])[1] + secs = sec_fld.getfield(dual, dual.aStringArray[dual.wSecurityOffset * 2 :])[1] + if string[-1].wTowerId != 0 or secs[-1].wAuthnSvc != 0: + raise ValueError("Invalid DUALSTRINGARRAY !") + return string[:-1], secs[:-1] + + +def _HashStringBinding(strings: List[STRINGBINDING]): + """ + Hash a STRINGBINDING list + """ + return hashlib.sha256(b"".join(bytes(x) for x in strings)).digest() + + +# Entries. + + +class IPID_Entry: + """ + An entry in the IPID table + [MS-DCOM] 3.1.1.1 Abstract Data Model + """ + + def __init__(self): + self.ipid: Optional[uuid.UUID] = None + self.iid: Optional[uuid.UUID] = None + self.oid: Optional[int] = None + self.oxid: Optional[int] = None + self.cPublicRefs: int = 0 + self.cPrivateRefs: int = 0 + self.state: Any = None + # Additions + self.iface: Optional[ComInterface] = None + + +class OID_Entry: + """ + An entry in the OID table + [MS-DCOM] 3.1.1.1 Abstract Data Model + """ + + def __init__(self): + self.oid: Optional[int] = None + self.oxid: Optional[int] = None + self.ipids: List[uuid.UUID] = [] + self.hash: Optional[bytes] = None + self.last_orpc: int = None + self.garbage_collection: bool = True + self.state = None + + +class Resolver_Entry: + """ + An entry in the Resolver table. + [MS-DCOM] 3.2.1 Abstract Data Model + """ + + def __init__(self): + self.hash: Optional[bytes] = None + self.binds: List[STRINGBINDING] = [] + self.secs: List[SECURITYBINDING] = [] + self.setid: Optional[int] = None + self.client: Optional[DCERPC_Client] = None + + +class SETID_Entry: + """ + An entry in the SETID table. + [MS-DCOM] 3.2.1 Abstract Data Model + """ + + def __init__(self): + self.setid: Optional[int] = None + self.oids: List[int] = [] + self.seq: Optional[int] = None + + +class OXID_Entry: + """ + An entry in the OXID table. + [MS-DCOM] 3.2.1 Abstract Data Model + """ + + def __init__(self): + self.oxid: Optional[int] = None + self.bindingInfo: Optional[Tuple[str, int]] = None + self.authnHint: DCE_C_AUTHN_LEVEL = DCE_C_AUTHN_LEVEL.CONNECT + self.version: Optional[COMVERSION] = None + self.ipid_IRemUnknown: Optional[uuid.UUID] = None + + def __repr__(self): + return f"" + + +class ObjectInstance: + """ + An reference to an instantiated object. + + This is a helper to manipulate this object and perform calls over it. + """ + + def __init__(self, client: "DCOM_Client", oid: int): + self.client = client + self.oid = oid + + def __repr__(self): + return f"" + + @property + def valid(self): + """ + Returns whether the current object still exists + """ + return self.oid in self.client.OID_table + + @property + def ndr64(self): + """ + Whether NDR64 is required to talk to this object + """ + return self.client.ndr64 + + def sr1_req( + self, + pkt: NDRPacket, + iface: ComInterface, + ssp=None, + auth_level=None, + timeout=None, + **kwargs, + ): + """ + Make an ORPC call on this object instance. + + :param iface: the ComInterface to call. + :param pkt: the request to make. + + :param ssp: (optional) non default SSP to use to connect to the object exporter + :param auth_level: (optional) non default authn level to use + :param timeout: (optional) timeout for the connection + """ + # Look for this object's entry + try: + oid_entry = self.client.OID_table[self.oid] + except KeyError: + raise ValueError("This object has been released.") + + # Look for the ipid matching the interface required by the user + ipid = None + for ipid in oid_entry.ipids: + ipid_entry = self.client.IPID_table[ipid] + if ipid_entry.iid == iface.uuid: + break + else: + # Acquire interface on the object + self.client.AcquireInterface( + ipid=oid_entry.ipids[0], + iids=[ + iface, + ], + cPublicRefs=1, + ) + + return self.client.sr1_orpc_req( + ipid=ipid, + pkt=pkt, + ssp=ssp, + auth_level=auth_level, + timeout=timeout, + **kwargs, + ) + + def release(self): + """ + Call IRemUnknown2::RemRelease to release counts on an object reference. + """ + for ipid in self.client.OID_table[self.oid].ipids: + self.client.RemRelease(ipid) class DCOM_Client(DCERPC_Client): """ A wrapper of DCERPC_Client that adds functions to use COM interfaces. - In this client, the DCE/RPC is abstracted to allow to focus on the upper - DCOM one. DCE/RPC interfaces are bound automatically and ORPCTHIS/ORPCTHAT - automatically added/extracted. - - It also provides common handlers for the few [MS-DCOM] special interfaces. + :param cid: the client identifier """ - def __init__(self, verb=True, **kwargs): + IREMUNKNOWN = find_com_interface("IRemUnknown2") + + def __init__(self, cid: GUID = None, verb=True, **kwargs): + # Pick a random cid to identify this client + self.cid = cid or GUID(RandUUID().bytes_le) + + # The OXID table kept up-to-date by the client + self.OXID_table: Dict[int, OXID_Entry] = {} + + # The IPID table kept up-to-date by the client + self.IPID_table: Dict[int, IPID_Entry] = {} + + # The OID table kept up-to-date by the client + self.OID_table: Dict[int, OID_Entry] = {} + + # The Resolver table kept up-to-date by the client + self.Resolver_table: Dict[STRINGBINDING, Resolver_Entry] = {} + + # DCOM defaults to at least PKT_INTEGRITY + if "auth_level" not in kwargs and "ssp" in kwargs: + kwargs["auth_level"] = DCE_C_AUTHN_LEVEL.PKT_INTEGRITY + super(DCOM_Client, self).__init__( - DCERPC_Transport.NCACN_IP_TCP, ndr64=False, verb=verb, **kwargs + DCERPC_Transport.NCACN_IP_TCP, + ndr64=False, + verb=verb, + **kwargs, + ) + + def connect(self, host: str, timeout=5): + """ + Initiate a connection to the object resolver. + + :param host: the host to connect to + :param timeout: (optional) the connection timeout (default 5) + """ + # [MS-DCOM] 3.2.4.1.2.1 Determining RPC Binding Information + binds, _ = ServerAlive2(host) + host, port = self._ChoseRPCBinding(binds) + + super(DCOM_Client, self).connect( + host=host, + port=port, + timeout=timeout, ) - def connect(self, *args, **kwargs): - kwargs.setdefault("port", 135) - super(DCOM_Client, self).connect(*args, **kwargs) + def sr1_req(self, pkt, **kwargs): + raise NotImplementedError("Cannot use sr1_req on DCOM_Client !") + + def _GetObjectInstance(self, oid: int): + """ + Internal function to get an ObjectInstance from an oid + """ + return ObjectInstance( + client=self, + oid=oid, + ) - def ServerAlive2(self): + def _RemoteCreateInstanceOrGetClassObject( + self, + clsreq, + clsresp, + clsid: uuid.UUID, + iids: List[ComInterface], + ) -> ObjectInstance: """ - Call IObjectExporter::ServerAlive2 + Internal function common to RemoteCreateInstance and RemoteGetClassObject """ - self.bind_or_alter(find_dcerpc_interface("IObjectExporter")) - resp = self.sr1_req(ServerAlive2_Request(ndr64=False)) - binds, secs = _parseStringArray(resp.ppdsaOrBindings.value) - DCOMResults = collections.namedtuple("DCOMResults", ["addresses", "ssps"]) - addresses = [] - ssps = [] - for b in binds: - if b.wTowerId == 0: + if not iids: + raise ValueError("Must specify at least one interface !") + + # Bind IObjectExporter if not already + self.bind_or_alter(find_dcerpc_interface("IRemoteSCMActivator")) + + # [MS-DCOM] sect 3.1.2.5.2.3.3 - Issuing the Activation Request + + # Build the activation properties + ActivationProperties = [ + SpecialPropertiesData( + # Same as windows + dwDefaultAuthnLvl=self.auth_level, + dwOrigClsctx=16, + dwFlags=2, # ??? + ndr64=False, + ), + InstantiationInfoData( + classId=GUID(_uid_to_bytes(clsid)), + classCtx=16, + actvflags=0, + fIsSurrogate=0, + clientCOMVersion=COMVERSION( + MajorVersion=5, + MinorVersion=7, + ), + pIID=[GUID(_uid_to_bytes(x.uuid)) for x in iids], + ndr64=False, + ), + ActivationContextInfoData( + pIFDClientCtx=MInterfacePointer( + abData=OBJREF(iid=IID_IContext) + / OBJREF_CUSTOM( + clsid=CLSID_ContextMarshaler, + pObjectData=Context( + ContextId=uuid.UUID("53394e9f-e973-4bf0-a341-154519534fe1"), + Flags="CTXMSHLFLAGS_BYVAL", + ), + ), + ), + ndr64=False, + ), + SecurityInfoData( + pServerInfo=COSERVERINFO( + pwszName=self.host, + ), + ndr64=False, + ), + LocationInfoData(ndr64=False), + ScmRequestInfoData( + remoteRequest=customREMOTE_REQUEST_SCM_INFO( + pRequestedProtseqs=[ + # Note <51> for Windows Vista and later + int(DCERPC_Transport.NCACN_IP_TCP), + ] + ), + ndr64=False, + ), + ] + + # Build CustomHeader + hdr = CustomHeader( + pclsid=[ + GUID(_uid_to_bytes(CLSID_SpecialSystemProperties)), + GUID(_uid_to_bytes(CLSID_InstantiationInfo)), + GUID(_uid_to_bytes(CLSID_ActivationContextInfo)), + GUID(_uid_to_bytes(CLSID_SecurityInfo)), + GUID(_uid_to_bytes(CLSID_ServerLocationInfo)), + GUID(_uid_to_bytes(CLSID_ScmRequestInfo)), + ], + pSizes=[ + # Account for the size of the Type1 header + padding + len(x) + 16 + (-len(x) % 8) + for x in ActivationProperties + ], + ndr64=False, + ) + hdr.headerSize = len(hdr) + 16 # 16: size of the Type1 serialization header + hdr.totalSize = hdr.headerSize + sum(hdr.valueof("pSizes")) + + # Build final request + pkt = clsreq( + orpcthis=ORPCTHIS( + version=COMVERSION( + MajorVersion=5, + MinorVersion=7, + ), + flags=tagCPFLAGS.CPFLAG_PROPAGATE, + cid=self.cid, + ), + pActProperties=MInterfacePointer( + abData=OBJREF(iid=IID_IActivationPropertiesIn) + / OBJREF_CUSTOM( + clsid=CLSID_ActivationPropertiesIn, + pObjectData=ActivationPropertiesBlob( + CustomHeader=hdr, + Property=ActivationProperties, + ), + ), + ), + ndr64=False, + ) + + if isinstance(pkt, RemoteCreateInstance_Request): + pkt.pUnkOuter = None + + # Send and receive + resp = super(DCOM_Client, self).sr1_req(pkt) + if not resp or resp.status != 0: + raise ValueError("%s failed." % clsreq.__name__) + + entry = OXID_Entry() + objrefs = [] + + # [MS-DCOM] sect 3.2.4.1.1.3 - Updating the Client OXID Table after Activation + abData = OBJREF(resp.valueof("ppActProperties").abData) + for prop in abData.pObjectData.Property: + if ScmReplyInfoData in prop: + # Information about the object exporter the server found for us + remoteReply = prop[ScmReplyInfoData].valueof("remoteReply") + + # Get OXID, IPID, COMVERSION, authentication level hint + entry.oxid = remoteReply.Oxid + entry.version = remoteReply.serverVersion + entry.authnHint = DCE_C_AUTHN_LEVEL(remoteReply.authnHint) + entry.ipid_IRemUnknown = _uid_from_bytes( + bytes(remoteReply.ipidRemUnknown), ndrendian=remoteReply.ndrendian + ) + + # Set RPC bindings from the activation request + binds, _ = _ParseStringArray(remoteReply.valueof("pdsaOxidBindings")) + entry.bindingInfo = self._ChoseRPCBinding(binds) + + if PropsOutInfo in prop: + # Information about the interfaces that the client requested + info = prop[PropsOutInfo] + + # Check that all interfaces were obtained + phresults = info.valueof("phresults") + if any(x > 0 for x in phresults): + raise ValueError( + "Interfaces %s were not obtained !" + % [iids[i] for i, x in enumerate(phresults) if x > 0] + ) + + # Now store the object references for each interface + for i, ptr in enumerate(info.valueof("ppIntfData")): + if phresults[i] == 0: + objrefs.append(OBJREF(ptr.abData)) + else: + objrefs.append(None) + + # Update the OXID table + if entry.oxid not in self.OXID_table: + self.OXID_table[entry.oxid] = entry + + # Get oid + oid = objrefs[0].std.oid + + # Add an entry to the IPID table for the RemUnknown + if entry.ipid_IRemUnknown not in self.IPID_table: + ipid_entry = IPID_Entry() + ipid_entry.iface = self.IREMUNKNOWN + ipid_entry.iid = self.IREMUNKNOWN.uuid + ipid_entry.oxid = entry.oxid + ipid_entry.oid = oid + self.IPID_table[entry.ipid_IRemUnknown] = ipid_entry + + # "For each object reference returned from the activation request for + # which the corresponding status code indicates success, the client MUST + # unmarshal the object reference" + for i, obj in enumerate(objrefs): + if obj is None: continue - addresses.append(b.aNetworkAddr) - for b in secs: - ssps.append( - "%s%s" - % ( - b.sprintf("%wAuthnSvc%"), - b.aPrincName and "%s/" % b.aPrincName or "", + # Unmarshall + self._UnmarshallObjref(obj, iid=iids[i]) + + return self._GetObjectInstance(oid=oid) + + def _UnmarshallObjref( + self, + obj: OBJREF, + iid: Optional[ComInterface] = None, + ) -> int: + """ + [MS-DCOM] sect 3.2.4.1.2 - Unmarshaling an Object Reference + + :param iid: "IID specified by the application when unmarshalling the object + reference" (see [MS-DCOM] sect 4.5) + """ + # "If the OBJREF_STANDARD flag is set" + if OBJREF_STANDARD in obj and iid: + # "the client MUST look up the OXID entry in the OXID + # table using the OXID from the STDOBJREF" + try: + ox = self.OXID_table[obj.std.oxid] + except KeyError: + # "If the table entry is not found" + + # "determine the RPC binding information to be used" + binds, _ = _ParseStringArray(obj.saResAddr) + host, port = self._ChoseRPCBinding(binds) + + # "issue OXID resolution" + ox = self.ResolveOxid2(oxid=obj.std.oxid, host=host, port=port) + + # "Next, the client MUST update its tables" + self._UpdateTables(iid, ox, obj, obj.std) + + # "Finally, the client MUST compare the IID in the OBJREF with the + # IID specified by the application" + if obj.iid != iid.uuid: + # "First, the client SHOULD acquire an object reference of the IID + # specified by the application" + self.AcquireInterface( + ipid=obj.std.ipid, + iids=[ + iid, + ], + cPublicRefs=1, + ) + + # "Next, the client MUST release the object reference unmarshaled + # from the OBJREF" + self.RemRelease(obj.std.ipid) + + return obj.std.oid + else: + obj.show() + raise NotImplementedError("Non OBJREF_STANDARD ! Please report.") + + def _UpdateTables( + self, + iface: ComInterface, + ox: OXID_Entry, + obj: OBJREF, + std: STDOBJREF, + ) -> None: + """ + [MS-DCOM] 3.2.4.1.2.3 Updating Client Tables After Unmarshaling + """ + # [MS-DCOM] 3.2.4.1.2.3.1 Updating the OXID + if std.oxid not in self.OXID_table: + self.OXID_table[std.oxid] = ox + + # [MS-DCOM] 3.2.4.1.2.3.2 Updating the OID/IPID/Resolver + if std.ipid in self.IPID_table: + self.IPID_table[std.ipid].cPublicRefs += std.cPublicRefs + else: + entry = IPID_Entry() + entry.ipid = std.ipid + entry.oxid = std.oxid + entry.oid = std.oid + entry.iid = obj.iid + entry.iface = iface + entry.cPublicRefs = std.cPublicRefs + if entry.cPublicRefs == 0: + # "If the STDOBJREF contains a public reference count of zero, + # the client MUST obtain additional references on the interface" + raise NotImplementedError("Should acquire additional references !") + entry.cPrivateRefs = 0 + self.IPID_table[std.ipid] = entry + + if std.oid in self.OID_table: + oid_entry = self.OID_table[std.oid] + if std.ipid not in oid_entry.ipids: + oid_entry.ipids.append(std.ipid) + else: + binds, secs = _ParseStringArray(obj.saResAddr) + + oid_entry = OID_Entry() + oid_entry.oid = std.oid + oid_entry.oxid = std.oxid + oid_entry.ipids.append(std.ipid) + oid_entry.garbage_collection = not std.flags.SORF_NOPING + oid_entry.hash = _HashStringBinding(binds) + self.OID_table[std.oid] = oid_entry + + if oid_entry.hash not in self.Resolver_table: + resolver_entry = Resolver_Entry() + resolver_entry.setid = 0 + resolver_entry.hash = oid_entry.hash + resolver_entry.binds = binds + resolver_entry.secs = secs + self.Resolver_table[oid_entry.hash] = resolver_entry + + def _ChoseRPCBinding(self, bindings: List[STRINGBINDING]): + """ + [MS-DCOM] 3.2.4.1.2.1 - Determining RPC Binding Information for OXID Resolution + """ + # We don't try security bindings, only string ones (connection). + # We take the first valid one. + for binding in bindings: + # Only NCACN_IP_TCP is supported by DCOM + if binding.wTowerId == DCERPC_Transport.NCACN_IP_TCP: + # [MS-DCOM] 2.2.19.3 + m = re.match(r"(.*)\[(.*)\]", binding.aNetworkAddr) + if m: + host, port = m.group(1), int(m.group(2)) + else: + host, port = binding.aNetworkAddr, 135 + + # Check validity of the host/port tuple + if valid_ip6(host): + # IPv6 + pass + elif valid_ip(host): + # IPv4 + pass + else: + # Netbios/FQDN + try: + socket.gethostbyname(host) + except Exception: + # Resolution failed. Skip. + continue + + # Success + return host, port + raise ValueError("No valid bindings available !") + + def UnmarshallObjectReference( + self, mifaceptr: MInterfacePointer, iid: ComInterface + ): + """ + [MS-DCOM] 3.2.4.3 Marshaling an Object Reference + + Unmarshall a MInterfacePointer received by the applicative layer. + """ + oid = self._UnmarshallObjref(obj=OBJREF(mifaceptr.abData), iid=iid) + return self._GetObjectInstance(oid) + + def ResolveOxid2( + self, oxid: int, host: Optional[str] = None, port: Optional[int] = None + ): + """ + [MS-DCOM] 3.2.4.1.2.2 Issuing the OXID Resolution Request + + :param oxid: the OXID to resolve + :param host: (optional) connect to a different host + :param port: (optional) connect to a different port + """ + + if host == self.host and port == self.port: + host = self.host + port = self.port + client = self + else: + # Create and connect client + client = DCOM_Client( + # Note <85>: Windows uses INTEGRITY + auth_level=DCE_C_AUTHN_LEVEL.PKT_INTEGRITY, + ssp=self.ssp, + ) + client.connect(host, port=port) + + # Bind IObjectExporter if not already + client.bind_or_alter(find_dcerpc_interface("IObjectExporter")) + + try: + # Perform ResolveOxid2 + resp = super(DCOM_Client, client).sr1_req( + ResolveOxid2_Request( + pOxid=oxid, + arRequestedProtseqs=[ + DCERPC_Transport.NCACN_IP_TCP, + ], + ndr64=self.ndr64, ) ) - return DCOMResults(addresses, ssps) + finally: + if host != self.host or port != self.port: + client.close() + + # Entry + if oxid in self.OXID_table: + entry = self.OXID_table[oxid] + else: + entry = OXID_Entry() + + # Get OXID, IPID, COMVERSION, authentication level hint + entry.oxid = oxid + entry.version = resp.pComVersion + entry.authnHint = DCE_C_AUTHN_LEVEL(resp.pAuthnHint) + entry.ipid_IRemUnknown = _uid_from_bytes( + bytes(resp.pipidRemUnknown), ndrendian=resp.ndrendian + ) + + # Set RPC bindings from the oxid request + binds, _ = _ParseStringArray(resp.valueof("ppdsaOxidBindings")) + entry.bindingInfo = self._ChoseRPCBinding(binds) + + # Update the OXID table + if entry.oxid not in self.OXID_table: + self.OXID_table[entry.oxid] = entry + + return entry + + def RemoteCreateInstance( + self, clsid: uuid.UUID, iids: List[ComInterface] + ) -> ObjectInstance: + """ + Calls IRemoteSCMActivator::RemoteCreateInstance and returns a OXID_Entry + that points to an instance of the provided class. + + :param clsid: the class ID to initialize + :param iids: the IDs of the interfaces to request + """ + return self._RemoteCreateInstanceOrGetClassObject( + RemoteCreateInstance_Request, + RemoteCreateInstance_Response, + clsid, + iids, + ) + + def RemoteGetClassObject( + self, clsid: uuid.UUID, iids: List[ComInterface] + ) -> ObjectInstance: + """ + Calls IRemoteSCMActivator::RemoteGetClassObject and returns a OXID_Entry + that points to the factory. + + :param clsid: the class ID to initialize + :param iids: the IDs of the interfaces to request + """ + return self._RemoteCreateInstanceOrGetClassObject( + RemoteGetClassObject_Request, + RemoteGetClassObject_Response, + clsid, + iids, + ) + + def sr1_orpc_req( + self, + pkt: NDRPacket, + ipid: uuid.UUID, + ssp=None, + auth_level=None, + timeout=5, + **kwargs, + ): + """ + Make an ORPC call. + + :param ipid: the reference to a specific interface on an object. + :param pkt: the request to make. + + :param ssp: (optional) non default SSP to use to connect to the object exporter + :param auth_level: (optional) non default authn level to use + :param timeout: (optional) timeout for the connection + """ + # [MS-DCOM] sect 3.2.4.2 + + # 1. look up the object exporter information in the client tables + + try: + # "The client MUST use the IPID specified by the client application to + # look up the IPID entry in the IPID table." + ipid_entry = self.IPID_table[ipid] + except KeyError: + raise ValueError("The IPID that was passed is unknown.") + + # "The client MUST then look up the OXID entry" + oxid_entry = self.OXID_table[ipid_entry.oxid] + oid_entry = self.OID_table[ipid_entry.oid] + resolver_entry = self.Resolver_table[oid_entry.hash] + + # Get opnum + try: + opnum = pkt.overload_fields[DceRpc5Request]["opnum"] + except KeyError: + raise ValueError("This packet is not part of a registered COM interface !") + + # Build ORPC request + + if resolver_entry.client is None: + # We don't have a client ready, make one. + resolver_entry.client = DCERPC_Client( + DCERPC_Transport.NCACN_IP_TCP, + ssp=ssp or self.ssp, + auth_level=auth_level or oxid_entry.authnHint, + verb=self.verb, + ) + + resolver_entry.client.connect( + host=oxid_entry.bindingInfo[0], + port=oxid_entry.bindingInfo[1], + timeout=timeout, + ) + + # Bind the COM interface + resolver_entry.client.bind_or_alter(ipid_entry.iface) + + # We need to set the NDR very late, after the bind + pkt.ndr64 = resolver_entry.client.ndr64 + + # "The ORPCTHIS and ORPCTHAT structures MUST be marshaled using + # the NDR [2.0] Transfer Syntax" + pkt = ( + ORPCTHIS( + version=oxid_entry.version, + cid=self.cid, + ndr64=False, + ) + / pkt + ) + + # Send/Receive ! + resp = resolver_entry.client.sr1_req( + pkt, + opnum=opnum, + objectuuid=ipid, + **kwargs, + ) + + return resp[ORPCTHAT].payload + + def AcquireInterface( + self, + ipid: uuid.UUID, + iids: List[ComInterface], + cPublicRefs: int, + ): + """ + [MS-DCOM] 3.2.4.4.3 - Acquiring Additional Interfaces on the Object + """ + # 1. Look up the OID entry + ipid_entry = self.IPID_table[ipid] + oxid_entry = self.OXID_table[ipid_entry.oxid] + + # 2. Perform call + resp = self.sr1_orpc_req( + ipid=oxid_entry.ipid_IRemUnknown, + pkt=RemQueryInterface_Request( + ripid=GUID(_uid_to_bytes(ipid)), + cRefs=cPublicRefs, + cIids=len(iids), + iids=[GUID(_uid_to_bytes(x.uuid)) for x in iids], + ), + ) + + # 3. Process answer + if not resp or resp.status != 0: + raise ValueError + + # "When the call returns successfully..." + for i, remqir in enumerate(resp.valueof("ppQIResults")): + self._UnmarshallObjref( + OBJREF(iid=iids[i].uuid) + / OBJREF_STANDARD(std=STDOBJREF(bytes(remqir.std))), + iid=iids[i], + ) + + def RemRelease(self, ipid: uuid.UUID): + """ + 3.2.4.4.2 Releasing Reference Counts on an Interface + """ + + # 1. Look up the OID entry + ipid_entry = self.IPID_table[ipid] + oxid_entry = self.OXID_table[ipid_entry.oxid] + oid_entry = self.OID_table[ipid_entry.oid] + + # 2. Perform call + resp = self.sr1_orpc_req( + ipid=oxid_entry.ipid_IRemUnknown, + pkt=RemRelease_Request( + InterfaceRefs=[ + REMINTERFACEREF( + ipid=GUID(_uid_to_bytes(ipid)), + cPublicRefs=ipid_entry.cPublicRefs, + cPrivateRefs=ipid_entry.cPrivateRefs, + ) + ], + ), + ) + + # 3. Process answer + if resp and resp.status == 0: + # "When the call returns successfully..." + # "It MUST remove the IPID entry from the IPID table." + del self.IPID_table[ipid] + + # "It MUST remove the IPID from the IPID list in the OID entry." + oid_entry.ipids.remove(ipid) + + # "If the IPID list of the OID entry is empty, it MUST remove the + # OID entry from the OID table." + if not oid_entry.ipids: + del self.OID_table[ipid_entry.oid] + + +def ServerAlive2(host, timeout=5) -> Tuple[List[STRINGBINDING], List[SECURITYBINDING]]: + """ + Call IObjectExporter::ServerAlive2 + """ + client = DCERPC_Client( + transport=DCERPC_Transport.NCACN_IP_TCP, + verb=False, + ndr64=False, + # "The client MUST NOT specify security on the call" + auth_level=DCE_C_AUTHN_LEVEL.NONE, + ) + client.connect(host, port=135, timeout=timeout) + + # Bind IObjectExporter if not already + client.bind_or_alter(find_dcerpc_interface("IObjectExporter")) + + # Send ServerAlive2 request + resp = client.sr1_req(ServerAlive2_Request(ndr64=False), timeout=timeout) + if not resp or resp.status != 0: + raise ValueError("ServerAlive2 failed !") + + # Parse bindings and security options + return _ParseStringArray(resp.ppdsaOrBindings.value) diff --git a/scapy/layers/msrpce/mseerr.py b/scapy/layers/msrpce/mseerr.py index 7c32fe21fc8..4c08204e8d8 100644 --- a/scapy/layers/msrpce/mseerr.py +++ b/scapy/layers/msrpce/mseerr.py @@ -10,191 +10,20 @@ # Wireshark does not know how to read this ! import uuid -from enum import IntEnum -from scapy.fields import StrFixedLenField, UTCTimeField +from scapy.fields import UTCTimeField +from scapy.packet import Packet, bind_layers + from scapy.layers.dcerpc import ( DceRpc5Fault, DceRpc5BindNak, - NDRConfPacketListField, - NDRConfStrLenField, - NDRConfStrLenFieldUtf16, - NDRFullEmbPointerField, - NDRInt3264EnumField, - NDRIntField, - NDRPacket, - NDRPacketField, - NDRRecursiveField, NDRSerializeType1PacketField, - NDRShortField, - NDRSignedShortField, - NDRUnionField, - NDRLongField, +) +from scapy.layers.msrpce.raw.ms_eerr import ( + ExtendedErrorInfo, + EEComputerNamePresent, ) from scapy.layers.smb2 import STATUS_ERREF -from scapy.packet import Packet, bind_layers - -# [MS-EERR] structures - - -class EEAString(NDRPacket): - ALIGNMENT = (4, 8) - fields_desc = [ - NDRSignedShortField("nLength", None, size_of="pString"), - NDRFullEmbPointerField( - NDRConfStrLenField("pString", "", size_is=lambda pkt: pkt.nLength), - ), - ] - - -class EEUString(NDRPacket): - ALIGNMENT = (4, 8) - fields_desc = [ - NDRSignedShortField("nLength", None, size_of="pString"), - NDRFullEmbPointerField( - NDRConfStrLenFieldUtf16("pString", "", size_is=lambda pkt: pkt.nLength), - ), - ] - - -class BinaryEEInfo(NDRPacket): - ALIGNMENT = (4, 8) - fields_desc = [ - NDRSignedShortField("nSize", None, size_of="pBlob"), - NDRFullEmbPointerField( - NDRConfStrLenField("pBlob", "", size_is=lambda pkt: pkt.nSize), - ), - ] - - -class ExtendedErrorParamTypesInternal(IntEnum): - eeptiAnsiString = 1 - eeptiUnicodeString = 2 - eeptiLongVal = 3 - eeptiShortValue = 4 - eeptiPointerValue = 5 - eeptiNone = 6 - eeptiBinary = 7 - - -class ExtendedErrorParam(NDRPacket): - ALIGNMENT = (8, 8) - fields_desc = [ - NDRInt3264EnumField("Type", 0, ExtendedErrorParamTypesInternal), - NDRUnionField( - [ - ( - NDRPacketField("value", EEAString(), EEAString), - ( - (lambda pkt: getattr(pkt, "Type", None) == 1), - (lambda _, val: val.tag == 1), - ), - ), - ( - NDRPacketField("value", EEUString(), EEUString), - ( - (lambda pkt: getattr(pkt, "Type", None) == 2), - (lambda _, val: val.tag == 2), - ), - ), - ( - NDRIntField("value", 0), - ( - (lambda pkt: getattr(pkt, "Type", None) == 3), - (lambda _, val: val.tag == 3), - ), - ), - ( - NDRSignedShortField("value", 0), - ( - (lambda pkt: getattr(pkt, "Type", None) == 4), - (lambda _, val: val.tag == 4), - ), - ), - ( - NDRLongField("value", 0), - ( - (lambda pkt: getattr(pkt, "Type", None) == 5), - (lambda _, val: val.tag == 5), - ), - ), - ( - StrFixedLenField("value", "", length=0), - ( - (lambda pkt: getattr(pkt, "Type", None) == 6), - (lambda _, val: val.tag == 6), - ), - ), - ( - NDRPacketField("value", BinaryEEInfo(), BinaryEEInfo), - ( - (lambda pkt: getattr(pkt, "Type", None) == 7), - (lambda _, val: val.tag == 7), - ), - ), - ], - StrFixedLenField("value", "", length=0), - align=(2, 8), - switch_fmt=("H", "I"), - ), - ] - - -class EEComputerNamePresent(IntEnum): - eecnpPresent = 1 - eecnpNotPresent = 2 - - -class EEComputerName(NDRPacket): - ALIGNMENT = (4, 8) - fields_desc = [ - NDRInt3264EnumField("Type", 0, EEComputerNamePresent), - NDRUnionField( - [ - ( - NDRPacketField("value", EEUString(), EEUString), - ( - (lambda pkt: getattr(pkt, "Type", None) == 1), - (lambda _, val: val.tag == 1), - ), - ), - ( - StrFixedLenField("value", "", length=0), - ( - (lambda pkt: getattr(pkt, "Type", None) == 2), - (lambda _, val: val.tag == 2), - ), - ), - ], - StrFixedLenField("value", "", length=0), - align=(2, 8), - switch_fmt=("H", "I"), - ), - ] - - -class ExtendedErrorInfo(NDRPacket): - ALIGNMENT = (8, 8) - DEPORTED_CONFORMANTS = ["Params"] - fields_desc = [ - NDRRecursiveField("Next"), - NDRPacketField("ComputerName", EEComputerName(), EEComputerName), - NDRIntField("ProcessID", 0), - NDRLongField("TimeStamp", 0), - NDRIntField("GeneratingComponent", 0), - NDRIntField("Status", 0), - NDRShortField("DetectionLocation", 0), - NDRShortField("Flags", 0), - NDRSignedShortField("nLen", None, size_of="Params"), - NDRConfPacketListField( - "Params", - [], - ExtendedErrorParam, - size_is=lambda pkt: pkt.nLen, - conformant_in_struct=True, - ), - ] - # Encapsulation packets @@ -595,6 +424,7 @@ class DceRpc5ExtendedErrorInfo(Packet): "extended_error", ExtendedErrorInfo(), ExtendedErrorInfo, + ptr_pack=True, ) ] @@ -603,7 +433,7 @@ def show(self) -> None: Print stacktrace """ # Get a list of ErrorInfo - cur = self.extended_error.value + cur = self.extended_error errors = [cur] while cur and cur.Next: cur = cur.Next.value diff --git a/scapy/layers/msrpce/mspac.py b/scapy/layers/msrpce/mspac.py index b4e17fa7dcc..6185673c804 100644 --- a/scapy/layers/msrpce/mspac.py +++ b/scapy/layers/msrpce/mspac.py @@ -20,8 +20,8 @@ FieldListField, FlagsField, LEIntEnumField, - LELongField, LEIntField, + LELongField, LEShortField, MultipleTypeField, PacketField, @@ -31,6 +31,7 @@ StrFixedLenField, StrLenFieldUtf16, UTCTimeField, + UUIDField, XStrField, XStrLenField, ) @@ -616,7 +617,7 @@ class _CLAIMSClaimSet(_NDRConfField, NDRSerializeType1PacketLenField): def m2i(self, pkt, s): if pkt.usCompressionFormat == CLAIMS_COMPRESSION_FORMAT.COMPRESSION_FORMAT_NONE: - return ndr_deserialize1(s, CLAIMS_SET, ndr64=False) + return ndr_deserialize1(s, CLAIMS_SET, ptr_pack=True) else: # TODO: There are 3 funky compression formats... see sect 2.2.18.4 return NDRConformantString(value=s) @@ -624,7 +625,7 @@ def m2i(self, pkt, s): def i2m(self, pkt, val): val = val[0] if pkt.usCompressionFormat == CLAIMS_COMPRESSION_FORMAT.COMPRESSION_FORMAT_NONE: - return ndr_serialize1(val) + return ndr_serialize1(val, ptr_pack=True) else: # funky return bytes(val) @@ -755,16 +756,27 @@ class PAC_ATTRIBUTES_INFO(Packet): _PACTYPES[0x11] = PAC_ATTRIBUTES_INFO -# sect 2.15 - PAC_REQUESTOR +# sect 2.15 - PAC_REQUESTOR_SID -class PAC_REQUESTOR(Packet): +class PAC_REQUESTOR_SID(Packet): fields_desc = [ PacketField("Sid", WINNT_SID(), WINNT_SID), ] -_PACTYPES[0x12] = PAC_REQUESTOR +_PACTYPES[0x12] = PAC_REQUESTOR_SID + +# sect 2.16 - PAC_REQUESTOR_GUID + + +class PAC_REQUESTOR_GUID(Packet): + fields_desc = [ + UUIDField("Guid", None), + ] + + +_PACTYPES[0x14] = PAC_REQUESTOR_GUID # sect 2.3 @@ -781,7 +793,7 @@ def addfield(self, pkt, s, val): x = self.i2m(pkt, v) pay = pkt.Payloads[i] if isinstance(pay, NDRPacket) or isinstance(pay, NDRSerialization1Header): - lgth = len(ndr_serialize1(pay)) + lgth = len(ndr_serialize1(pay, ptr_pack=True)) else: lgth = len(pay) if v.cbBufferSize is None: @@ -797,7 +809,7 @@ def addfield(self, pkt, s, val): class _PACTYPEPayloads(PacketListField): def i2m(self, pkt, val): if isinstance(val, NDRPacket) or isinstance(val, NDRSerialization1Header): - s = ndr_serialize1(val) + s = ndr_serialize1(val, ptr_pack=True) else: s = bytes(val) return s + b"\x00" * ((-len(s)) % 8) @@ -806,8 +818,7 @@ def getfield(self, pkt, s): if not pkt or not s: return s, [] result = [] - for i in range(len(pkt.Buffers)): - buf = pkt.Buffers[i] + for buf in pkt.Buffers: offset = buf.Offset - 16 * len(pkt.Buffers) - 8 try: cls = _PACTYPES[buf.ulType] @@ -818,7 +829,7 @@ def getfield(self, pkt, s): val = ndr_deserialize1( s[offset : offset + buf.cbBufferSize], cls, - ndr64=False, + ptr_pack=True, ) else: val = cls(s[offset : offset + buf.cbBufferSize]) diff --git a/scapy/layers/msrpce/raw/ms_dcom.py b/scapy/layers/msrpce/raw/ms_dcom.py index 3cfe8c92be4..33240f3601e 100644 --- a/scapy/layers/msrpce/raw/ms_dcom.py +++ b/scapy/layers/msrpce/raw/ms_dcom.py @@ -3,14 +3,20 @@ # See https://scapy.net/ for more information # Copyright (C) Gabriel Potter -# ms-dcom.idl compiled on 06/07/2025 -# This file is a stripped version ! Use scapy-rpc for the full. +# [ms-dcom] v25.0 (Mon, 16 Sep 2024) + """ RPC definitions for the following interfaces: +- IActivation (v0.0): 4d9f4ab8-7d1c-11cf-861e-0020af6e7c57 +- IRemoteSCMActivator (v0.0): 000001A0-0000-0000-C000-000000000046 - IObjectExporter (v0.0): 99fcfec4-5260-101b-bbcb-00aa0021347a +- IUnknown (v0.0): 00000000-0000-0000-C000-000000000046 +- IRemUnknown (v0.0): 00000131-0000-0000-C000-000000000046 +- IRemUnknown2 (v0.0): 00000143-0000-0000-C000-000000000046 This file is auto-generated by midl-to-scapy, do not modify. """ +from enum import IntEnum import uuid from scapy.fields import StrFixedLenField @@ -25,6 +31,7 @@ NDRConfVarStrNullFieldUtf16, NDRFullEmbPointerField, NDRFullPointerField, + NDRInt3264EnumField, NDRIntField, NDRLongField, NDRPacketField, @@ -40,6 +47,12 @@ class COMVERSION(NDRPacket): fields_desc = [NDRShortField("MajorVersion", 0), NDRShortField("MinorVersion", 0)] +class tagCPFLAGS(IntEnum): + CPFLAG_PROPAGATE = 1 + CPFLAG_EXPOSE = 2 + CPFLAG_ENVOY = 4 + + class GUID(NDRPacket): ALIGNMENT = (4, 4) fields_desc = [ @@ -73,10 +86,11 @@ class ORPC_EXTENT_ARRAY(NDRPacket): NDRFullEmbPointerField( NDRConfPacketListField( "extent", - [ORPC_EXTENT()], + [], ORPC_EXTENT, size_is=lambda pkt: ((pkt.size + 1) & (~1)), - ), + ptr_pack=True, + ) ), ] @@ -85,11 +99,11 @@ class ORPCTHIS(NDRPacket): ALIGNMENT = (4, 8) fields_desc = [ NDRPacketField("version", COMVERSION(), COMVERSION), - NDRIntField("flags", 0), + NDRInt3264EnumField("flags", 0, tagCPFLAGS), NDRIntField("reserved1", 0), NDRPacketField("cid", GUID(), GUID), NDRFullEmbPointerField( - NDRPacketField("extensions", ORPC_EXTENT_ARRAY(), ORPC_EXTENT_ARRAY), + NDRPacketField("extensions", ORPC_EXTENT_ARRAY(), ORPC_EXTENT_ARRAY) ), ] @@ -110,7 +124,7 @@ class ORPCTHAT(NDRPacket): fields_desc = [ NDRIntField("flags", 0), NDRFullEmbPointerField( - NDRPacketField("extensions", ORPC_EXTENT_ARRAY(), ORPC_EXTENT_ARRAY), + NDRPacketField("extensions", ORPC_EXTENT_ARRAY(), ORPC_EXTENT_ARRAY) ), ] @@ -130,6 +144,172 @@ class DUALSTRINGARRAY(NDRPacket): ] +class RemoteActivation_Request(NDRPacket): + fields_desc = [ + NDRPacketField("ORPCthis", ORPCTHIS(), ORPCTHIS), + NDRPacketField("Clsid", GUID(), GUID), + NDRFullPointerField(NDRConfVarStrNullFieldUtf16("pwszObjectName", "")), + NDRFullPointerField( + NDRPacketField("pObjectStorage", MInterfacePointer(), MInterfacePointer) + ), + NDRIntField("ClientImpLevel", 0), + NDRIntField("Mode", 0), + NDRIntField("Interfaces", None, size_of="pIIDs"), + NDRConfPacketListField("pIIDs", [], GUID, size_is=lambda pkt: pkt.Interfaces), + NDRShortField("cRequestedProtseqs", None, size_of="aRequestedProtseqs"), + NDRConfFieldListField( + "aRequestedProtseqs", + [], + NDRShortField("", 0), + size_is=lambda pkt: pkt.cRequestedProtseqs, + ), + ] + + +class RemoteActivation_Response(NDRPacket): + fields_desc = [ + NDRPacketField("ORPCthat", ORPCTHAT(), ORPCTHAT), + NDRLongField("pOxid", 0), + NDRFullPointerField( + NDRPacketField("ppdsaOxidBindings", DUALSTRINGARRAY(), DUALSTRINGARRAY) + ), + NDRPacketField("pipidRemUnknown", GUID(), GUID), + NDRIntField("pAuthnHint", 0), + NDRPacketField("pServerVersion", COMVERSION(), COMVERSION), + NDRSignedIntField("phr", 0), + NDRConfPacketListField( + "ppInterfaceData", + [], + MInterfacePointer, + size_is=lambda pkt: pkt.Interfaces, + ptr_pack=True, + ), + NDRConfFieldListField( + "pResults", [], NDRSignedIntField("", 0), size_is=lambda pkt: pkt.Interfaces + ), + NDRIntField("status", 0), + ] + + +IACTIVATION_OPNUMS = {0: DceRpcOp(RemoteActivation_Request, RemoteActivation_Response)} +register_dcerpc_interface( + name="IActivation", + uuid=uuid.UUID("4d9f4ab8-7d1c-11cf-861e-0020af6e7c57"), + version="0.0", + opnums=IACTIVATION_OPNUMS, +) + + +class RemoteGetClassObject_Request(NDRPacket): + fields_desc = [ + NDRPacketField("orpcthis", ORPCTHIS(), ORPCTHIS), + NDRFullPointerField( + NDRPacketField("pActProperties", MInterfacePointer(), MInterfacePointer) + ), + ] + + +class RemoteGetClassObject_Response(NDRPacket): + fields_desc = [ + NDRPacketField("orpcthat", ORPCTHAT(), ORPCTHAT), + NDRFullPointerField( + NDRPacketField("ppActProperties", MInterfacePointer(), MInterfacePointer) + ), + NDRIntField("status", 0), + ] + + +class RemoteCreateInstance_Request(NDRPacket): + fields_desc = [ + NDRPacketField("orpcthis", ORPCTHIS(), ORPCTHIS), + NDRFullPointerField( + NDRPacketField("pUnkOuter", MInterfacePointer(), MInterfacePointer) + ), + NDRFullPointerField( + NDRPacketField("pActProperties", MInterfacePointer(), MInterfacePointer) + ), + ] + + +class RemoteCreateInstance_Response(NDRPacket): + fields_desc = [ + NDRPacketField("orpcthat", ORPCTHAT(), ORPCTHAT), + NDRFullPointerField( + NDRPacketField("ppActProperties", MInterfacePointer(), MInterfacePointer) + ), + NDRIntField("status", 0), + ] + + +IREMOTESCMACTIVATOR_OPNUMS = { # 0: Opnum0NotUsedOnWire, + # 1: Opnum1NotUsedOnWire, + # 2: Opnum2NotUsedOnWire, + 3: DceRpcOp(RemoteGetClassObject_Request, RemoteGetClassObject_Response), + 4: DceRpcOp(RemoteCreateInstance_Request, RemoteCreateInstance_Response), +} +register_dcerpc_interface( + name="IRemoteSCMActivator", + uuid=uuid.UUID("000001A0-0000-0000-C000-000000000046"), + version="0.0", + opnums=IREMOTESCMACTIVATOR_OPNUMS, +) + + +class ResolveOxid_Request(NDRPacket): + fields_desc = [ + NDRLongField("pOxid", 0), + NDRShortField("cRequestedProtseqs", None, size_of="arRequestedProtseqs"), + NDRConfFieldListField( + "arRequestedProtseqs", + [], + NDRShortField("", 0), + size_is=lambda pkt: pkt.cRequestedProtseqs, + ), + ] + + +class ResolveOxid_Response(NDRPacket): + fields_desc = [ + NDRFullPointerField( + NDRPacketField("ppdsaOxidBindings", DUALSTRINGARRAY(), DUALSTRINGARRAY) + ), + NDRPacketField("pipidRemUnknown", GUID(), GUID), + NDRIntField("pAuthnHint", 0), + NDRIntField("status", 0), + ] + + +class SimplePing_Request(NDRPacket): + fields_desc = [NDRLongField("pSetId", 0)] + + +class SimplePing_Response(NDRPacket): + fields_desc = [NDRIntField("status", 0)] + + +class ComplexPing_Request(NDRPacket): + fields_desc = [ + NDRLongField("pSetId", 0), + NDRShortField("SequenceNum", 0), + NDRShortField("cAddToSet", None, size_of="AddToSet"), + NDRShortField("cDelFromSet", None, size_of="DelFromSet"), + NDRConfFieldListField( + "AddToSet", [], NDRLongField("", 0), size_is=lambda pkt: pkt.cAddToSet + ), + NDRConfFieldListField( + "DelFromSet", [], NDRLongField("", 0), size_is=lambda pkt: pkt.cDelFromSet + ), + ] + + +class ComplexPing_Response(NDRPacket): + fields_desc = [ + NDRLongField("pSetId", 0), + NDRShortField("pPingBackoffFactor", 0), + NDRIntField("status", 0), + ] + + class ServerAlive_Request(NDRPacket): fields_desc = [] @@ -138,6 +318,31 @@ class ServerAlive_Response(NDRPacket): fields_desc = [NDRIntField("status", 0)] +class ResolveOxid2_Request(NDRPacket): + fields_desc = [ + NDRLongField("pOxid", 0), + NDRShortField("cRequestedProtseqs", None, size_of="arRequestedProtseqs"), + NDRConfFieldListField( + "arRequestedProtseqs", + [], + NDRShortField("", 0), + size_is=lambda pkt: pkt.cRequestedProtseqs, + ), + ] + + +class ResolveOxid2_Response(NDRPacket): + fields_desc = [ + NDRFullPointerField( + NDRPacketField("ppdsaOxidBindings", DUALSTRINGARRAY(), DUALSTRINGARRAY) + ), + NDRPacketField("pipidRemUnknown", GUID(), GUID), + NDRIntField("pAuthnHint", 0), + NDRPacketField("pComVersion", COMVERSION(), COMVERSION), + NDRIntField("status", 0), + ] + + class ServerAlive2_Request(NDRPacket): fields_desc = [] @@ -154,7 +359,11 @@ class ServerAlive2_Response(NDRPacket): IOBJECTEXPORTER_OPNUMS = { + 0: DceRpcOp(ResolveOxid_Request, ResolveOxid_Response), + 1: DceRpcOp(SimplePing_Request, SimplePing_Response), + 2: DceRpcOp(ComplexPing_Request, ComplexPing_Response), 3: DceRpcOp(ServerAlive_Request, ServerAlive_Response), + 4: DceRpcOp(ResolveOxid2_Request, ResolveOxid2_Response), 5: DceRpcOp(ServerAlive2_Request, ServerAlive2_Response), } register_dcerpc_interface( @@ -163,3 +372,141 @@ class ServerAlive2_Response(NDRPacket): version="0.0", opnums=IOBJECTEXPORTER_OPNUMS, ) +IUNKNOWN_OPNUMS = { # 0: Opnum0NotUsedOnWire, + # 1: Opnum1NotUsedOnWire, + # 2: Opnum2NotUsedOnWire +} +register_com_interface( + name="IUnknown", + uuid=uuid.UUID("00000000-0000-0000-C000-000000000046"), + opnums=IUNKNOWN_OPNUMS, +) + + +class STDOBJREF(NDRPacket): + ALIGNMENT = (8, 8) + fields_desc = [ + NDRIntField("flags", 0), + NDRIntField("cPublicRefs", 0), + NDRLongField("oxid", 0), + NDRLongField("oid", 0), + NDRPacketField("ipid", GUID(), GUID), + ] + + +class REMQIRESULT(NDRPacket): + ALIGNMENT = (8, 8) + fields_desc = [ + NDRSignedIntField("hResult", 0), + NDRPacketField("std", STDOBJREF(), STDOBJREF), + ] + + +class RemQueryInterface_Request(NDRPacket): + fields_desc = [ + NDRPacketField("ripid", GUID(), GUID), + NDRIntField("cRefs", 0), + NDRShortField("cIids", None, size_of="iids"), + NDRConfPacketListField("iids", [], GUID, size_is=lambda pkt: pkt.cIids), + ] + + +class RemQueryInterface_Response(NDRPacket): + fields_desc = [ + NDRConfPacketListField( + "ppQIResults", [], REMQIRESULT, size_is=lambda pkt: pkt.cIids, ptr_pack=True + ), + NDRIntField("status", 0), + ] + + +class REMINTERFACEREF(NDRPacket): + ALIGNMENT = (4, 4) + fields_desc = [ + NDRPacketField("ipid", GUID(), GUID), + NDRIntField("cPublicRefs", 0), + NDRIntField("cPrivateRefs", 0), + ] + + +class RemAddRef_Request(NDRPacket): + fields_desc = [ + NDRShortField("cInterfaceRefs", None, size_of="InterfaceRefs"), + NDRConfPacketListField( + "InterfaceRefs", [], REMINTERFACEREF, size_is=lambda pkt: pkt.cInterfaceRefs + ), + ] + + +class RemAddRef_Response(NDRPacket): + fields_desc = [ + NDRConfFieldListField( + "pResults", + [], + NDRSignedIntField("", 0), + size_is=lambda pkt: pkt.cInterfaceRefs, + ), + NDRIntField("status", 0), + ] + + +class RemRelease_Request(NDRPacket): + fields_desc = [ + NDRShortField("cInterfaceRefs", None, size_of="InterfaceRefs"), + NDRConfPacketListField( + "InterfaceRefs", [], REMINTERFACEREF, size_is=lambda pkt: pkt.cInterfaceRefs + ), + ] + + +class RemRelease_Response(NDRPacket): + fields_desc = [NDRIntField("status", 0)] + + +IREMUNKNOWN_OPNUMS = { # 0: Opnum0NotUsedOnWire, + # 1: Opnum1NotUsedOnWire, + # 2: Opnum2NotUsedOnWire, + 3: DceRpcOp(RemQueryInterface_Request, RemQueryInterface_Response), + 4: DceRpcOp(RemAddRef_Request, RemAddRef_Response), + 5: DceRpcOp(RemRelease_Request, RemRelease_Response), +} +register_com_interface( + name="IRemUnknown", + uuid=uuid.UUID("00000131-0000-0000-C000-000000000046"), + opnums=IREMUNKNOWN_OPNUMS, +) + + +class RemQueryInterface2_Request(NDRPacket): + fields_desc = [ + NDRPacketField("ripid", GUID(), GUID), + NDRShortField("cIids", None, size_of="iids"), + NDRConfPacketListField("iids", [], GUID, size_is=lambda pkt: pkt.cIids), + ] + + +class RemQueryInterface2_Response(NDRPacket): + fields_desc = [ + NDRConfFieldListField( + "phr", [], NDRSignedIntField("", 0), size_is=lambda pkt: pkt.cIids + ), + NDRConfPacketListField( + "ppMIF", [], MInterfacePointer, size_is=lambda pkt: pkt.cIids, ptr_pack=True + ), + NDRIntField("status", 0), + ] + + +IREMUNKNOWN2_OPNUMS = { # 0: Opnum0NotUsedOnWire, + # 1: Opnum1NotUsedOnWire, + # 2: Opnum2NotUsedOnWire, + 3: DceRpcOp(RemQueryInterface_Request, RemQueryInterface_Response), + 4: DceRpcOp(RemAddRef_Request, RemAddRef_Response), + 5: DceRpcOp(RemRelease_Request, RemRelease_Response), + 6: DceRpcOp(RemQueryInterface2_Request, RemQueryInterface2_Response), +} +register_com_interface( + name="IRemUnknown2", + uuid=uuid.UUID("00000143-0000-0000-C000-000000000046"), + opnums=IREMUNKNOWN2_OPNUMS, +) diff --git a/scapy/layers/msrpce/raw/ms_eerr.py b/scapy/layers/msrpce/raw/ms_eerr.py new file mode 100644 index 00000000000..1d0aad982aa --- /dev/null +++ b/scapy/layers/msrpce/raw/ms_eerr.py @@ -0,0 +1,194 @@ +# SPDX-License-Identifier: GPL-2.0-only +# This file is part of Scapy RPC +# See https://scapy.net/ for more information +# Copyright (C) Gabriel Potter + +# [ms-eerr] v16.0 (Tue, 23 Apr 2024) + +""" +RPC definitions for the following interfaces: +- +This file is auto-generated by midl-to-scapy, do not modify. +""" + +from enum import IntEnum +import uuid + +from scapy.fields import StrFixedLenField +from scapy.layers.dcerpc import ( + NDRPacket, + NDRConfPacketListField, + NDRConfStrLenField, + NDRConfStrLenFieldUtf16, + NDRFullEmbPointerField, + NDRInt3264EnumField, + NDRIntField, + NDRPacketField, + NDRRecursiveClass, + NDRShortField, + NDRSignedIntField, + NDRSignedLongField, + NDRSignedShortField, + NDRUnionField, +) + + +class EEAString(NDRPacket): + ALIGNMENT = (4, 8) + fields_desc = [ + NDRSignedShortField("nLength", None, size_of="pString"), + NDRFullEmbPointerField( + NDRConfStrLenField("pString", "", size_is=lambda pkt: pkt.nLength) + ), + ] + + +class EEUString(NDRPacket): + ALIGNMENT = (4, 8) + fields_desc = [ + NDRSignedShortField("nLength", None, size_of="pString"), + NDRFullEmbPointerField( + NDRConfStrLenFieldUtf16("pString", "", size_is=lambda pkt: pkt.nLength) + ), + ] + + +class BinaryEEInfo(NDRPacket): + ALIGNMENT = (4, 8) + fields_desc = [ + NDRSignedShortField("nSize", None, size_of="pBlob"), + NDRFullEmbPointerField( + NDRConfStrLenField("pBlob", "", size_is=lambda pkt: pkt.nSize) + ), + ] + + +class ExtendedErrorParamTypesInternal(IntEnum): + eeptiAnsiString = 1 + eeptiUnicodeString = 2 + eeptiLongVal = 3 + eeptiShortValue = 4 + eeptiPointerValue = 5 + eeptiNone = 6 + eeptiBinary = 7 + + +class ExtendedErrorParam(NDRPacket): + ALIGNMENT = (8, 8) + fields_desc = [ + NDRInt3264EnumField("Type", 0, ExtendedErrorParamTypesInternal), + NDRUnionField( + [ + ( + NDRPacketField("value", EEAString(), EEAString), + ( + (lambda pkt: getattr(pkt, "Type", None) == 1), + (lambda _, val: val.tag == 1), + ), + ), + ( + NDRPacketField("value", EEUString(), EEUString), + ( + (lambda pkt: getattr(pkt, "Type", None) == 2), + (lambda _, val: val.tag == 2), + ), + ), + ( + NDRSignedIntField("value", 0), + ( + (lambda pkt: getattr(pkt, "Type", None) == 3), + (lambda _, val: val.tag == 3), + ), + ), + ( + NDRSignedShortField("value", 0), + ( + (lambda pkt: getattr(pkt, "Type", None) == 4), + (lambda _, val: val.tag == 4), + ), + ), + ( + NDRSignedLongField("value", 0), + ( + (lambda pkt: getattr(pkt, "Type", None) == 5), + (lambda _, val: val.tag == 5), + ), + ), + ( + StrFixedLenField("value", "", length=0), + ( + (lambda pkt: getattr(pkt, "Type", None) == 6), + (lambda _, val: val.tag == 6), + ), + ), + ( + NDRPacketField("value", BinaryEEInfo(), BinaryEEInfo), + ( + (lambda pkt: getattr(pkt, "Type", None) == 7), + (lambda _, val: val.tag == 7), + ), + ), + ], + StrFixedLenField("value", "", length=0), + align=(2, 8), + switch_fmt=("H", "I"), + ), + ] + + +class EEComputerNamePresent(IntEnum): + eecnpPresent = 1 + eecnpNotPresent = 2 + + +class EEComputerName(NDRPacket): + ALIGNMENT = (4, 8) + fields_desc = [ + NDRInt3264EnumField("Type", 0, EEComputerNamePresent), + NDRUnionField( + [ + ( + NDRPacketField("value", EEUString(), EEUString), + ( + (lambda pkt: getattr(pkt, "Type", None) == 1), + (lambda _, val: val.tag == 1), + ), + ), + ( + StrFixedLenField("value", "", length=0), + ( + (lambda pkt: getattr(pkt, "Type", None) == 2), + (lambda _, val: val.tag == 2), + ), + ), + ], + StrFixedLenField("value", "", length=0), + align=(2, 8), + switch_fmt=("H", "I"), + ), + ] + + +class ExtendedErrorInfo(NDRPacket): + ALIGNMENT = (8, 8) + DEPORTED_CONFORMANTS = ["Params"] + fields_desc = [ + NDRFullEmbPointerField( + NDRPacketField("Next", None, NDRRecursiveClass("ExtendedErrorInfo")) + ), + NDRPacketField("ComputerName", EEComputerName(), EEComputerName), + NDRIntField("ProcessID", 0), + NDRSignedLongField("TimeStamp", 0), + NDRIntField("GeneratingComponent", 0), + NDRIntField("Status", 0), + NDRShortField("DetectionLocation", 0), + NDRShortField("Flags", 0), + NDRSignedShortField("nLen", None, size_of="Params"), + NDRConfPacketListField( + "Params", + [], + ExtendedErrorParam, + size_is=lambda pkt: pkt.nLen, + conformant_in_struct=True, + ), + ] diff --git a/scapy/layers/msrpce/rpcclient.py b/scapy/layers/msrpce/rpcclient.py index 6e81221dfec..a25f6587126 100644 --- a/scapy/layers/msrpce/rpcclient.py +++ b/scapy/layers/msrpce/rpcclient.py @@ -11,9 +11,16 @@ import socket from scapy.config import conf +from scapy.error import log_runtime from scapy.layers.dcerpc import ( + _DCE_RPC_ERROR_CODES, + ComInterface, + CommonAuthVerifier, + DCE_C_AUTHN_LEVEL, + DCERPC_Transport, DceRpc5, + DceRpc5AbstractSyntax, DceRpc5AlterContext, DceRpc5AlterContextResp, DceRpc5Auth3, @@ -24,16 +31,16 @@ DceRpc5Fault, DceRpc5Request, DceRpc5Response, - DceRpc5AbstractSyntax, DceRpc5TransferSyntax, + DceRpcInterface, + DceRpcSecVT, + DceRpcSecVTCommand, + DceRpcSecVTPcontext, + DceRpcSession, DceRpcSocket, - DCERPC_Transport, find_dcerpc_interface, - CommonAuthVerifier, - DCE_C_AUTHN_LEVEL, - # NDR - NDRPointer, NDRContextHandle, + NDRPointer, ) from scapy.layers.gssapi import ( SSP, @@ -57,30 +64,62 @@ UUID, ) +# Typing +from typing import ( + Optional, + Union, +) + class DCERPC_Client(object): """ A basic DCE/RPC client - :param ndr64: Should ask for NDR64 when binding (default False) + :param transport: the transport to use. + :param ndr64: should ask for NDR64 when binding (default conf.ndr64) + :param ndrendian: the endianness to use (default little) + :param verb: enable verbose logging (default True) + :param auth_level: the DCE_C_AUTHN_LEVEL to use """ - def __init__(self, transport, ndr64=False, ndrendian="little", verb=True, **kwargs): + def __init__( + self, + transport: DCERPC_Transport, + ndr64: Optional[bool] = None, + ndrendian: str = "little", + verb: bool = True, + auth_level: Optional[DCE_C_AUTHN_LEVEL] = None, + auth_context_id: int = 0, + **kwargs, + ): self.sock = None self.transport = transport assert isinstance( transport, DCERPC_Transport ), "transport must be from DCERPC_Transport" + + # Counters self.call_id = 0 - self.cont_id = 0 - self.ndr64 = ndr64 + self.all_cont_id = 0 # number of contexts sent + + # Session parameters + if ndr64 is None: + ndr64 = conf.ndr64 + self.ndr64: bool = ndr64 self.ndrendian = ndrendian self.verb = verb - self.host = None - self.auth_level = kwargs.pop("auth_level", DCE_C_AUTHN_LEVEL.NONE) - self.auth_context_id = kwargs.pop("auth_context_id", 0) + self.host: str = None + self.port: int = -1 self.ssp = kwargs.pop("ssp", None) # type: SSP self.sspcontext = None + if auth_level is not None: + self.auth_level = auth_level + elif self.ssp is not None: + self.auth_level = DCE_C_AUTHN_LEVEL.CONNECT + else: + self.auth_level = DCE_C_AUTHN_LEVEL.NONE + self.auth_context_id = auth_context_id + self._first_time_on_interface = True self.dcesockargs = kwargs self.dcesockargs["transport"] = self.transport @@ -101,9 +140,17 @@ def from_smblink(cls, smbcli, smb_kwargs={}, **kwargs): ) return client + @property + def session(self) -> DceRpcSession: + return self.sock.session + def connect(self, host, port=None, timeout=5, smb_kwargs={}): """ - Initiate a connection + Initiate a connection. + + :param host: the host to connect to + :param port: (optional) the port to connect to + :param timeout: (optional) the connection timeout (default 5) """ if port is None: if self.transport == DCERPC_Transport.NCACN_IP_TCP: # IP/TCP @@ -115,6 +162,7 @@ def connect(self, host, port=None, timeout=5, smb_kwargs={}): "Can't guess the port for transport: %s" % self.transport ) self.host = host + self.port = port sock = socket.socket() sock.settimeout(timeout) if self.verb: @@ -146,11 +194,19 @@ def connect(self, host, port=None, timeout=5, smb_kwargs={}): ) def close(self): + """ + Close the DCE/RPC client. + """ if self.verb: print("X Connection closed\n") self.sock.close() def sr1(self, pkt, **kwargs): + """ + Send/Receive a DCE/RPC message. + + The DCE/RPC header is added automatically. + """ self.call_id += 1 pkt = ( DceRpc5( @@ -158,14 +214,23 @@ def sr1(self, pkt, **kwargs): pfc_flags="PFC_FIRST_FRAG+PFC_LAST_FRAG", endian=self.ndrendian, auth_verifier=kwargs.pop("auth_verifier", None), + vt_trailer=kwargs.pop("vt_trailer", None), ) / pkt ) if "pfc_flags" in kwargs: pkt.pfc_flags = kwargs.pop("pfc_flags") + if "objectuuid" in kwargs: + pkt.pfc_flags += "PFC_OBJECT_UUID" + pkt.object = kwargs.pop("objectuuid") return self.sock.sr1(pkt, verbose=0, **kwargs) def send(self, pkt, **kwargs): + """ + Send a DCE/RPC message. + + The DCE/RPC header is added automatically. + """ self.call_id += 1 pkt = ( DceRpc5( @@ -173,60 +238,119 @@ def send(self, pkt, **kwargs): pfc_flags="PFC_FIRST_FRAG+PFC_LAST_FRAG", endian=self.ndrendian, auth_verifier=kwargs.pop("auth_verifier", None), + vt_trailer=kwargs.pop("vt_trailer", None), ) / pkt ) if "pfc_flags" in kwargs: pkt.pfc_flags = kwargs.pop("pfc_flags") + if "objectuuid" in kwargs: + pkt.pfc_flags += "PFC_OBJECT_UUID" + pkt.object = kwargs.pop("objectuuid") return self.sock.send(pkt, **kwargs) def sr1_req(self, pkt, **kwargs): + """ + Send/Receive a DCE/RPC request. + + :param pkt: the inner DCE/RPC message, without any header. + """ if self.verb: - print(conf.color_theme.opening(">> REQUEST: %s" % pkt.__class__.__name__)) + if "objectuuid" in kwargs: + # COM + print( + conf.color_theme.opening( + ">> REQUEST (COM): %s" % pkt.payload.__class__.__name__ + ) + ) + else: + print( + conf.color_theme.opening(">> REQUEST: %s" % pkt.__class__.__name__) + ) + # Add sectrailer if first time talking on this interface + vt_trailer = b"" + if ( + self._first_time_on_interface + and self.transport != DCERPC_Transport.NCACN_NP + ): + # In the first request after a bind, Windows sends a trailer to verify + # that the negotiated transfer/interface wasn't altered. + self._first_time_on_interface = False + vt_trailer = DceRpcSecVT( + commands=[ + DceRpcSecVTCommand(SEC_VT_COMMAND_END=1) + / DceRpcSecVTPcontext( + InterfaceId=self.session.rpc_bind_interface.uuid, + TransferSyntax="NDR64" if self.ndr64 else "NDR 2.0", + TransferVersion=1 if self.ndr64 else 2, + ) + ] + ) + + # Optional: force opnum + opnum = {} + if "opnum" in kwargs: + opnum["opnum"] = kwargs.pop("opnum") + # Send/receive resp = self.sr1( - DceRpc5Request(cont_id=self.cont_id, alloc_hint=len(pkt)) / pkt, + DceRpc5Request( + cont_id=self.session.cont_id, + alloc_hint=len(pkt) + len(vt_trailer), + **opnum, + ) + / pkt, + vt_trailer=vt_trailer, **kwargs, ) + + # Parse result + result = None if DceRpc5Response in resp: if self.verb: - print( - conf.color_theme.success( - "<< RESPONSE: %s" - % (resp[DceRpc5Response].payload.__class__.__name__) - ) - ) - return resp[DceRpc5Response].payload - else: - if self.verb: - if DceRpc5Fault in resp: - if resp[DceRpc5Fault].payload and not isinstance( - resp[DceRpc5Fault].payload, conf.raw_layer - ): - resp[DceRpc5Fault].payload.show() - if resp.status == 0x00000005: - print(conf.color_theme.fail("! nca_s_fault_access_denied")) - elif resp.status == 0x00000721: - print( - conf.color_theme.fail( - "! nca_s_fault_sec_pkg_error " - "(error in checksum/encryption)" - ) + if "objectuuid" in kwargs: + # COM + print( + conf.color_theme.success( + "<< RESPONSE (COM): %s" + % (resp[DceRpc5Response].payload.payload.__class__.__name__) ) - else: - print( - conf.color_theme.fail( - "! %s" % STATUS_ERREF.get(resp.status, "Failure") - ) + ) + else: + print( + conf.color_theme.success( + "<< RESPONSE: %s" + % (resp[DceRpc5Response].payload.__class__.__name__) ) - resp.show() - return - return resp + ) + result = resp[DceRpc5Response].payload + elif DceRpc5Fault in resp: + if self.verb: + print(conf.color_theme.success("<< FAULT")) + # If [MS-EERR] is loaded, show the extended info + if resp[DceRpc5Fault].payload and not isinstance( + resp[DceRpc5Fault].payload, conf.raw_layer + ): + resp[DceRpc5Fault].payload.show() + result = resp + if self.verb and getattr(resp, "status", 0) != 0: + if resp.status in _DCE_RPC_ERROR_CODES: + print(conf.color_theme.fail(f"! {_DCE_RPC_ERROR_CODES[resp.status]}")) + elif resp.status in STATUS_ERREF: + print(conf.color_theme.fail(f"! {STATUS_ERREF[resp.status]}")) + else: + print(conf.color_theme.fail("! Failure")) + resp.show() + return result - def get_bind_context(self, interface): - return [ + def _get_bind_context(self, interface): + """ + Internal: get the bind DCE/RPC context. + """ + # NDR 2.0 + contexts = [ DceRpc5Context( - cont_id=0, + cont_id=self.all_cont_id, abstract_syntax=DceRpc5AbstractSyntax( if_uuid=interface.uuid, if_version=interface.if_version, @@ -239,10 +363,14 @@ def get_bind_context(self, interface): ) ], ), - ] + ( - [ + ] + self.all_cont_id += 1 + + # NDR64 + if self.ndr64: + contexts.append( DceRpc5Context( - cont_id=1, + cont_id=self.all_cont_id, abstract_syntax=DceRpc5AbstractSyntax( if_uuid=interface.uuid, if_version=interface.if_version, @@ -254,26 +382,34 @@ def get_bind_context(self, interface): if_version=1, ) ], + ) + ) + self.all_cont_id += 1 + + # BindTimeFeatureNegotiationBitmask + contexts.append( + DceRpc5Context( + cont_id=self.all_cont_id, + abstract_syntax=DceRpc5AbstractSyntax( + if_uuid=interface.uuid, + if_version=interface.if_version, ), - DceRpc5Context( - cont_id=2, - abstract_syntax=DceRpc5AbstractSyntax( - if_uuid=interface.uuid, - if_version=interface.if_version, - ), - transfer_syntaxes=[ - DceRpc5TransferSyntax( - if_uuid=uuid.UUID("6cb71c2c-9812-4540-0300-000000000000"), - if_version=1, - ) - ], - ), - ] - if self.ndr64 - else [] + transfer_syntaxes=[ + DceRpc5TransferSyntax( + if_uuid=uuid.UUID("6cb71c2c-9812-4540-0300-000000000000"), + if_version=1, + ) + ], + ) ) + self.all_cont_id += 1 - def _bind(self, interface, reqcls, respcls): + return contexts + + def _bind(self, interface: Union[DceRpcInterface, ComInterface], reqcls, respcls): + """ + Internal: used to send a bind/alter request + """ # Build a security context: [MS-RPCE] 3.3.1.5.2 if self.verb: print( @@ -282,14 +418,16 @@ def _bind(self, interface, reqcls, respcls): + (" (with %s)" % self.ssp.__class__.__name__ if self.ssp else "") ) ) + # Do we need an authenticated bind if not self.ssp or ( - self.transport == DCERPC_Transport.NCACN_NP + self.sspcontext is not None + or self.transport == DCERPC_Transport.NCACN_NP and self.auth_level < DCE_C_AUTHN_LEVEL.PKT_INTEGRITY ): # NCACN_NP = SMB without INTEGRITY/PRIVACY does not bind the RPC securely, # again as it has already authenticated during the SMB Session Setup resp = self.sr1( - reqcls(context_elem=self.get_bind_context(interface)), + reqcls(context_elem=self._get_bind_context(interface)), auth_verifier=None, ) status = GSS_S_COMPLETE @@ -322,7 +460,7 @@ def _bind(self, interface, reqcls, respcls): self.sspcontext.clifailure() return False resp = self.sr1( - reqcls(context_elem=self.get_bind_context(interface)), + reqcls(context_elem=self._get_bind_context(interface)), auth_verifier=( None if not self.sspcontext @@ -338,8 +476,7 @@ def _bind(self, interface, reqcls, respcls): + ( # If the SSP supports "Header Signing", advertise it "+PFC_SUPPORT_HEADER_SIGN" - if self.ssp is not None - and self.sock.session.support_header_signing + if self.ssp is not None and self.session.support_header_signing else "" ) ), @@ -376,7 +513,7 @@ def _bind(self, interface, reqcls, respcls): respcls = DceRpc5AlterContextResp resp = self.sr1( DceRpc5AlterContext( - context_elem=self.get_bind_context(interface) + context_elem=self._get_bind_context(interface) ), auth_verifier=CommonAuthVerifier( auth_type=self.ssp.auth_type, @@ -396,6 +533,8 @@ def _bind(self, interface, reqcls, respcls): token=resp.auth_verifier.auth_value, target_name="host/" + self.host, ) + else: + log_runtime.error("GSS_Init_sec_context failed with %s !" % status) # Check context acceptance if ( status == GSS_S_COMPLETE @@ -404,15 +543,17 @@ def _bind(self, interface, reqcls, respcls): ): self.call_id = 0 # reset call id port = resp.sec_addr.port_spec.decode() - ndr = self.sock.session.ndr64 and "NDR64" or "NDR32" - self.cont_id = int(self.sock.session.ndr64) # ctx 0 for NDR32, 1 for NDR64 + ndr = self.session.ndr64 and "NDR64" or "NDR32" + self.ndr64 = self.session.ndr64 + self.cont_id = int(self.session.ndr64) # ctx 0 for NDR32, 1 for NDR64 if self.verb: print( conf.color_theme.success( f"<< {respcls.__name__} port '{port}' using {ndr}" ) ) - self.sock.session.sspcontext = self.sspcontext + self.session.sspcontext = self.sspcontext + self._first_time_on_interface = True return True else: if self.verb: @@ -427,22 +568,20 @@ def _bind(self, interface, reqcls, respcls): ): resp[DceRpc5BindNak].payload.show() elif DceRpc5Fault in resp: - if resp.status == 0x00000005: - print(conf.color_theme.fail("! nca_s_fault_access_denied")) - elif resp.status == 0x00000721: - print( - conf.color_theme.fail( - "! nca_s_fault_sec_pkg_error " - "(error in checksum/encryption)" + if getattr(resp, "status", 0) != 0: + if resp.status in _DCE_RPC_ERROR_CODES: + print( + conf.color_theme.fail( + f"! {_DCE_RPC_ERROR_CODES[resp.status]}" + ) ) - ) - else: - print( - conf.color_theme.fail( - "! %s" % STATUS_ERREF.get(resp.status, "Failure") + elif resp.status in STATUS_ERREF: + print( + conf.color_theme.fail(f"! {STATUS_ERREF[resp.status]}") ) - ) - resp.show() + else: + print(conf.color_theme.fail("! Failure")) + resp.show() if DceRpc5Fault in resp: if resp[DceRpc5Fault].payload and not isinstance( resp[DceRpc5Fault].payload, conf.raw_layer @@ -453,32 +592,40 @@ def _bind(self, interface, reqcls, respcls): resp.show() return False - def bind(self, interface): + def bind(self, interface: Union[DceRpcInterface, ComInterface]): """ Bind the client to an interface + + :param interface: the DceRpcInterface object """ return self._bind(interface, DceRpc5Bind, DceRpc5BindAck) - def alter_context(self, interface): + def alter_context(self, interface: Union[DceRpcInterface, ComInterface]): """ Alter context: post-bind context negotiation + + :param interface: the DceRpcInterface object """ return self._bind(interface, DceRpc5AlterContext, DceRpc5AlterContextResp) - def bind_or_alter(self, interface): + def bind_or_alter(self, interface: Union[DceRpcInterface, ComInterface]): """ Bind the client to an interface or alter the context if already bound + + :param interface: the DceRpcInterface object """ - if not self.sock.session.rpc_bind_interface: + if not self.session.rpc_bind_interface: # No interface is bound self.bind(interface) - else: + elif self.session.rpc_bind_interface != interface: # An interface is already bound self.alter_context(interface) - def open_smbpipe(self, name): + def open_smbpipe(self, name: str): """ - Open a certain filehandle with the SMB automaton + Open a certain filehandle with the SMB automaton. + + :param name: the name of the pipe """ self.ipc_tid = self.smbrpcsock.tree_connect("IPC$") self.smbrpcsock.open_pipe(name) @@ -493,15 +640,20 @@ def close_smbpipe(self): def connect_and_bind( self, - ip, - interface, - port=None, - timeout=5, + ip: str, + interface: DceRpcInterface, + port: Optional[int] = None, + timeout: int = 5, smb_kwargs={}, ): """ Asks the Endpoint Mapper what address to use to connect to the interface, then uses connect() followed by a bind() + + :param ip: the ip to connect to + :param interface: the DceRpcInterface object + :param port: (optional, NCACN_NP only) the port to connect to + :param timeout: (optional) the connection timeout (default 5) """ if self.transport == DCERPC_Transport.NCACN_IP_TCP: # IP/TCP @@ -517,7 +669,7 @@ def connect_and_bind( else: return # 2. Connect to that IP:PORT - self.connect(ip, port=port) + self.connect(ip, port=port, timeout=timeout) elif self.transport == DCERPC_Transport.NCACN_NP: # SMB # 1. ask the endpoint mapper (over SMB) for the namedpipe @@ -693,6 +845,7 @@ def get_endpoint( transport=DCERPC_Transport.NCACN_IP_TCP, ndrendian="little", verb=True, + ssp=None, smb_kwargs={}, ): """ @@ -702,6 +855,7 @@ def get_endpoint( :param interface: :param mode: :param verb: + :param ssp: :return: a list of connection tuples for this interface """ @@ -710,6 +864,7 @@ def get_endpoint( ndr64=False, ndrendian=ndrendian, verb=verb, + ssp=ssp, ) # EPM only works with NDR32 client.connect(ip, smb_kwargs=smb_kwargs) if transport == DCERPC_Transport.NCACN_NP: # SMB diff --git a/scapy/layers/msrpce/rpcserver.py b/scapy/layers/msrpce/rpcserver.py index 807f111d0c9..5a8435cac17 100644 --- a/scapy/layers/msrpce/rpcserver.py +++ b/scapy/layers/msrpce/rpcserver.py @@ -17,22 +17,23 @@ from scapy.volatile import RandShort from scapy.layers.dcerpc import ( + CommonAuthVerifier, + DCE_RPC_INTERFACES, + DCERPC_Transport, DceRpc5, - DceRpcSession, + DceRpc5AlterContext, + DceRpc5AlterContextResp, + DceRpc5Auth3, DceRpc5Bind, DceRpc5BindAck, DceRpc5BindNak, - DceRpc5Auth3, - DceRpc5AlterContext, - DceRpc5AlterContextResp, - DceRpc5Result, + DceRpc5PortAny, DceRpc5Request, DceRpc5Response, + DceRpc5Result, DceRpc5TransferSyntax, - DceRpc5PortAny, - CommonAuthVerifier, - DCE_RPC_INTERFACES, - DCERPC_Transport, + DceRpcInterface, + DceRpcSession, RPC_C_AUTHN_LEVEL, ) @@ -45,6 +46,12 @@ prot_and_addr_t, ) +# Typing +from typing import ( + Dict, + Optional, +) + class _DCERPC_Server_metaclass(type): def __new__(cls, name, bases, dct): @@ -58,22 +65,20 @@ def __new__(cls, name, bases, dct): class DCERPC_Server(metaclass=_DCERPC_Server_metaclass): def __init__( self, - transport, - ndr64=False, - verb=True, - local_ip=None, - port=None, - portmap=None, + transport: DCERPC_Transport, + ndr64: Optional[bool] = None, + verb: bool = True, + local_ip: str = None, + port: int = None, + portmap: Dict[DceRpcInterface, int] = None, **kwargs, ): self.transport = transport self.session = DceRpcSession(**kwargs) self.queue = deque() + if ndr64 is None: + ndr64 = conf.ndr64 self.ndr64 = ndr64 - if ndr64: - self.ndr_name = "NDR64" - else: - self.ndr_name = "NDR 2.0" # For endpoint mapper. TODO: improve separation/handling of SMB/IP etc self.local_ip = local_ip self.port = port @@ -130,6 +135,8 @@ def spawn(cls, transport, iface=None, port=135, bg=False, **kwargs): :param iface: the interface to spawn it on (default: conf.iface) :param port: the port to spawn it on (for IP_TCP or the SMB server) :param bg: background mode? (default: False) + :param ndr64: whether NDR64 is supported or not (default: conf.ndr64). + This attribute will be overwritten if the client doesn't support it. """ if transport == DCERPC_Transport.NCACN_IP_TCP: # IP/TCP case @@ -287,39 +294,60 @@ def recv(self, data): auth_value=auth_value, ) - def get_result(ctx): + # Detect if the client requested NDR64 and the server agrees + self.ndr64 = self.ndr64 and any( + ctx.transfer_syntaxes[0].sprintf("%if_uuid%") == "NDR64" + for ctx in req.context_elem + ) + + # Process bind contexts and answer to them + results = [] + for ctx in req.context_elem: + # Get name name = ctx.transfer_syntaxes[0].sprintf("%if_uuid%") - if name == self.ndr_name: + if ( + # NDR64 + (name == "NDR64" and self.ndr64) + or + # NDR 2.0 + (name == "NDR 2.0" and not self.ndr64) + ): # Acceptance - return DceRpc5Result( - result=0, - reason=0, - transfer_syntax=DceRpc5TransferSyntax( - if_uuid=ctx.transfer_syntaxes[0].if_uuid, - if_version=ctx.transfer_syntaxes[0].if_version, - ), + results.append( + DceRpc5Result( + result=0, + reason=0, + transfer_syntax=DceRpc5TransferSyntax( + if_uuid=ctx.transfer_syntaxes[0].if_uuid, + if_version=ctx.transfer_syntaxes[0].if_version, + ), + ) ) elif name == "Bind Time Feature Negotiation": - return DceRpc5Result( - result=3, - reason=3, - transfer_syntax=DceRpc5TransferSyntax( - if_uuid="NULL", - if_version=0, - ), + # Handle Bind Time Feature + results.append( + DceRpc5Result( + result=3, + reason=3, + transfer_syntax=DceRpc5TransferSyntax( + if_uuid="NULL", + if_version=0, + ), + ) ) else: # Reject - return DceRpc5Result( - result=2, - reason=2, - transfer_syntax=DceRpc5TransferSyntax( - if_uuid="NULL", - if_version=0, - ), + results.append( + DceRpc5Result( + result=2, + reason=2, + transfer_syntax=DceRpc5TransferSyntax( + if_uuid="NULL", + if_version=0, + ), + ) ) - results = [get_result(x) for x in req.context_elem] if self.port is None: # Piped port_spec = ( @@ -349,7 +377,9 @@ def get_result(ctx): print( conf.color_theme.success( f">> {cls.__name__} {self.session.rpc_bind_interface.name}" - f" is on port '{port_spec.decode()}' using {self.ndr_name}" + f" is on port '{port_spec.decode()}' using " + ( + "NDR64" if self.ndr64 else "NDR32" + ) ) ) elif DceRpc5Request in req: diff --git a/scapy/layers/ntlm.py b/scapy/layers/ntlm.py index 7f92430e1f8..775bf06240d 100644 --- a/scapy/layers/ntlm.py +++ b/scapy/layers/ntlm.py @@ -2045,15 +2045,6 @@ def _getSessionBaseKey(self, Context, ntlm): return bytes(UserSessionKey) else: # Failed - from scapy.layers.smb2 import STATUS_ERREF - - print( - conf.color_theme.fail( - "! %s" % STATUS_ERREF.get(resp.status, "Failure !") - ) - ) - if resp.status not in STATUS_ERREF: - resp.show() return super(NTLMSSP_DOMAIN, self)._getSessionBaseKey(Context, ntlm) def _checkLogin(self, Context, auth_tok): diff --git a/scapy/layers/smb2.py b/scapy/layers/smb2.py index a19c5b97159..d76f982b745 100644 --- a/scapy/layers/smb2.py +++ b/scapy/layers/smb2.py @@ -92,6 +92,8 @@ # SMB2 sect 3.3.5.15 + [MS-ERREF] STATUS_ERREF = { 0x00000000: "STATUS_SUCCESS", + 0x00000002: "ERROR_FILE_NOT_FOUND", + 0x00000005: "ERROR_ACCESS_DENIED", 0x00000103: "STATUS_PENDING", 0x0000010B: "STATUS_NOTIFY_CLEANUP", 0x0000010C: "STATUS_NOTIFY_ENUM_DIR", @@ -101,6 +103,8 @@ 0x80000005: "STATUS_BUFFER_OVERFLOW", 0x80000006: "STATUS_NO_MORE_FILES", 0x8000002D: "STATUS_STOPPED_ON_SYMLINK", + 0x80070005: "E_ACCESSDENIED", + 0x8007000E: "E_OUTOFMEMORY", 0x80090308: "SEC_E_INVALID_TOKEN", 0x8009030C: "SEC_E_LOGON_DENIED", 0x8009030F: "SEC_E_MESSAGE_ALTERED", diff --git a/scapy/layers/smbclient.py b/scapy/layers/smbclient.py index 14eb47f8cf5..360acbce824 100644 --- a/scapy/layers/smbclient.py +++ b/scapy/layers/smbclient.py @@ -1216,7 +1216,11 @@ def __init__( # For some usages, we will also need the RPC wrapper from scapy.layers.msrpce.rpcclient import DCERPC_Client - self.rpcclient = DCERPC_Client.from_smblink(self.sock, ndr64=False, verb=False) + self.rpcclient = DCERPC_Client.from_smblink( + self.sock, + ndr64=False, + verb=bool(debug), + ) # We have a valid smb connection ! print( "%s authentication successful using %s%s !" @@ -1271,7 +1275,7 @@ def shares(self): # Poll cache if self.sh_cache: return self.sh_cache - # One of the 'hardest' considering it's an RPC + # It's an RPC self.rpcclient.open_smbpipe("srvsvc") self.rpcclient.bind(find_dcerpc_interface("srvsvc")) req = NetrShareEnum_Request( @@ -1283,10 +1287,12 @@ def shares(self): ), ), PreferedMaximumLength=0xFFFFFFFF, + ndr64=self.rpcclient.ndr64, ) resp = self.rpcclient.sr1_req(req, timeout=self.timeout) self.rpcclient.close_smbpipe() if not isinstance(resp, NetrShareEnum_Response): + resp.show() raise ValueError("NetrShareEnum_Request failed !") results = [] for share in resp.valueof("InfoStruct.ShareInfo.Buffer"): diff --git a/scapy/modules/ldaphero.py b/scapy/modules/ldaphero.py index 649673a5c12..dd518ec0ecc 100644 --- a/scapy/modules/ldaphero.py +++ b/scapy/modules/ldaphero.py @@ -697,7 +697,8 @@ def resolvesids(self, sids): IDL_DRSBind_Request( puuidClientDsa=NTDSAPI_CLIENT_GUID, pextClient=DRS_EXTENSIONS(rgb=bytes(DRS_EXTENSIONS_INT(Pid=1234))), - ) + ndr64=client.ndr64, + ), ) if bind_resp.status != 0: self.tprint("Bind Request failed.") @@ -719,7 +720,8 @@ def resolvesids(self, sids): rpNames=unknowns, ), ), - ) + ndr64=client.ndr64, + ), ) if resp.status != 0: self.tprint("DsCracknames Request failed.") diff --git a/scapy/modules/ticketer.py b/scapy/modules/ticketer.py index 6abb6e98b1b..87c591753bc 100644 --- a/scapy/modules/ticketer.py +++ b/scapy/modules/ticketer.py @@ -88,7 +88,7 @@ PAC_CLIENT_INFO, PAC_INFO_BUFFER, PAC_INFO_BUFFER, - PAC_REQUESTOR, + PAC_REQUESTOR_SID, PAC_SIGNATURE_DATA, PACTYPE, RPC_SID_IDENTIFIER_AUTHORITY, @@ -1414,7 +1414,7 @@ def _build_ticket(self, store): ) + ( [ - PAC_REQUESTOR( + PAC_REQUESTOR_SID( Sid=self._build_sid( store["REQ.Sid"], msdn=True ), @@ -1867,7 +1867,7 @@ def _getPACRequestor(self, pac, element): pacRequestor = pac.getPayload(0x00000012) if not pacRequestor: pac.Buffers.append(PAC_INFO_BUFFER(ulType=0x00000012)) - pacRequestor = PAC_REQUESTOR() + pacRequestor = PAC_REQUESTOR_SID() return self._make_fields( element, [("ReqSid", self._pretty_sid(pacRequestor.Sid))] ) diff --git a/scapy/packet.py b/scapy/packet.py index 8c558513157..8f17a104b00 100644 --- a/scapy/packet.py +++ b/scapy/packet.py @@ -717,7 +717,7 @@ def self_build(self): except Exception as ex: try: ex.args = ( - "While dissecting field '%s': " % f.name + + "While building field '%s': " % f.name + ex.args[0], ) + ex.args[1:] except (AttributeError, IndexError): diff --git a/test/scapy/layers/dcerpc.uts b/test/scapy/layers/dcerpc.uts index 832767e9e26..e496b68719c 100644 --- a/test/scapy/layers/dcerpc.uts +++ b/test/scapy/layers/dcerpc.uts @@ -407,7 +407,7 @@ import zlib data = zlib.decompress(b'x\x9c\xed\x9dw\x9c\x13\xd5\xfa\xffg\xe9K\xdb\x05D\x8a\x94(Udq2-\x13@$uY\xb6\xb2K\x17\x81\xc9\xccd\t[\x12\x92\xb0\x14\x15dY\x90*\xbdw\x10iJG\xe9 ^T@DDl\xc0\x15\xb9(`\xa1\xd8\x81\x8b~\x93%\x08\t$\xcf\x9c3s\xff\xf9\xfd\xc8\xeb\xb5\xafA?\x9fy\xe7\xccs\xce<\xcf\xcc\x99\x12\x82\xb8\xff\xb3>\x8e ~\x8d\xbb\xfb\xef\x9d3\xb3\x8bv\xf7\xdf\xdei\xfa\xa5\xf1\xb7\x86\x14-\x9a\x16\x92\x88;\xcbH\xbd\x0c\xa0\x97\x05\xf4r\x11\xfae\xd7\xd8\x8d\xdb&\xad\xb4,\xadS\xf2W\xf0\xbf\xcb\x03z\x05@\xaf\x18\xa1\x1f<\xd2}1\xf3]\xaey\xd4\xab\x7f\xaf:\xfd\xcd\x8d\xb1\x95\x00=\x1e\xd0+\x03z\x15@\xaf\n\xe8\xd5\x00\xbd:\xa0\'\x00z"\xa0\xd7\x88\xd0#?5\x01\xbd\x16\xa0?\x02\xe8\xb5\x01\xfdQ@\xaf\x03\xe8u\x01\xbd\x1e\xa0\xd7\x8f\xd0\x85\x1fz\x9cZ^\xbfB\xca\x8c\xd9M.n/\x93F=\x06\xe8\r\x00\xbd!\xa07\x02\xf4\xc6\x80\xae\x03\xf4\xc7\x01\xfd\t >M\x00\xbd)\xa07\x03\xf4\xe6\x80\xde\x02\xd0[\x02\xfa\x93\x80\xde*B\x9f\xf0\xfa\xa7/\xf6\xcd\xca\xb3mjt\xb8h\xe5\x99\x94\x06O\x01zk@O\x02\xf46\x80\xfe4\xa0\x93\x80\xae\x07\xb6\x9f\x02t\x1a\xd0\x19@g\x01\x9d\x03t\x03\xa0\xf3\x80n\x04\xf4\xb6\x80\xde\x0e\xd0\xdb\x03\xfa3\x80\xde\x01\xd0\x9f\x05\xf4\x8e\x80n\x02t3\xa0[\x00\xdd\n\xe86@\xb7\x03z2\xa0w\x02\xf4\x14@\xef\x0c\xe8\xa9\x80\x9e\x06\xe8\xe9\x80\x9e\x01\xe8\x99\x80\x9e\x05\xe8]\x00=\x1b\xd0s\x00\xbd+\xa0w\x03\xf4\xee\x80\xde\x03\xd0{\x02z/@\xef\r\xe8\xcf\x01z\x1f@\x7f\x1e\xd0\xfb\x02z?@\xef\x0f\xe8\x02\xa0;\x00]\x04t\t\xd0e@w\x02z.\xa0\x0f\x00t\x17\xa0\x0f\x04\xf4<@\xcf\x07\xf4\x02@/\x04t7\xa0{\x00}\x10\xa0{\x01\xdd\x07\xe8~@\x1f\x0c\xe8E\x80>\x04\xd0\x87\x02\xfa0@\x1f\x0e\xe8/D\xe8~\xd9[\xe0\xf3\x16\xfd\xa3\xbf\x18Z\x06\xcf\x93R\n<\xf9:\xa7\xd7%\x17J\xf9\xc3t\x85B\x81|\xdf\xf9l\xdcK\xc0\xf7\x8d\x08-\x83\xc7\xa5\x19\xb2\x7f\x88\xdb\x9b\xa7\xb3\xb8\x0b\x0be\xd1\xefr\x17\xea\xcc^w\x9e\xec\xd5\xf9doQ`\x11\xf8"\x8f\xdbU\xe8\x7f\x00g\xa4F\x9c\x975\xe2\x8c\xd2\x88S\x1cZ\x06\xcf\x1bSMY)\xba\x9c\xc0*.Q\x8e\xb5\xceh\x8cuJ0\xd6\x19\x83\xb1\xce\xd8\xd0\xf2Y\x94\xb8\xe8\x9cn\xaf.\xc3b\xd6e\xcb>\xd9\xaf+pK\x83\xf3\xe5\xfb\xd9\xafh\xc8~o\xde\xc7\xb7*|].e\xf4\xd6\xd5\xc3[Lm\xef\x19\x17bG\x1b\xc7\xe3\x01}B\x84~\xe5\xe4\x87\xdbxs\x15\xeb\xae\xa5\xf3\x96\x14Oi\xf0\xd7D`\xfdI\xa1ep\xfe!#;\x0b\x1c9\x93\xef\xf5\xe7\xa4\x80\xfeWC\xcb\xe0\xfc\x8a\xadH\x0e\xc4%\xdf\x9d\xab\xebj\xc9J\xc9\xba\xcf;\x05\xc1;\x15\xc1;-\xb4\x0c\xce1X;Y\xb2\x8a8\x9d%\xdf\x15\\\'-;\xcb\xa2\xb3Ek\xfbt\xcc\xf5f\x84\x96\xf5C\xebE[\xeb\xfd?\x8e7\xddP\xd42s\xe1\xd1\x91\xf9o\xd9\xc7\xc6\xcd\x0c\xad\x17\x9c+I\xf6\xba\x07{tY\xee|\x978L\x17\\/\xa50\x902\x9d\x82\x18\x18C\xb9\x1e\xb1\x14x\xe7\xfbf\x85\x96\xd1r\xa7\xc7\xebv\xba\xf2\xe5;%\x88\x98\r\xf8}r\xe1?\xde\xe0g\x0e\xa2\x7f.\xe0\x8f\xfc\xcc\x0b-\xa3\x8d\xd1\xf9\x80\xbe\x00\xd0\x17\x02\xfa"@_\x0c\xe8K\x00})\xa0/\x03\xf4\xe5\x80\xbe\x02\xd0_\x03\xf4\x95\x80\xfe:\xa0\xaf\x02\xf4\xd5\x80\xbe\x06\xd0\xd7\x02\xfa:@\x7f#\xb4\x0c\xeeW=\\\x85\x9d\xfc~\x8f\xce4\xd8\xefN\xca\xf2\xba\x87\x0e\xbbS]\xee_\xefM\xcc\xf5\xd6\x87\x96\xc19\xefn\x81\x04\x99\x9e\xeb\rd\x80\xfb}\x1b\x14\xfa6*\xf4mR\xe8\xdb\x1cZ\x06\xe7\xfc\xedCt\x81\n\xeb\xbb\xcf\xb3E\x81g\xab\x02\xcf6\x05\x9e\xb7\x14x\xdeV\xe0\xd9\xae\xc0\xb3C\x81g\xa7\x02\xcf\xae\xd028/n\x16|\xb2\xce\xee\xf2\xcaC\x84\xfc\xfc@\x82\xcfu\x15\xca\xc1\xb5\xee[)\xf0\xd9\x1dZ\x06\xafWX\x9d>\xab\xaf\xb4\x82>p\x18\x11{\x10\xbc{C\xcb\xe0\x0e-\x83\xd7/n\xa7\xcdt\xa1P\xc8\r\x1c2\xde>\x86Qp\xaaB\x1c\xd7\x80\xf1\x89\x06\x8c\x13\xa1e\x8b\xfb\x18\x81#\xac"\x97\xa4\xe4\xc4\xebS\r\x18\'5`|vO\r\xd0\xa7\x87\x84\xd2\xfb\xc2\xd2\xfb\xf5p\x15J\xee!\xa1K\xf0\xd9Y\x96>\xd1\xee\x96\x99\x01pg\x02\xfa\xac\x90\x10\xbc\xe7\xa2\xbb\xa70\xea=\x17\xb3\x15\xfa\xe6(\xf4\xcd\x8d\xe2s\xaei\x917zz\xcf\xb4U\xe3\xc7\xfe\xfdJ\xe2\xce\xc5\xf3"\xda\x1f\xa9\xcf\x8f\xd0OU\x95\xcc\xcf\xb3\xc6\xcc\x05sV\xd1S+V\x18\xbf B\xb7\x97;;|\x89\xef-[\xf1\x91W\xe6\x0cJ\x1c;la\x84\xce2\xba\xdcn\xee\xda\xb6\x89SV1\x1b\x9b?\xfa\xc8" ~\x8b\x01}\t\xa0/\x05\xf4e\x80\xbe\x1c\xd0WD\xe8K{\x9dj\xc7}nM\x9f\xea\x7f\'xi4\xee5`\xfd\x95\x80\xfe:\xa0\xaf\x02\xf4\xd5\x80\xbe&B\xaf\xf8\xd3;\xcf\xfd\xf6[\xbfN\x8bn\xb9?~\xe3\xfa\xf2\xf6kCB\xf0Y~\xbb\xd7G\xdd{\xe1,\xd2\xbb.\x867\xf2\xf3\x06\xd0\xae7\x01}}H\x08^\x1f\xc9\x12\x85\x9c"\xf1\xf6\xf3\x8a\xc1\xbf\xf2\x81\x8c]\xcd\xf3W\xef/O\xbe\xde\xcb\\\xdc\xa6u\xc9W\rOU\x8b#\x82\xaf{\x08\x08\xe5\x9e\x1f7\xe1\x87\x06\x87j,\xb9X\x89x*\xb1S\xff2\xa5B\x1cQ\xe5\xf6\xa2b\x19\xe2@`\x11_\x8e\xd8\xbf\x9a\x90J\x9f1\x0c\xfe\x95\xc3bV\xbd\xbdHL$n\'\x1c_\xce\x80\xc1\xfe\xc0\xb2\xb0\xf4\xd9\xb7\xbej\xdb\x9aP\x8b\xe8\x93\x95\x92e\xeb\x93R\xe8\xf2\xff\x83\x8e#jT \xfa\xf4\xb1Z\x82\xcf?g\x86\xfe\xd4\xb5?!\x903}\xb9\xa9\xd9\x1e\x914P\x06\x03I\x84\xc7\xe5\xdd\xe6?\x17=\xf2c3\xcb\x84y\x03\xedLI\xdd\rZ\xc5\x05\x99\x8b\x19\x17\xec\xf6\xdf\x1f\x97\xe03\xb9\x9d\xeep\xb7\xfd{\xc1\xc7\x83\x04\xa6\xf3\xbes\x15n\x14\xcb\xcd\x06(\xe6V"D\x9f\xec\xf0\x0cv\x94>\xe3\xd9S5\xaf.\x11|\x8e!\xc9\xc1\xc92/K\x94\x91eX\x916:EN[\xbe\xec4R\x06=\xe948E\x07\xc5\x18)J\xd0\x96op2<\'\x91F\xbdL3\x06R\xefp\x8a\xda\xf2Y\x92\xe2\x1c\x92\xc0\x93\x1c-R$GQFm\xf9zFt\xb2\x06\xd6\xe1`$Jp\xf2\xa2\x91)}\x86\xb5\xbfj\xbe\x8e\xc8L\xb3\xd9(\x1bI[\xf4\x9c\x89\xb2\x1b\xf5\xa4\xc1\xce\x1b,,e\xd2\xdb\xedv\x9e\xd7v;H#gt\xb2N\'\xa9\xa7\x05#\xc7\x8b4\xd6\xd5\xf2\xa1\xb1\xae\x96\x0f\x8dul>\xe2XW\xbb\x1d\xd0X\xc7\xe6\xc7\x18\xeb\xd8\xcc\xffQ^\x7f\x98w\x1f\xe6]\x15y7\x8c\xf7X\xc5^gjS\xbd\xd2JF\xcdi\xf3\xd5\xe9\x84\x81jy/\xc4%Wk\xb7\xe5M\xfb\xe2\xba\x7f\xcd\xb6\xe5g\rU\xcb\xab\xe2\xee8\xcb\xb8\xb9\xaee\xda\xae\x8fV\xbc\xe6\xbezZ-\xef\xc3u_d\x9c\xbb6\xd12\xb7\xf1\xae\xea\x13v\xc7UU\xcb\xb3\xff~-aW\xad\xca\x1d\xd7e\xcd\x1f\xfa\xf3\xdb\xab{\xab\xe5\x15.-v\x9d\xeb\xb7\xc9\xbee\xe1\xf2\x83\xb6}K]jy\x83\x98V\x95\xde\xd2\xb72\xef\xd0\xaf\xb4\xc6\xcd\xae\xeaP\xcb\xdb\xc8\x9d\xbf!m\x7f\xa5\xf3\xe2\xaf\x9e\x1c\x93|\xb4\xf8g\xb5\xbc\xc33\x9f\xf9V\xf2\x1b;m\xb4\xc6/\xbf9\xb1\xed\xb7jy{K&\xd7\x996}fJ\xf1{\xcfN\x9d\x7f\xb8wY\xb5<\xe7\xa57\xd2-\xcb\xb3\xac\xeb\x9f]\xf0\xc2\x91\x9c\xda\xdb\xcb\xa8\xe4\xfd\xbe\x9b\xaa\xbb\xfd\xf0\xa7\xa61\x9f\x1c2U\x94\xfeP=^\x8e\xddx\xfc\xe0\x9a\xf8\x83\xe6M\xdd\xbc\xfc\x98YG\xae\x12*y\x1f}9\xb2u\x85\x0b\x1dS\xb7\xd6\x9c\xa5\xafxe\xc1F\x0c^X\x8e}\xf7\xc8\xbaq\xdf3\xeb\x8b\x8ak\x17\r\xd6\xc9\xbd\x9b(\xe6)\xacEj\xf9P-\xc2\xe6#\xd6"\xb5\xdb\x01\xd5"l~\x8cZ\x84\xcd\x8c2Vv\xd79P\xb4\xf5\x838\xf3*\x9b\xa1\xed\x97e\x9f\xaa\xaf\xf5q\x8bZ>4V\xb0\xf9\x88cE\xedv@c\x05\x9b\x1fc\xac`3\xa3\x8c\x15j\xc7w\xdd\x86\xd5\xfb\xcc6\xf5\xe0H\x87\xc7k\xaa\xa9\xf5XQ\xcb\x87\xc6\n6\x1fq\xac\xa8\xdd\x0eh\xac`\xf3c\x8c\x15lf\x94\xb1\xb2w\xd3\xc81\x99\x95\xb6en[\x98\xda:\xf5\xdb\xab\xc7\xb4\xeeKl>b_\xaa\xdd\x0e\xa8/\xb1\xf91\xfa\x12\x9b\x19\xa5/7\x9c:\xbc=\xf5\xe2\x0e{\xc9\xcf\xeeA\xfb\x9f]\xdcM\xeb\xbe\xc4\xe6#\xf6\xa5\xda\xed\x80\xfa\x12\x9b\x1f\xa3/\xb1\x99Q\xfarV\x99\x7f\xe9vN\xfe\xae\xd3\xfa}\x1d\x86^i\xb7\xc9\xaau_b\xf3\x11\xfbR\xedv@}\x89\xcd\x8f\xd1\x97\xd8\xcch9vw\xadf\x9f\xe6w\xb2\x8c\xe9;\xe2f{\xba\xe0\x84\xe69\x16\x97\x8f\x9acUn\x07\x98cq\xf9\xb1r,.3J_\xb2-:T[\xfa\xd1\x87\x19k\xce\x99\x96\x7fQ\xff\xa7\x0c\xadc\x80\xcd\x8f\x11\x03lf\x94\x18\xdc\xbc^\xfe\xbd\x15W[e,\xf5\xf9\xba\'L^\xdcA\xeb\x18`\xf3c\xc4\x00\x9b\x19%\x06\xd3\x0e\x1b\x1e\xdbT\xd47s\xc5\xfb\xad\xf6\xbd\xd7\xf8\xa9\x8dZ\xc7\x00\x9b\x1f#\x06\xd8\xcc(1\xf8\xa6\xdd\x7f\'^)H\xca\xd8\xe0\x7f1\xf9h\xe5\x7fw\xd6:\x06\xd8\xfc\x181\xc0fF\x89A\xc7Us\x93\x1a\xf6\xdcaZ[m\xf5\xc4E}.\xf1Z\xc7\x00\x9b\x1f#\x06\xd8\xcc(\xf3b3\xd7\x0be\x1f-\x9b\x9f6\xe1\x8b\x833\r=\x89sjy\xedF7\xedY\xbf\xfd\'\x9d\xf7\xdcz\xb7a\xbds\x1dp\xee\x15\x08\xe3y\xe2\xd7\xeeig\xea\xd0y\xf1\xf3\xbf\x9f^4\xa2b\x7f\xb5\xbc\xf1\x93\xe9v7\xf2\xb7[KF\xe4\xd4\xd6\xef\x9e7]-o\xc7\x89\xe2W\xf6\xfc`\xb5\xaf\xf8\xe2\xa27g\xd4\xb4\x05jyL\xd9\xe7*u=?-}\xc1$\x13s\xfd\xc2\x8c_\xd4\xf2\xb6\xe7\xfd\x98z`\x9b;u\xf3;\xcf\xaf<6\xfd\xe3/U_\xe3+x\xe3\xd0\xdb\xc5\xdd2\xd6O8/\xfd\xf4\x82y\x08\xf2\xf5l\x03O\xd12)\x89N\x92b$F\x14\xc4\xff\xc1\xbd\x95\xacE\xaf\x8fhw\x8f\x17\x12\xce7\x1b~"m\x19;\xf2\xa5g\xae\x1f\xcfCm7\xcf3NQ\x12DR&I\x9a\xd4\x0b\x066\xfc8\x0e\x9b_z\x1c\xc7\x1a\r\xbc\x9e4Q6\x83\x9d\xb2\x9bi\x13G\xf2&\x83\x81d9\x96\xa4\xb4\xdd\x0e\x87\x18\x8c?\xcd\xf1\x0e\x86%e\x9a\xa7Hm\xf9\xfa@\xb7\x8a,K\xb1\x94\x9118\x1c\x923\xe2\x9e\xd4\xbe\'\x13\xc6\xed\xb0\x9c\xad\xb1i\xf5\xb1\x8c\xaf?\xd7\x8d\xd1\xba\x1f\xb0\xf9\x88\xfd\xa0v;\xa0~P\xcb\x87\xfa\xa1q\xf1\x13\xd9\xe7N\x8e\xe9Tr\xad\x9f0f\xe9\x8f&\xad\xdb\xaf\x96\x0f\xb5_\xf5\xb5z\x80\xff\xde\xa7\xc4\xca\xe3\xd5,\xb6\xddUO^g\xc6~\xff\'\xfa\xb1A\xe0\xc4\xcf)\xf2\x92\x93"I\xc9\xc8\x90Fm\xf92\'3\x12-\x08\xa4\xd1ht\xca\x02\xad\xd7\xf8\x1e8\x88_\xf5\xe0\x9b\xe4\xee>\x1f\xd9\xe7W\x9f6-y\xfc\xb9\xf6\xa8|\'\xc3\x1aE\x07\xcd\xf3N\x83\x81rH")\x85\xf3\x8fUY5bC\xa7\x81\x99\xe3\x07\x0c\xb4V\xecz\xf2\x06r\xffR\x82(r\xa4^b\x04\')\xcb\x81\x7f\x87?\xef\xf1\xc7\x86\xbd\xbf\xd7k\xb4+s\xc9\x94\xed\x8buu\xe9\x17\x95>+\x10|\xde#\xee\xee\xf3\x1e\xc1\xdfD\xea\xad\x96\x99\x90@\xf4\xf1\xb8\xac\xfd\x1b\xb3_5\xac\xd9\x7f\xacFq\x12\xb1\x96\xe8r>A\xeb\xb1\x83\xccGl\x7fq\x85?\x1aw\xa9|\x91\xacX\xe0\xafr\xf1\xa3K\x13\xb5n?2\x1fq\xec7\xb8\xe2\xaf\xfc\x9a\xa9B\xc6\xe8\xf5\xa7\xfd7\x1b\x9c\xc9\xd1\x80\x1f|HZ\xbe\xc3O?\xe3c\xc6\xb5\xa9\xd0q\xe1\xce\'z\xc4\xc7\x9fI,\x1f\xc0\xc4)\xe17#H\xd9IR2\xcf\'\xc9\x0e\x9aNb\x18=\x97\xc4S\x14\x95\x148\xb3&\r$\xed\x08\xeck\xc2}\xc7L\xd8\xdfw\xe7\xdcHp\xf2\x1c-\x88\xce\xc0\x169dJd\xd8p\xbend\xfbj\x93\x1bt\xb5/\xdb\xd2\xa5{\xbb\'\'~\x85|\xcc\xc4p\x06\x03\xc712\xeb`\x03Gd\x8c\xde\x18>G\x80\xcd\xbf}\xcd\xcd\xc22\x16\xcefb\xad\xac\xd1`dm\xa4EO\x1b\xf5\xb4IO[#\x9e\xe9{k\xe7\x94M\x7fv/\x93\\r\xd6T\x9e\x9a\xb1Z\xf9=\xaf\n\xb7\x03\x9b\x8f\xb8\x1d\xc5\xcdw<=O>m^_kO\xeb\xd5\x0b\x8e7@\xae\x9bFY\x12D\x81!y\xde@\xd3\xbc\xc09\xb5\xe5;\x04\x87@\n\xb23p$\xaeg\xf5\xa4\xc0;\xb4\xe5\x8b\x0c/\xd1\x0c\xcbI\xa4\x9ebX)\xb01\xda\xf2\xe9 \x94"%\xde\x118\xc6\xd7\x1beg\xe4\xbd\xa0\xb4\xff\xd7E\x1dz\x98\xd6~:zm\xe3>IiZ\xc7G-\x1f\x8a\x8fZ>\x14\x9f}5\x16\x8d\xdc12\xcf\xb2\xba\xe6\xe6\x93U\x9fOS~o\xb4\xc2\xf6\xab\xe5C\xedoV.\xe5L\xb3\x1c\xd9\xf6\xb6\x7fD\x8b\xcf\xf6/l\xaa!\xbf\xb4~\xfee\xa95rMV\x8b\x94UC\xab\xf6\xec\xd5\xe5\x857\x14\xd7\xcfj\xa1\xfa9$\xcf\x17Q@\xc3\xda\x8f\xcc\x8f\x8c\xbf^\x96\x1c<\x1f8\xbbu\n\xc1\x19\'*"\xffT\xab_\xe9\x87\xcb\x89\x13\x92\x97\x9d\xc9\x9a\x97=\xfa\xc2\xea\xb2\x1a\xf3\xe3\xb7\x1e\xfa\xf9\xd0;\x84i\xac\xf1\x8bu\x8f\x8cu!_\x0b\x85\xf8g\xb6\x1d?0\xeaF\x7f\xeb\xa2\xcf.\xc8\x13\xc7\xd8R\x91\xf9\xb2\xc8K\x0e\x87S\xa6(\x89\x97E\x83\x1cQ\x07\xb0\xf9\xa5u\xc0L\x19\x18\xbd\x9e\xe2\xf4V\x0be5\x18H\xd2\xa6\xe7X\xab\x857\xd9-\x11\xe3t\x83#\xe3\xd7M_\x1d\xb0\xbe\\\xab\xdc\xfcE\xe7\xeb(\x9f\xd3Q\xb8\x1d\xd8|\xc4\xed(\x9f\xfb\xc3\xf0\xef\xda\xd6O\x1e\x97\xf4\xcb\xc2\xc4\xc5\xa7\x91\xef\x1d\x85\xb6\x03\x9b\x8f\xb8\x1d\x8c\xf0\xc7\x85\xdf\x16\xa7\xa4,\xff\x8c\x1e\xf0a\xfc8\xe5\xd7\n\x15n\x076\x1fq;*O\x1d\xb9\xe5\xfc\xf7\xe5,\xcb\xd7\xd5\xdc\xe5\x9c\x147N\xeb\xed\xc0\xe6#n\xc7\x8a\xfc\x9b\xeb\xfaVe\x92\xa7\x8f\xa9|\xd9\xfb\xd7d\xf4\xe3\x0c`;\xb0\xf9\xf0v\x84}\x8f\xa7\xf7\xfc\x7f\x8d\x9c\xe0\xc9\xd8\xf9h\xa3)\x8d\xebu\xb8\x86\xf6=V+\xc3\xf1\xa4\xd9\xcc[\xadz\xce@2F\x93\xc1\x1c8\xb0\xd4\x9b)J\x1f>w\x89\xfd=\xd5\x89\xae\xb2W\x96\xdc\x16w\xa1\xdf\xeb\x0e\xde#\x91\x1d\xfaS\xc7\xad\x19\xe2Z]Bn\xa1\xdb\xe7w\x89>"\xe2\xdc\x07\x9b}\xe7\xba\x87\xcc\xd0\x06\x9ee\x9d\x92\xa4g\x04V\x16\xc5\xf0\x98\x0c9\x9b\xf7n\xea7\x972\'\xec\xbe\xd8\xfdJf9\xe5\xe7\xa2@L\xb0\xb9\nb\x82\xcdV\x18\x93\xcdl\xff6\xd7.\xae2-\xb5\xcay\x96\x85i\x8cV1\xc1\xe6*\x88\t6;zL\xc2\xf8\x03+&u{\xf2\x885c\xe2\xc0\xe3}{\x9d\xdb\xaf\xfc>\x89\xe8\xfc\xb0\xf9\xd7\xf5-\x96wy\xae\xf8g\xd3\xd65\xf5\x0fu\x1b\x9eX\x17e\xfe\xb5\xe6\xdd\xf9\xd7\xb0~Df\xde\xed\xc7\x0c[\xd7\xb4\xcc\xe4\xcc\x8c~\xc1\xc6\x13\x84vm\xad|\xb7\xad\x8e\xc0\xfft\xa8e&\xd4\t]\xcb\xd3;8Q\x96\xf4F#+\xc8\x82\x931\x84M?i\xd3\xfe\xc6\x81\xf6\xc7\xdfm\x7f\xd8\xb52\xecX\xc7\x13\x19]\xad9\xfd\xd2J\xc3\x1c^\x17\xb0\x99\xa5u\xc1nd\xecf\xc6h\xb0\x99\x98@\x0cL\xb4\xd1\xce\xf06\xcaH2v\xce\xa6]\x7f\x96\xbd\x1b\x8f\xb0k\x9c\xd8m\xafL\xf8\x84\x02\x9fO\x97\xef\t^<\xbc3/\xa9\x8eY\x87\xc8qI\xc1\x9f\x15Ks\x8bB\xf0\xe7\x84%]\xd6\xed_\xe0\x0c\xcbO\xd8\xfc\x9a\xc1\xdf\x83\xf7\xcb\xa2_\x96\xfa\xf9\xfcn\xaf\x90+\xdf3\xe6\xd4\xb1\xab\x10\xf9>\xc1\xe7\xf3\xb8\xbc\xc1\x80\x84\xbd\xd7\x0f\x9b\x99\x18dz\xdc\xf9.qX\xbe\xdb\x9d7\xd8Ct\t\x08]Tsk\x10i9\xa6~6SN?[\x865+3%#x\xb9\x80H\x0e\xfd\xa9cW\x0c\xb6Y\x14<\xda\xc5 \xd0\xd6@r\xbb\xdbT\xadr\xa7O\x16\x07{]\xfea\xa5\xd7\xff\x03\xff\xd3\x1e\xfaS\xc7\xad@\x08\x83%\x97\xff\x9e}B]\xde\xac\x1a\xca\x9b\xa5\xa3+,Y\x86\xe5\x06k\xf2\xdbS\xfe\xdeL\xa4\xed\xad\xb6\xf8\xfa\xb2i\x7f=\xa6E]Bf*\xacKj\xda\x1a\xad.\xe12q\xea\x12\xeew\xc5\xaaK\xd8\xb1\x8eQ\x97\xb0\x99\x88uIM\x7fF\xabK\xd8m\x8fQ\x97\xb0\x99\n\xeb\x126_A]\xc2f\xc7\xa8K\xd8L\xa0.as\x15\xd4%lv\x94\xba\xa4"\x061\xeb\x92\x8a\xdc\x19\xb3.as\xa3\xd4%\xec\xbc\xa9\xb0.\xed\xcb\xee\xbd\xf9\xcc\xb7\x9d3\xd7\xbf>iU\x8bV\xd5\xc7*\xbdG V]Bf*\xacKj\xda\x1a\xad.\xe12q\xea\x12\xeew\xc5\xaaK\xd8\xb1\x8eQ\x97\xb0\x99\x88uIM\x7fF\xabK\xd8m\x8fQ\x97\xb0\x99\n\xeb\x126_A]\xc2f\xc7\xa8K\xd8L\xa0.as\x15\xd4%lv\x94\xba\xa4"\x061\xeb\x92\x8a\xdc\x19\xb3.as\xa3\xd4%\xec\xbc\xa9\xb0.\r\xed\xce\xd4dj\x1e]w\x85\x88kb\xcb=vS\x8b\xf3%d\xa6\xc2\xba\xa4\xa6\xad\xd1\xea\x12.\x13\xa7.\xe1~W\xac\xba\x84\x1d\xeb\x18u\t\x9b\x89X\x97\xd4\xf4g\xb4\xba\x84\xdd\xf6\x18u\t\x9b\xa9\xb0.a\xf3\x15\xd4%lv\x8c\xba\x84\xcd\x04\xea\x126WA]\xc2fG\xa9K*b\x10\xb3.\xa9\xc8\x9d1\xeb\x1267J]\xc2\xce\x9b\xd1\xebR\xf8\xb5\xdf\xc7\x1bT\xa1zT\xcf\x98t\xe2\xcbM\xeev%\x9f+~\xc7\x1ePC\x90\xb9\nj\x08.\x13\xa7\x86\xe0~W\xac\x1a\x82\x1d\xeb\x185\x04\x9b\x89XC\xd4\xf4g\xb4\x1a\x82\xdd\xf6\x185\x04\x9b\xa9\xb0\x86`\xf3\x15\xd4\x10lv\x8c\x1a\x82\xcd\x04j\x086WA\r\xc1fG\xa9!*b\x10\xb3\x86\xa8\xc8\x9d1k\x0867J\r\xc1\xce\x9b\x0fk\xc8\xc3\x1a\xf2\xb0\x86<\xac!\x0fk\xc8\xc3\x1a\xa2}\r\t\x9f\xa3\xe8q\xefy\xce\xf8uZ\xe4z\\&\xd6\x9c\x13\xe6w\xc5\x9csBe*\x99s\xc2e\xa2\xce9\xa9\xe8\xcf\xa8sN\xb8m\x8f5\xe7\x84\xcbT:\xe7\x84\xcbW2\xe7\x84\xcb\x8e5\xe7\x84\xcb\x84\xe6\x9cp\xb9J\xe6\x9cp\xd9\xd1\xe6\x9c\xf0c\x10{\xce\t\x97\x0b\xcd9\xe1r\xa3\xcd9\xe1\xe6M\xbc\\\xaf\xf898\x84\\\xaf\xfc\xd9:\xf5\xb9^\xf1w!\xe4z\xe5\xcf\xee)\xcf\xf5\xca\x99\xear=R\x7f*\xcc\xf5\xca\xdb\xae<\xd7+g\xe2\xe5z\xe5|\xf4\\\xaf\x9c\xad<\xd7+g\xa2\xe5z\xe5\\\xf4\\\xaf\x9c\xad,\xd7\xa3\xc4\x00%\xd7+\xe7\xa2\xe5z\xe5\\e\xb9^y\xde\x8c\x9e\xeb\xc3\xf22k\xee\xf2\x9f\n\xa9\xc7k\xac\x0b\xec\xe4\x99\x07N\x7fR\xee\x7f\x98\x97q\xbf+V^Ff*\xc8\xcb\xd8L\xc4\xbc\x8c\x1b\x8fXy\x19\xbb\xed1\xf226Sa^\xc6\xe6+\xc8\xcb\xd8\xec\x18y\x19\x9b\t\xe4el\xae\x82\xbc\x8c\xcd\x8e\x92\x97U\xc4 f^\xc6\xe6\x02y\x19\x9b\x1b%/c\xe7\xcd\xe8y9,\x07a\xbf\xf7\x0f1\x07!\x7f\x8f\x82\x1c\x84\xdd\xf6\x189\x08\x9b\xa90\x07a\xf3\x15\xe4 lv\x8c\x1c\x84\xcd\x04r\x106WA\x0e\xc2fG\xc9A*b\x103\x07\xa9xWh\xcc\x1c\x84\xcd\x8d\x92\x83p\xf7\xdd\x189(\xec\xd9\xe9\x84\xb8j\xf5\xe9\x06\xad-\x1b\x7f\x9b\xf4\xeb\xc5\xf2\xe4<\xd4g\xa7%I\xe4%\xd6!\x1b\x05J`E#GG\xbc\x07\xa2l\xc1\xa0\xb9o\x8c\x7f\xdf\xbakR\xcb\x9eM\xbfm\x80\xfc>\x99\x07\xf0\xc3r\xdb\xecg\xaf\x16\xdd:\xfa_\xeb\xf2\xd6\xd2\xac\xcaD\xcdx\x94\xdcV5\xca\xbb1\x91\x99\x11m\x96%#/Q\xa2$8I\x96\xd1\xb32\x15\xf1<\xb9\xd0\xdc\xd6yj1\x97:e\xef\xc8\x8e3\xd6\xa4\xc0\xbf\x9f\xac\xa0\xcd\xc8L\xc46/\xa8@\xaf_\xbd\xfc\xb2}9{\xe6\x16\xbd\xe0\xdc(-\xda\x8c\xccDl\xf3\x8dS\x85U\x9av~/sB\x93\x99O5:,\x1f\xd7\xa2\xcd\xc8L\xc46\x87\xdfk6~\x9d\x16mFf\xc2m\x0e\xe3\xb7\xfd#\xfd\xeb\xa4\x83m3^.[b\xb8\xf5\'\xd9\n\xf5wm)\xd9)\xc9\xb2\x835\xb0N\xbdS\x08\xec\xee\x11\xef\x8cl\xb3t\xc2\xb6\x92\x96s2F\x15e\x9c-\x18f*An?\xcd\xf1\xa4\xdeH\x8a\x82$Q\x1c)\x91dx\xccG\xed\x1a\xceY\xe7^c\xdf\xa4\xe6^\xe5\x89\xc4\xb2e\x10b\x9e\x10%\xe6\xcdGT\x96\xb7\x7fOw\xfb\xc8\xb2\xb0\xe1\x7f\xbf\xa9]\x03\xb9\xcdFV\xe4\r$ep\x90<\xcb;E\'\x1f~\xfc\x85\xcd\xafLt\xf7\x14\xba\xf2\xe4\xec\xd2\xe3\xaf\xb0c\x18lf\x15"[\xf0\x15\x08\x85i\xa5\xc70a\xef[CfF\xbeo-;\xb3[W[6\x11\xed}kg\xab\xe6M\xad\xf2~n\xe2\x96:\xc4\x99\xb8\n\x0e\xe4\xf7c\n\x06\x07\xc7J\x8c\xe0\xe0i\x89b\xf4F\x86\x0f?F\xc7\xe6\x97\x1e\xa3\xf3\x8c\x85\xb4r6\xd2`\xb0\x18\x18\xbb\xc5j\xb5\xd8lV=\xa3\xa7h\xde\xac\xedv\xe8\x8d\x9cAtr\x0e\xdeh\xe0i\x83\xc3@S\xffo\xf1\x1b\x15\x1d\x9d\xfb\xd2\x85\x0fL+\xf6\'\xbc\xdd\xfd\xcb\xea\x07P\xf9\x9c(\xb1\xa4\xa4\xd7;\x04Jd\r\x82DF\xbc\x97\xee\xca\xac\x96\x9f\xfc\xba\xe1\xa0y\xe5\xe6MV\xe7\x89\xce[\xb5\xe6\xf7\xcf\x99\xf2\x1f\xef\x98\x0e\xc9o\xcd~\xcc\xfeJ\xbb\xcb\xc89\x18\xe2\x7f\xd07~\xd6\xf7\xd4\xc6\x8c\x15\xe7\xca5\x9e\xb3E@\xfe\x8d\x0b\x88\xff\xd4\x85\x89\xb7\x8e\xaei\x9d\xba\xe6p\xfaOM\x9f\xde\x9b\xae5\xbf\xde\xbeEi\x8bO\xecH\x9dzy\xca\xa0a\x9b\xcb\x8c\xd6\x9a\x7fk\xc9\xd5_\x8e\xdd\x9cf\x9e\x99\xeey\xb3\xb0\xbao\x172_\xef\xe0\r2\'\tFI\xa6h\x92\x95\x9c\xe15DW\xa7\xf8\xb9v\xbf\x7f\\c\xed\'\xc4\xfe\xccw\x9b\x9eB\xa9\xdb\xb5\xee\xd6\x90\xb0\xdc\x83\xcc\x0c\xcb=\x94\x99\xa5H\x93\x853\x99\x8d&=i\xe3\xcc\xb4\xcdn\xd4\xf3\x16\x8e7\xe9\xc3\xdb>\xf7\xe3\xbf\xf3j.K\xe4\xe6\xd2\xf6w\xff\xf4\xe7$i\xd1vd&f\xdb_;\xb0.+\xbd\xc7\x96\x8e\x8b\x9c\x973O\xde\\ZT\x1e\xa1\xedu\xef\xb6=\x8c\xd9\xaf\xcd\x88\xf1\xbf\xcc(\xb2,\\x\xeb\x89!)}Z\xa0\xc4\xa3e\x94x 3\xc3\xe2\xc1\xf2f+e\xb5\x18\xacV\xdef\xe3Y;\xcf\xb0\xb4\x91\xe5X\xbdIO\x87\x8f\xf3\x9a\x87\xd2\xf7=\xfdm\xb9\x94W\xf7\xce\xa8\xf3\xce\x9eO\xe7\xa0\x8es\xd6H2\x12)\x894\xcd\x18YI\x10\xe4\x88\xdf\x94\xc2\xe6\x97n\x87\xc1h0q\x94\x8d\xa5-\xa4\xcd`\xe7\xcd\x81\xe3I\xda\xac7\x98)\x13e\x0f\xdf\x0enin\xc5\'\xaa\xad\xed\xb4\xd3\xf5V\xbdN\t\xfc\x11\xe4wz\x91T`\x1f%9\xbd s\xb2\x18\xf8W\xe9\xab\xbb\x89\xff\x03\x056\xf1\x00') conf.max_list_count = 500 -pkt = ept_lookup_Response(data) +pkt = ept_lookup_Response(data, ndr64=False) towers = [protocol_tower_t(x.valueof("tower").tower_octet_string) for x in pkt.valueof("entries")] assert len(towers) == 430 @@ -507,7 +507,7 @@ PID: 960 - 18/09/2023 12:33:50.167234 (1695040430) | Status: 1825 | DetectionLocation: OSF_SCALL__DoSecurityCallbackAndAccessCheck | Flags 0 - | Params: [('eeptiLongVal', 2583494340)] + | Params: [('eeptiLongVal', -1711472956)] PID: 960 - 18/09/2023 12:33:50.151428 (1695040430) | Generating Component: Security Provider | Status: STATUS_SUCCESS diff --git a/test/scapy/layers/kerberos.uts b/test/scapy/layers/kerberos.uts index fc96f8ccd8c..eb94aaef16b 100644 --- a/test/scapy/layers/kerberos.uts +++ b/test/scapy/layers/kerberos.uts @@ -215,7 +215,7 @@ assert [type(x) for x in pkt.Payloads] == [ UPN_DNS_INFO, NDRSerialization1Header, PAC_ATTRIBUTES_INFO, - PAC_REQUESTOR, + PAC_REQUESTOR_SID, PAC_SIGNATURE_DATA, PAC_SIGNATURE_DATA, ] @@ -226,10 +226,10 @@ assert pkt.Payloads[2].DnsDomainName == 'DOM1.LOCAL' assert pkt.Payloads[2].SamName == 'SRV$' assert pkt.Payloads[2].Sid.summary() == 'S-1-5-21-826288890-480667314-1550869521-1104' -assert pkt.Payloads[3].value.Claims.ClaimsSet.value.value[0].value.ClaimsArrays.value.value[0].usClaimsSourceType == 1 -claimentry = pkt.Payloads[3].value.Claims.ClaimsSet.value.value[0].value.ClaimsArrays.value.value[0].ClaimEntries.value.value[0] -assert claimentry.Id.value.value[0].value == b'ad://ext/AuthenticationSilo' -assert claimentry.Values.value.StringValues.value.value[0].value.value[0].value == b'T0-silo' +assert pkt.Payloads[3].valueof("Claims.ClaimsSet.ClaimsArrays")[0].usClaimsSourceType == 1 +claimentry = pkt.Payloads[3].valueof("Claims.ClaimsSet.ClaimsArrays")[0].valueof("ClaimEntries")[0] +assert claimentry.valueof("Id") == b'ad://ext/AuthenticationSilo' +assert claimentry.valueof("Values.StringValues")[0] == b"T0-silo" assert pkt.Payloads[4].Flags[0].PAC_WAS_REQUESTED @@ -252,9 +252,9 @@ pkt = AuthorizationData(data) assert isinstance(pkt.seq[0].adData.Payloads[0], NDRSerialization1Header) k = pkt.seq[0].adData.Payloads[0].value assert isinstance(k, KERB_VALIDATION_INFO) -assert k.EffectiveName.Buffer.value.value[0].value == b'lzhu' -assert k.LogonDomainName.Buffer.value.value[0].value == b"NTDEV" -assert "S%s" % "-".join(str(x) for x in k.LogonDomainId.value.SubAuthority) == 'S21-397955417-626881126-188441444' +assert k.valueof("EffectiveName.Buffer") == b'lzhu' +assert k.valueof("LogonDomainName.Buffer") == b"NTDEV" +assert "S-1-5-%s" % "-".join(str(x) for x in k.LogonDomainId.value.SubAuthority) == 'S-1-5-21-397955417-626881126-188441444' assert len(k.ExtraSids.value.value) == 13 assert [x.RelativeId for x in k.GroupIds.value.value] == [3392609, 2999049, 3322974, 513, 2931095, 3338539, 3354830, 3026599, 3338538, 2931096, 3392610, 3342740, 3392630, 3014318, 2937394, 3278870, 3038018, 3322975, 3513546, 2966661, 3338434, 3271401, 3051245, 3271606, 3026603, 3018354] @@ -840,6 +840,7 @@ claimSet = CLAIMS_SET( usReservedType=0, ulReservedFieldSize=0, ReservedField=None, + ndr64=False, ) = MSPAC - Check that Pointers, Arrays, etc. were inferred diff --git a/test/scapy/layers/msnrpc.uts b/test/scapy/layers/msnrpc.uts index 16e1a842b81..7b31bf85421 100644 --- a/test/scapy/layers/msnrpc.uts +++ b/test/scapy/layers/msnrpc.uts @@ -130,7 +130,8 @@ pkt = ept_map_Request( referent_id=2, value=twr_p_t(tower_octet_string=b'\x05\x00\x13\x00\rxV4\x124\x12\xcd\xab\xef\x00\x01#Eg\xcf\xfb\x01\x00\x02\x00\x00\x00\x13\x00\r\x04]\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00+\x10H`\x02\x00\x02\x00\x00\x00\x01\x00\x0b\x02\x00\x00\x00\x01\x00\x07\x02\x00\x00\x87\x01\x00\t\x04\x00\x00\x00\x00\x00') ), - max_towers=4 + max_towers=4, + ndr64=False, ) output = bytearray(bytes(pkt)) @@ -166,6 +167,7 @@ pkt = ept_map_Response( twr_p_t(tower_octet_string=b'\x05\x00\x13\x00\rxV4\x124\x12\xcd\xab\xef\x00\x01#Eg\xcf\xfb\x01\x00\x02\x00\x00\x00\x13\x00\r\x04]\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00+\x10H`\x02\x00\x02\x00\x00\x00\x01\x00\x0b\x02\x00\x00\x00\x01\x00\x07\x02\x00\xc2\x0c\x01\x00\t\x04\x00\xc0\xa8z\x11'), twr_p_t(tower_octet_string=b'\x05\x00\x13\x00\rxV4\x124\x12\xcd\xab\xef\x00\x01#Eg\xcf\xfb\x01\x00\x02\x00\x00\x00\x13\x00\r\x04]\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00+\x10H`\x02\x00\x02\x00\x00\x00\x01\x00\x0b\x02\x00\x00\x00\x01\x00\x07\x02\x00\xc2\x03\x01\x00\t\x04\x00\xc0\xa8z\x11') ], + ndr64=False, ) pkt.ITowers.value[0].value[0].referent_id = 0x3 @@ -186,6 +188,7 @@ pkt = NetrServerReqChallenge_Request( ComputerName=b'WIN1', ClientChallenge=PNETLOGON_CREDENTIAL(data=b'12345678'), PrimaryName=None, + ndr64=False, ) assert bytes(pkt) == bytes(chall_req) @@ -199,7 +202,8 @@ assert chall_resp.status == 0 = [EXCH] - Re-build NetrServerReqChallenge_Response from scratch pkt = NetrServerReqChallenge_Response( - ServerChallenge=PNETLOGON_CREDENTIAL(data=b'Zq/\xc4D\xfeRI') + ServerChallenge=PNETLOGON_CREDENTIAL(data=b'Zq/\xc4D\xfeRI'), + ndr64=False, ) assert bytes(pkt) == bytes(chall_resp) @@ -223,6 +227,7 @@ pkt = NetrServerAuthenticate3_Request( PrimaryName=None, SecureChannelType="WorkstationSecureChannel", NegotiateFlags=1611661311, + ndr64=False, ) output = bytearray(bytes(pkt)) @@ -242,7 +247,8 @@ pkt = NetrServerAuthenticate3_Response( ServerCredential=PNETLOGON_CREDENTIAL(data=b'1h\x8d\xb8\xf4zH\xaf'), NegotiateFlags=1611661311, AccountRid=1105, - status=0 + status=0, + ndr64=False, ) assert bytes(pkt) == bytes(auth_resp)