|
| 1 | +[MS-DCOM] |
| 2 | +========= |
| 3 | + |
| 4 | +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. |
| 5 | +Before reading this, have a look at Scapy's `DCE/RPC <dcerpc.html>`_ documentation page. |
| 6 | + |
| 7 | +Terminology |
| 8 | +----------- |
| 9 | + |
| 10 | +- In DCOM one instantiates 'classes' to get 'object references'. A class implements one or several 'interfaces', each of which has methods. |
| 11 | +- ``CLSID``: the UIID of a **class**, used to instantiate it. This is typically chosen by whoever implements the COM object. |
| 12 | +- ``IID``: the UIID of an **interface**, used to request an IPID. This is chosen by whoever defines the COM interface (mostly Microsoft). |
| 13 | +- ``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. |
| 14 | + |
| 15 | +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. |
| 16 | + |
| 17 | +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. |
| 18 | +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. |
| 19 | + |
| 20 | +General behavior of a DCOM client |
| 21 | +--------------------------------- |
| 22 | + |
| 23 | +1. Setup the DCOM client (endpoint, SSP, etc.) |
| 24 | +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``). |
| 25 | +3. Acquire the IPID of an interface of the object. |
| 26 | +4. Call a method of that interface. |
| 27 | +5. Release the reference counts on the interface (delete the IPID). |
| 28 | + |
| 29 | +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. |
| 30 | + |
| 31 | +Using the DCOM client |
| 32 | +--------------------- |
| 33 | + |
| 34 | +General usage |
| 35 | +~~~~~~~~~~~~~ |
| 36 | + |
| 37 | +1. Setup the DCOM client and connect to the object resolver (which is by default on port 135). |
| 38 | + |
| 39 | +.. code:: python |
| 40 | +
|
| 41 | + from scapy.layers.msrpce.msdcom import DCOM_Client |
| 42 | + from scapy.layers.ntlm import NTLMSSP |
| 43 | +
|
| 44 | + client = DCOM_Client( |
| 45 | + ssp=NTLMSSP(UPN="[email protected]", PASSWORD="Scapy1111@"), |
| 46 | + ) |
| 47 | + client.connect("server1.domain.local") |
| 48 | +
|
| 49 | +.. note:: See the examples in `DCE/RPC <dcerpc.html>`_ to connect with SPNEGO/Kerberos. |
| 50 | + |
| 51 | +2. Instantiate a class to get an object reference |
| 52 | + |
| 53 | +.. code:: python |
| 54 | +
|
| 55 | + import uuid |
| 56 | + from scapy.layers.dcerpc import find_com_interface |
| 57 | + import scapy.layers.msrpce.raw.ms_pla |
| 58 | +
|
| 59 | + CLSID_TraceSessionCollection = uuid.UUID("03837530-098b-11d8-9414-505054503030") |
| 60 | + # The COM interface must have been compiled by scapy-rpc (midl-to-scapy) |
| 61 | + IDataCollectorSetCollection = find_com_interface("IDataCollectorSetCollection") |
| 62 | +
|
| 63 | + # Get new object reference |
| 64 | + objref = client.RemoteCreateInstance( |
| 65 | + # The CLSID we're instantiating |
| 66 | + clsid=CLSID_TraceSessionCollection, |
| 67 | + iids=[ |
| 68 | + # An initial list of interfaces to ask for. There must be at least 1. |
| 69 | + IDataCollectorSetCollection, |
| 70 | + ] |
| 71 | + ) |
| 72 | +
|
| 73 | +3. Call a method on that object reference |
| 74 | + |
| 75 | +.. code:: python |
| 76 | +
|
| 77 | + result = objref.sr1_req( |
| 78 | + # The request message (here from [MS-PLA]) |
| 79 | + pkt=GetDataCollectorSets_Request( |
| 80 | + server=None, |
| 81 | + filter=NDRPointer( |
| 82 | + referent_id=0x72657355, |
| 83 | + value=FLAGGED_WORD_BLOB( |
| 84 | + cBytes=18, |
| 85 | + asData=r"session\*".encode("utf-16le"), |
| 86 | + ) |
| 87 | + ), |
| 88 | + ), |
| 89 | + # The interface to send it on |
| 90 | + iface=IDataCollectorSetCollection, |
| 91 | + ) |
| 92 | +
|
| 93 | +4. Release all the requested interfaces on the object reference |
| 94 | + |
| 95 | +.. code:: python |
| 96 | +
|
| 97 | + objref.release() |
| 98 | +
|
| 99 | +5. Close the client |
| 100 | + |
| 101 | +.. code:: python |
| 102 | +
|
| 103 | + client.close() |
| 104 | +
|
| 105 | +
|
| 106 | +Unmarshalling object references |
| 107 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 108 | + |
| 109 | +Some methods return a reference to an object that is created by the remote server. On the network, |
| 110 | +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. |
| 111 | + |
| 112 | +.. code:: python |
| 113 | +
|
| 114 | + # For instance, let's assume we're calling Next() of the IEnumVARIANT |
| 115 | + resp = enum.sr1_req( |
| 116 | + pkt=Next_Request(celt=1), |
| 117 | + iface=IEnumVARIANT, |
| 118 | + ) |
| 119 | +
|
| 120 | + # Get the MInterfacePointer value |
| 121 | + value = resp.valueof("rgVar")[0].valueof("_varUnion") |
| 122 | + assert isinstance(value, MInterfacePointer) |
| 123 | +
|
| 124 | + # Unmarshall it and acquire an initial interface on it. |
| 125 | + objref = client.UnmarshallObjectReference( |
| 126 | + value, |
| 127 | + iid=IDataCollectorSet, |
| 128 | + ) |
0 commit comments