Skip to content

Commit

Permalink
Add some [MS-DRSR] parts (#4572)
Browse files Browse the repository at this point in the history
  • Loading branch information
gpotter2 authored Oct 21, 2024
1 parent 28287eb commit a9eed2d
Show file tree
Hide file tree
Showing 5 changed files with 380 additions and 0 deletions.
1 change: 1 addition & 0 deletions scapy/layers/msrpce/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
# Low-level RPC definitions
"msrpce.raw.ept",
"msrpce.raw.ms_dcom",
"msrpce.raw.ms_drsr",
"msrpce.raw.ms_nrpc",
"msrpce.raw.ms_samr",
"msrpce.raw.ms_srvs",
Expand Down
83 changes: 83 additions & 0 deletions scapy/layers/msrpce/msdrsr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# This file is part of Scapy
# See https://scapy.net/ for more information
# Copyright (C) Gabriel Potter

"""
[MS-DRSR] Directory Replication Service (DRS) Remote Protocol
"""

import uuid
from scapy.packet import Packet
from scapy.fields import LEIntField, FlagsField, UUIDField, UTCTimeField

from scapy.layers.msrpce.raw.ms_drsr import UUID
from scapy.layers.msrpce.raw.ms_drsr import * # noqa: F403,F401

# [MS-DRSR] sect 5.39 DRS_EXTENSIONS_INT


class DRS_EXTENSIONS_INT(Packet):
fields_desc = [
FlagsField(
"dwFlags",
0,
-32,
{
0x00000001: "BASE",
0x00000002: "ASYNCREPL",
0x00000004: "REMOVEAPI",
0x00000008: "MOVEREQ_V2",
0x00000010: "GETCHG_DEFLATE",
0x00000020: "DCINFO_V1",
0x00000040: "RESTORE_USN_OPTIMIZATION",
0x00000080: "ADDENTRY",
0x00000100: "KCC_EXECUTE",
0x00000200: "ADDENTRY_V2",
0x00000400: "LINKED_VALUE_REPLICATION",
0x00000800: "DCINFO_V2",
0x00001000: "INSTANCE_TYPE_NOT_REQ_ON_MOD",
0x00002000: "CRYPTO_BIND",
0x00004000: "GET_REPL_INFO",
0x00008000: "STRONG_ENCRYPTION",
0x00010000: "DCINFO_VFFFFFFFF",
0x00020000: "TRANSITIVE_MEMBERSHIP",
0x00040000: "ADD_SID_HISTORY",
0x00080000: "POST_BETA3",
0x00100000: "GETCHGREQ_V5",
0x00200000: "GETMEMBERSHIPS2",
0x00400000: "GETCHGREQ_V6",
0x00800000: "NONDOMAIN_NCS",
0x01000000: "GETCHGREQ_V8",
0x02000000: "GETCHGREPLY_V5",
0x04000000: "GETCHGREPLY_V6",
0x08000000: "WHISTLER_BETA3",
0x10000000: "W2K3_DEFLATE",
0x20000000: "GETCHGREQ_V10",
0x40000000: "R2",
0x80000000: "R3",
},
),
UUIDField("SiteObjGuid", None, uuid_fmt=UUIDField.FORMAT_LE),
LEIntField("Pid", 0),
UTCTimeField("dwReplEpoch", None, fmt="<I"),
FlagsField(
"dwFlagsExt",
0,
-32,
{
0x00000001: "ADAM",
0x00000002: "LH_BETA2",
0x00000004: "RECYCLE_BIN",
0x00000100: "GETCHGREPLY_V9",
0x00000400: "RPC_CORRELATIONID_1",
},
),
UUIDField("ConfigObjGuid", None, uuid_fmt=UUIDField.FORMAT_LE),
LEIntField("dwExtCaps", 0),
]


# [MS-DRSR] sect 5.138 NTDSAPI_CLIENT_GUID

NTDSAPI_CLIENT_GUID = UUID(uuid.UUID("{e24d201a-4fd6-11d1-a3da-0000f875ae0d}").bytes_le)
209 changes: 209 additions & 0 deletions scapy/layers/msrpce/raw/ms_drsr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
# SPDX-License-Identifier: GPL-2.0-only
# This file is part of Scapy
# See https://scapy.net/ for more information
# Copyright (C) Gabriel Potter

"""
Very partial RPC definitions for the following interfaces:
- drsuapi (v4.0): e3514235-4b06-11d1-ab04-00c04fc2dcd2
"""

from enum import IntEnum
import uuid

from scapy.fields import StrFixedLenField
from scapy.layers.dcerpc import (
NDRPacket,
DceRpcOp,
NDRByteField,
NDRConfFieldListField,
NDRConfPacketListField,
NDRConfStrLenField,
NDRConfStrLenFieldUtf16,
NDRConfVarFieldListField,
NDRConfVarStrNullField,
NDRConfVarStrNullFieldUtf16,
NDRContextHandle,
NDRFullPointerField,
NDRInt3264EnumField,
NDRIntField,
NDRLongField,
NDRPacketField,
NDRRecursiveField,
NDRRefEmbPointerField,
NDRShortField,
NDRSignedByteField,
NDRSignedIntField,
NDRSignedLongField,
NDRUnionField,
NDRVarStrLenField,
NDRVarStrLenFieldUtf16,
register_dcerpc_interface,
)


class UUID(NDRPacket):
ALIGNMENT = (4, 4)
fields_desc = [
NDRIntField("Data1", 0),
NDRShortField("Data2", 0),
NDRShortField("Data3", 0),
StrFixedLenField("Data4", "", length=8),
]


class DRS_EXTENSIONS(NDRPacket):
ALIGNMENT = (4, 8)
DEPORTED_CONFORMANTS = ["rgb"]
fields_desc = [
NDRIntField("cb", None, size_of="rgb"),
NDRConfStrLenField(
"rgb", "", size_is=lambda pkt: pkt.cb, conformant_in_struct=True
),
]


class IDL_DRSBind_Request(NDRPacket):
fields_desc = [
NDRFullPointerField(NDRPacketField("puuidClientDsa", UUID(), UUID)),
NDRFullPointerField(
NDRPacketField("pextClient", DRS_EXTENSIONS(), DRS_EXTENSIONS)
),
]


class IDL_DRSBind_Response(NDRPacket):
fields_desc = [
NDRFullPointerField(
NDRPacketField("ppextServer", DRS_EXTENSIONS(), DRS_EXTENSIONS)
),
NDRPacketField("phDrs", NDRContextHandle(), NDRContextHandle),
NDRIntField("status", 0),
]


class IDL_DRSUnbind_Request(NDRPacket):
fields_desc = [NDRPacketField("phDrs", NDRContextHandle(), NDRContextHandle)]


class IDL_DRSUnbind_Response(NDRPacket):
fields_desc = [
NDRPacketField("phDrs", NDRContextHandle(), NDRContextHandle),
NDRIntField("status", 0),
]


class DRS_MSG_CRACKREQ_V1(NDRPacket):
ALIGNMENT = (4, 8)
fields_desc = [
NDRIntField("CodePage", 0),
NDRIntField("LocaleId", 0),
NDRIntField("dwFlags", 0),
NDRIntField("formatOffered", 0),
NDRIntField("formatDesired", 0),
NDRIntField("cNames", None, size_of="rpNames"),
NDRFullPointerField(
NDRConfFieldListField(
"rpNames",
[],
NDRFullPointerField(
NDRConfVarStrNullFieldUtf16("rpNames", ""), deferred=True
),
size_is=lambda pkt: pkt.cNames,
),
deferred=True,
),
]


class PDS_NAME_RESULT_ITEMW(NDRPacket):
ALIGNMENT = (4, 8)
fields_desc = [
NDRIntField("status", 0),
NDRFullPointerField(NDRConfVarStrNullFieldUtf16("pDomain", ""), deferred=True),
NDRFullPointerField(NDRConfVarStrNullFieldUtf16("pName", ""), deferred=True),
]


class DS_NAME_RESULTW(NDRPacket):
ALIGNMENT = (4, 8)
fields_desc = [
NDRIntField("cItems", None, size_of="rItems"),
NDRFullPointerField(
NDRConfPacketListField(
"rItems",
[PDS_NAME_RESULT_ITEMW()],
PDS_NAME_RESULT_ITEMW,
size_is=lambda pkt: pkt.cItems,
),
deferred=True,
),
]


class DRS_MSG_CRACKREPLY_V1(NDRPacket):
ALIGNMENT = (4, 8)
fields_desc = [
NDRFullPointerField(
NDRPacketField("pResult", DS_NAME_RESULTW(), DS_NAME_RESULTW), deferred=True
)
]


class IDL_DRSCrackNames_Request(NDRPacket):
fields_desc = [
NDRPacketField("hDrs", NDRContextHandle(), NDRContextHandle),
NDRIntField("dwInVersion", 0),
NDRUnionField(
[
(
NDRPacketField(
"pmsgIn", DRS_MSG_CRACKREQ_V1(), DRS_MSG_CRACKREQ_V1
),
(
(lambda pkt: getattr(pkt, "dwInVersion", None) == 1),
(lambda _, val: val.tag == 1),
),
)
],
StrFixedLenField("pmsgIn", "", length=0),
align=(4, 8),
switch_fmt=("L", "L"),
),
]


class IDL_DRSCrackNames_Response(NDRPacket):
fields_desc = [
NDRIntField("pdwOutVersion", 0),
NDRUnionField(
[
(
NDRPacketField(
"pmsgOut", DRS_MSG_CRACKREPLY_V1(), DRS_MSG_CRACKREPLY_V1
),
(
(lambda pkt: getattr(pkt, "pdwOutVersion", None) == 1),
(lambda _, val: val.tag == 1),
),
)
],
StrFixedLenField("pmsgOut", "", length=0),
align=(4, 8),
switch_fmt=("L", "L"),
),
NDRIntField("status", 0),
]


DRSUAPI_OPNUMS = {
0: DceRpcOp(IDL_DRSBind_Request, IDL_DRSBind_Response),
1: DceRpcOp(IDL_DRSUnbind_Request, IDL_DRSUnbind_Response),
12: DceRpcOp(IDL_DRSCrackNames_Request, IDL_DRSCrackNames_Response),
}
register_dcerpc_interface(
name="drsuapi",
uuid=uuid.UUID("e3514235-4b06-11d1-ab04-00c04fc2dcd2"),
version="4.0",
opnums=DRSUAPI_OPNUMS,
)
Binary file added test/pcaps/dcerpc_msdrsr_cracknames.pcapng.gz
Binary file not shown.
87 changes: 87 additions & 0 deletions test/scapy/layers/msdrsr.uts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
% MS-DRSR tests

+ [MS-DRSR] test vectors

+ Dissect DRSR Crack_Names exchange

= [EXCH] - Load MSDRSR exchange and decrypt (SPNEGOSSP/NTLMSSP)

load_layer("msrpce")
bind_layers(TCP, DceRpc5, sport=49685) # the DCE/RPC port
bind_layers(TCP, DceRpc5, dport=49685)

conf.dcerpc_session_enable = True
conf.winssps_passive = [
SPNEGOSSP(
[
NTLMSSP(
IDENTITIES={
"Administrator": MD4le("Password123!"),
},
)
]
)
]
pkts = sniff(offline=scapy_path('test/pcaps/dcerpc_msdrsr_cracknames.pcapng.gz'), session=TCPSession)
conf.dcerpc_session_enable = False

= [EXCH] - Check IDL_DRSBind_Request

from scapy.layers.msrpce.msdrsr import DRS_EXTENSIONS_INT

bindreq = pkts[7]
assert IDL_DRSBind_Request in bindreq
ext = DRS_EXTENSIONS_INT(bindreq[IDL_DRSBind_Request].valueof("pextClient").rgb)
assert ext.Pid == 1234
assert ext.dwReplEpoch == 1729468809

= [EXCH] - Check IDL_DRSBind_Response

import uuid

bindresp = pkts[8]
assert IDL_DRSBind_Response in bindresp
assert bindresp[IDL_DRSBind_Response].phDrs.uuid == b'\xf4$I\xf5\xde\x0c\xfcO\x8b\xfa\xb0Y\x87\xf4\x11i'
ext = DRS_EXTENSIONS_INT(bindresp[IDL_DRSBind_Response].valueof("ppextServer").rgb)
assert ext.dwFlags.GETCHGREQ_V10
assert ext.dwFlags == 0x3fffff7f
assert ext.Pid == 696
assert ext.ConfigObjGuid == uuid.UUID('14ea64e0-3470-48e6-9ace-77012d8d474f')

= [EXCH] - Check IDL_DRSCrackNames_Request

cnreq = pkts[9]
assert IDL_DRSCrackNames_Request in cnreq

crackreq = cnreq[IDL_DRSCrackNames_Request].valueof("pmsgIn")
assert crackreq.formatOffered == 11
assert crackreq.formatDesired == 0xfffffff2

assert crackreq.valueof("rpNames") == [
b'S-1-5-21-1924137214-3718646274-40215721-522',
b'S-1-5-21-1924137214-3718646274-40215721-498',
b'S-1-5-21-1924137214-3718646274-40215721-516',
b'S-1-5-21-1924137214-3718646274-40215721-526',
b'S-1-5-21-1924137214-3718646274-40215721-527',
b'S-1-5-21-1924137214-3718646274-40215721-512',
b'S-1-5-21-1924137214-3718646274-40215721-519',
b'S-1-5-21-1924137214-3718646274-40215721-513',
]

= [EXCH] - Check IDL_DRSCrackNames_Response

cnresp = pkts[10]
assert IDL_DRSCrackNames_Response in cnresp

crackresp = cnresp[IDL_DRSCrackNames_Response].valueof("pmsgOut")
assert [x.valueof("pName") for x in crackresp.valueof("pResult").valueof("rItems")] == [
b'Cloneable Domain Controllers@DOMAIN',
b'Enterprise Read-only Domain Controllers@DOMAIN',
b'Domain Controllers@DOMAIN',
b'Key Admins@DOMAIN',
b'Enterprise Key Admins@DOMAIN',
b'Domain Admins@DOMAIN',
b'Enterprise Admins@DOMAIN',
b'Domain Users@DOMAIN',
]

0 comments on commit a9eed2d

Please sign in to comment.