Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/layers/dcerpc.py: 48%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# SPDX-License-Identifier: GPL-2.0-or-later
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Gabriel Potter
6# scapy.contrib.description = DCE/RPC
7# scapy.contrib.status = loads
9"""
10DCE/RPC
11Distributed Computing Environment / Remote Procedure Calls
13Based on [C706] - aka DCE/RPC 1.1
14https://pubs.opengroup.org/onlinepubs/9629399/toc.pdf
16And on [MS-RPCE]
17https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rpce/290c38b1-92fe-4229-91e6-4fc376610c15
19.. note::
20 Please read the documentation over
21 `DCE/RPC <https://scapy.readthedocs.io/en/latest/layers/dcerpc.html>`_
22"""
24import collections
25import importlib
26import inspect
27import struct
29from enum import IntEnum
30from functools import partial
31from uuid import UUID
33from scapy.base_classes import Packet_metaclass
35from scapy.config import conf
36from scapy.compat import bytes_encode, plain_str
37from scapy.error import log_runtime
38from scapy.layers.dns import DNSStrField
39from scapy.layers.ntlm import (
40 NTLM_Header,
41 NTLMSSP_MESSAGE_SIGNATURE,
42)
43from scapy.packet import (
44 Packet,
45 Raw,
46 bind_bottom_up,
47 bind_layers,
48 bind_top_down,
49 NoPayload,
50)
51from scapy.fields import (
52 _FieldContainer,
53 BitEnumField,
54 BitField,
55 ByteEnumField,
56 ByteField,
57 ConditionalField,
58 EnumField,
59 Field,
60 FieldLenField,
61 FieldListField,
62 FlagsField,
63 IntField,
64 LEIntEnumField,
65 LEIntField,
66 LELongField,
67 LEShortEnumField,
68 LEShortField,
69 LenField,
70 MultipleTypeField,
71 PacketField,
72 PacketLenField,
73 PacketListField,
74 PadField,
75 ReversePadField,
76 ShortEnumField,
77 ShortField,
78 SignedByteField,
79 StrField,
80 StrFixedLenField,
81 StrLenField,
82 StrLenFieldUtf16,
83 StrNullField,
84 StrNullFieldUtf16,
85 TrailerField,
86 UUIDEnumField,
87 UUIDField,
88 XByteField,
89 XLEIntField,
90 XLELongField,
91 XLEShortField,
92 XShortField,
93 XStrFixedLenField,
94)
95from scapy.sessions import DefaultSession
96from scapy.supersocket import StreamSocket
98from scapy.layers.kerberos import (
99 KRB_InnerToken,
100 Kerberos,
101)
102from scapy.layers.gssapi import (
103 GSSAPI_BLOB,
104 GSSAPI_BLOB_SIGNATURE,
105 GSS_S_COMPLETE,
106 GSS_S_FLAGS,
107 GSS_C_FLAGS,
108 SSP,
109)
110from scapy.layers.inet import TCP
112from scapy.contrib.rtps.common_types import (
113 EField,
114 EPacket,
115 EPacketField,
116 EPacketListField,
117)
119# Typing imports
120from typing import (
121 Optional,
122 Union,
123)
125# the alignment of auth_pad
126# This is 4 in [C706] 13.2.6.1 but was updated to 16 in [MS-RPCE] 2.2.2.11
127_COMMON_AUTH_PAD = 16
128# the alignment of the NDR Type 1 serialization private header
129# ([MS-RPCE] sect 2.2.6.2)
130_TYPE1_S_PAD = 8
132# DCE/RPC Packet
133DCE_RPC_TYPE = {
134 0: "request",
135 1: "ping",
136 2: "response",
137 3: "fault",
138 4: "working",
139 5: "no_call",
140 6: "reject",
141 7: "acknowledge",
142 8: "connectionless_cancel",
143 9: "frag_ack",
144 10: "cancel_ack",
145 11: "bind",
146 12: "bind_ack",
147 13: "bind_nak",
148 14: "alter_context",
149 15: "alter_context_resp",
150 16: "auth3",
151 17: "shutdown",
152 18: "co_cancel",
153 19: "orphaned",
154}
155_DCE_RPC_4_FLAGS1 = [
156 "reserved_01",
157 "last_frag",
158 "frag",
159 "no_frag_ack",
160 "maybe",
161 "idempotent",
162 "broadcast",
163 "reserved_7",
164]
165_DCE_RPC_4_FLAGS2 = [
166 "reserved_0",
167 "cancel_pending",
168 "reserved_2",
169 "reserved_3",
170 "reserved_4",
171 "reserved_5",
172 "reserved_6",
173 "reserved_7",
174]
175DCE_RPC_TRANSFER_SYNTAXES = {
176 UUID("00000000-0000-0000-0000-000000000000"): "NULL",
177 UUID("6cb71c2c-9812-4540-0300-000000000000"): "Bind Time Feature Negotiation",
178 UUID("8a885d04-1ceb-11c9-9fe8-08002b104860"): "NDR 2.0",
179 UUID("71710533-beba-4937-8319-b5dbef9ccc36"): "NDR64",
180}
181DCE_RPC_INTERFACES_NAMES = {}
182DCE_RPC_INTERFACES_NAMES_rev = {}
183COM_INTERFACES_NAMES = {}
184COM_INTERFACES_NAMES_rev = {}
187class DCERPC_Transport(IntEnum):
188 """
189 Protocols identifiers currently supported by Scapy
190 """
192 NCACN_IP_TCP = 0x07
193 NCACN_NP = 0x0F
194 # TODO: add more.. if people use them?
197# [C706] Appendix I with names from Appendix B
198DCE_RPC_PROTOCOL_IDENTIFIERS = {
199 0x0: "OSI OID", # Special
200 0x0D: "UUID", # Special
201 # Transports
202 # 0x2: "DNA Session Control",
203 # 0x3: "DNA Session Control V3",
204 # 0x4: "DNA NSP Transport",
205 # 0x5: "OSI TP4",
206 0x06: "NCADG_OSI_CLSN", # [C706]
207 0x07: "NCACN_IP_TCP", # [C706]
208 0x08: "NCADG_IP_UDP", # [C706]
209 0x09: "IP", # [C706]
210 0x0A: "RPC connectionless protocol", # [C706]
211 0x0B: "RPC connection-oriented protocol", # [C706]
212 0x0C: "NCALRPC",
213 0x0F: "NCACN_NP", # [MS-RPCE]
214 0x11: "NCACN_NB", # [C706]
215 0x12: "NCACN_NB_NB", # [MS-RPCE]
216 0x13: "NCACN_SPX", # [C706]
217 0x14: "NCADG_IPX", # [C706]
218 0x16: "NCACN_AT_DSP", # [C706]
219 0x17: "NCADG_AT_DSP", # [C706]
220 0x19: "NCADG_NB", # [C706]
221 0x1A: "NCACN_VNS_SPP", # [C706]
222 0x1B: "NCADG_VNS_IPC", # [C706]
223 0x1F: "NCACN_HTTP", # [MS-RPCE]
224}
227def _dce_rpc_endianness(pkt):
228 """
229 Determine the right endianness sign for a given DCE/RPC packet
230 """
231 if pkt.endian == 0: # big endian
232 return ">"
233 elif pkt.endian == 1: # little endian
234 return "<"
235 else:
236 return "!"
239class _EField(EField):
240 def __init__(self, fld):
241 super(_EField, self).__init__(fld, endianness_from=_dce_rpc_endianness)
244class DceRpc(Packet):
245 """DCE/RPC packet"""
247 @classmethod
248 def dispatch_hook(cls, _pkt=None, *args, **kargs):
249 if _pkt and len(_pkt) >= 1:
250 ver = ord(_pkt[0:1])
251 if ver == 4:
252 return DceRpc4
253 elif ver == 5:
254 return DceRpc5
255 return DceRpc5
257 @classmethod
258 def tcp_reassemble(cls, data, metadata, session):
259 if data[0:1] == b"\x05":
260 return DceRpc5.tcp_reassemble(data, metadata, session)
261 return DceRpc(data)
264bind_bottom_up(TCP, DceRpc, sport=135)
265bind_layers(TCP, DceRpc, dport=135)
268class _DceRpcPayload(Packet):
269 @property
270 def endianness(self):
271 if not self.underlayer:
272 return "!"
273 return _dce_rpc_endianness(self.underlayer)
276# sect 12.5
278_drep = [
279 BitEnumField("endian", 1, 4, ["big", "little"]),
280 BitEnumField("encoding", 0, 4, ["ASCII", "EBCDIC"]),
281 ByteEnumField("float", 0, ["IEEE", "VAX", "CRAY", "IBM"]),
282 ByteField("reserved1", 0),
283]
286class DceRpc4(DceRpc):
287 """
288 DCE/RPC v4 'connection-less' packet
289 """
291 name = "DCE/RPC v4"
292 fields_desc = (
293 [
294 ByteEnumField(
295 "rpc_vers", 4, {4: "4 (connection-less)", 5: "5 (connection-oriented)"}
296 ),
297 ByteEnumField("ptype", 0, DCE_RPC_TYPE),
298 FlagsField("flags1", 0, 8, _DCE_RPC_4_FLAGS1),
299 FlagsField("flags2", 0, 8, _DCE_RPC_4_FLAGS2),
300 ]
301 + _drep
302 + [
303 XByteField("serial_hi", 0),
304 _EField(UUIDField("object", None)),
305 _EField(UUIDField("if_id", None)),
306 _EField(UUIDField("act_id", None)),
307 _EField(IntField("server_boot", 0)),
308 _EField(IntField("if_vers", 1)),
309 _EField(IntField("seqnum", 0)),
310 _EField(ShortField("opnum", 0)),
311 _EField(XShortField("ihint", 0xFFFF)),
312 _EField(XShortField("ahint", 0xFFFF)),
313 _EField(LenField("len", None, fmt="H")),
314 _EField(ShortField("fragnum", 0)),
315 ByteEnumField("auth_proto", 0, ["none", "OSF DCE Private Key"]),
316 XByteField("serial_lo", 0),
317 ]
318 )
321# Exceptionally, we define those 3 here.
324class NL_AUTH_MESSAGE(Packet):
325 # [MS-NRPC] sect 2.2.1.3.1
326 name = "NL_AUTH_MESSAGE"
327 fields_desc = [
328 LEIntEnumField(
329 "MessageType",
330 0x00000000,
331 {
332 0x00000000: "Request",
333 0x00000001: "Response",
334 },
335 ),
336 FlagsField(
337 "Flags",
338 0,
339 -32,
340 [
341 "NETBIOS_DOMAIN_NAME",
342 "NETBIOS_COMPUTER_NAME",
343 "DNS_DOMAIN_NAME",
344 "DNS_HOST_NAME",
345 "NETBIOS_COMPUTER_NAME_UTF8",
346 ],
347 ),
348 ConditionalField(
349 StrNullField("NetbiosDomainName", ""),
350 lambda pkt: pkt.Flags.NETBIOS_DOMAIN_NAME,
351 ),
352 ConditionalField(
353 StrNullField("NetbiosComputerName", ""),
354 lambda pkt: pkt.Flags.NETBIOS_COMPUTER_NAME,
355 ),
356 ConditionalField(
357 DNSStrField("DnsDomainName", ""),
358 lambda pkt: pkt.Flags.DNS_DOMAIN_NAME,
359 ),
360 ConditionalField(
361 DNSStrField("DnsHostName", ""),
362 lambda pkt: pkt.Flags.DNS_HOST_NAME,
363 ),
364 ConditionalField(
365 # What the fuck? Why are they doing this
366 # The spec is just wrong
367 DNSStrField("NetbiosComputerNameUtf8", ""),
368 lambda pkt: pkt.Flags.NETBIOS_COMPUTER_NAME_UTF8,
369 ),
370 ]
373class NL_AUTH_SIGNATURE(Packet):
374 # [MS-NRPC] sect 2.2.1.3.2/2.2.1.3.3
375 name = "NL_AUTH_(SHA2_)SIGNATURE"
376 fields_desc = [
377 LEShortEnumField(
378 "SignatureAlgorithm",
379 0x0077,
380 {
381 0x0077: "HMAC-MD5",
382 0x0013: "HMAC-SHA256",
383 },
384 ),
385 LEShortEnumField(
386 "SealAlgorithm",
387 0xFFFF,
388 {
389 0xFFFF: "Unencrypted",
390 0x007A: "RC4",
391 0x001A: "AES-128",
392 },
393 ),
394 XLEShortField("Pad", 0xFFFF),
395 ShortField("Flags", 0),
396 XStrFixedLenField("SequenceNumber", b"", length=8),
397 XStrFixedLenField("Checksum", b"", length=8),
398 ConditionalField(
399 XStrFixedLenField("Confounder", b"", length=8),
400 lambda pkt: pkt.SealAlgorithm != 0xFFFF,
401 ),
402 MultipleTypeField(
403 [
404 (
405 StrFixedLenField("Reserved2", b"", length=24),
406 lambda pkt: pkt.SignatureAlgorithm == 0x0013,
407 ),
408 ],
409 StrField("Reserved2", b""),
410 ),
411 ]
414# [MS-RPCE] sect 2.2.1.1.7
415# https://learn.microsoft.com/en-us/windows/win32/rpc/authentication-service-constants
416# rpcdce.h
419class RPC_C_AUTHN(IntEnum):
420 NONE = 0x00
421 DCE_PRIVATE = 0x01
422 DCE_PUBLIC = 0x02
423 DEC_PUBLIC = 0x04
424 GSS_NEGOTIATE = 0x09
425 WINNT = 0x0A
426 GSS_SCHANNEL = 0x0E
427 GSS_KERBEROS = 0x10
428 DPA = 0x11
429 MSN = 0x12
430 KERNEL = 0x14
431 DIGEST = 0x15
432 NEGO_EXTENDED = 0x1E
433 PKU2U = 0x1F
434 LIVE_SSP = 0x20
435 LIVEXP_SSP = 0x23
436 CLOUD_AP = 0x24
437 NETLOGON = 0x44
438 MSONLINE = 0x52
439 MQ = 0x64
440 DEFAULT = 0xFFFFFFFF
443class RPC_C_AUTHN_LEVEL(IntEnum):
444 DEFAULT = 0x0
445 NONE = 0x1
446 CONNECT = 0x2
447 CALL = 0x3
448 PKT = 0x4
449 PKT_INTEGRITY = 0x5
450 PKT_PRIVACY = 0x6
453DCE_C_AUTHN_LEVEL = RPC_C_AUTHN_LEVEL # C706 name
456class RPC_C_IMP_LEVEL(IntEnum):
457 DEFAULT = 0x0
458 ANONYMOUS = 0x1
459 IDENTIFY = 0x2
460 IMPERSONATE = 0x3
461 DELEGATE = 0x4
464# C706 sect 13.2.6.1
467class CommonAuthVerifier(Packet):
468 name = "Common Authentication Verifier"
469 fields_desc = [
470 ByteEnumField(
471 "auth_type",
472 0,
473 RPC_C_AUTHN,
474 ),
475 ByteEnumField("auth_level", 0, RPC_C_AUTHN_LEVEL),
476 ByteField("auth_pad_length", None),
477 ByteField("auth_reserved", 0),
478 XLEIntField("auth_context_id", 0),
479 MultipleTypeField(
480 [
481 # SPNEGO
482 (
483 PacketLenField(
484 "auth_value",
485 GSSAPI_BLOB(),
486 GSSAPI_BLOB,
487 length_from=lambda pkt: pkt.parent.auth_len,
488 ),
489 lambda pkt: pkt.auth_type == 0x09 and pkt.parent and
490 # Bind/Alter
491 pkt.parent.ptype in [11, 12, 13, 14, 15, 16],
492 ),
493 (
494 PacketLenField(
495 "auth_value",
496 GSSAPI_BLOB_SIGNATURE(),
497 GSSAPI_BLOB_SIGNATURE,
498 length_from=lambda pkt: pkt.parent.auth_len,
499 ),
500 lambda pkt: pkt.auth_type == 0x09
501 and pkt.parent
502 and (
503 # Other
504 not pkt.parent
505 or pkt.parent.ptype not in [11, 12, 13, 14, 15, 16]
506 ),
507 ),
508 # Kerberos
509 (
510 PacketLenField(
511 "auth_value",
512 Kerberos(),
513 Kerberos,
514 length_from=lambda pkt: pkt.parent.auth_len,
515 ),
516 lambda pkt: pkt.auth_type == 0x10 and pkt.parent and
517 # Bind/Alter
518 pkt.parent.ptype in [11, 12, 13, 14, 15, 16],
519 ),
520 (
521 PacketLenField(
522 "auth_value",
523 KRB_InnerToken(),
524 KRB_InnerToken,
525 length_from=lambda pkt: pkt.parent.auth_len,
526 ),
527 lambda pkt: pkt.auth_type == 0x10
528 and pkt.parent
529 and (
530 # Other
531 not pkt.parent
532 or pkt.parent.ptype not in [11, 12, 13, 14, 15, 16]
533 ),
534 ),
535 # NTLM
536 (
537 PacketLenField(
538 "auth_value",
539 NTLM_Header(),
540 NTLM_Header,
541 length_from=lambda pkt: pkt.parent.auth_len,
542 ),
543 lambda pkt: pkt.auth_type in [0x0A, 0xFF] and pkt.parent and
544 # Bind/Alter
545 pkt.parent.ptype in [11, 12, 13, 14, 15, 16],
546 ),
547 (
548 PacketLenField(
549 "auth_value",
550 NTLMSSP_MESSAGE_SIGNATURE(),
551 NTLMSSP_MESSAGE_SIGNATURE,
552 length_from=lambda pkt: pkt.parent.auth_len,
553 ),
554 lambda pkt: pkt.auth_type in [0x0A, 0xFF]
555 and pkt.parent
556 and (
557 # Other
558 not pkt.parent
559 or pkt.parent.ptype not in [11, 12, 13, 14, 15, 16]
560 ),
561 ),
562 # NetLogon
563 (
564 PacketLenField(
565 "auth_value",
566 NL_AUTH_MESSAGE(),
567 NL_AUTH_MESSAGE,
568 length_from=lambda pkt: pkt.parent.auth_len,
569 ),
570 lambda pkt: pkt.auth_type == 0x44 and pkt.parent and
571 # Bind/Alter
572 pkt.parent.ptype in [11, 12, 13, 14, 15],
573 ),
574 (
575 PacketLenField(
576 "auth_value",
577 NL_AUTH_SIGNATURE(),
578 NL_AUTH_SIGNATURE,
579 length_from=lambda pkt: pkt.parent.auth_len,
580 ),
581 lambda pkt: pkt.auth_type == 0x44
582 and (
583 # Other
584 not pkt.parent
585 or pkt.parent.ptype not in [11, 12, 13, 14, 15]
586 ),
587 ),
588 ],
589 PacketLenField(
590 "auth_value",
591 None,
592 conf.raw_layer,
593 length_from=lambda pkt: pkt.parent and pkt.parent.auth_len or 0,
594 ),
595 ),
596 ]
598 def is_protected(self):
599 if not self.auth_value:
600 return False
601 if self.parent and self.parent.ptype in [11, 12, 13, 14, 15, 16]:
602 return False
603 return True
605 def is_ssp(self):
606 if not self.auth_value:
607 return False
608 if self.parent and self.parent.ptype not in [11, 12, 13, 14, 15, 16]:
609 return False
610 return True
612 def default_payload_class(self, pkt):
613 return conf.padding_layer
616# [MS-RPCE] sect 2.2.2.13 - Verification Trailer
617_SECTRAILER_MAGIC = b"\x8a\xe3\x13\x71\x02\xf4\x36\x71"
620class DceRpcSecVTCommand(Packet):
621 name = "Verification trailer command"
622 fields_desc = [
623 BitField("SEC_VT_MUST_PROCESS_COMMAND", 0, 1, tot_size=-2),
624 BitField("SEC_VT_COMMAND_END", 0, 1),
625 BitEnumField(
626 "Command",
627 0,
628 -14,
629 {
630 0x0001: "SEC_VT_COMMAND_BITMASK_1",
631 0x0002: "SEC_VT_COMMAND_PCONTEXT",
632 0x0003: "SEC_VT_COMMAND_HEADER2",
633 },
634 end_tot_size=-2,
635 ),
636 LenField("Length", None, fmt="<H"),
637 ]
640# [MS-RPCE] sect 2.2.2.13.2
643class DceRpcSecVTBitmask(Packet):
644 name = "rpc_sec_vt_bitmask"
645 fields_desc = [
646 LEIntField("bits", 1),
647 ]
649 def default_payload_class(self, pkt):
650 return conf.padding_layer
653bind_layers(DceRpcSecVTCommand, DceRpcSecVTBitmask, Command=0x0001)
656# [MS-RPCE] sect 2.2.2.13.4
659class DceRpcSecVTPcontext(Packet):
660 name = "rpc_sec_vt_pcontext"
661 fields_desc = [
662 UUIDEnumField(
663 "InterfaceId",
664 None,
665 (
666 DCE_RPC_INTERFACES_NAMES.get,
667 lambda x: DCE_RPC_INTERFACES_NAMES_rev.get(x.lower()),
668 ),
669 uuid_fmt=UUIDField.FORMAT_LE,
670 ),
671 LEIntField("Version", 0),
672 UUIDEnumField(
673 "TransferSyntax",
674 None,
675 DCE_RPC_TRANSFER_SYNTAXES,
676 uuid_fmt=UUIDField.FORMAT_LE,
677 ),
678 LEIntField("TransferVersion", 0),
679 ]
681 def default_payload_class(self, pkt):
682 return conf.padding_layer
685bind_layers(DceRpcSecVTCommand, DceRpcSecVTPcontext, Command=0x0002)
688# [MS-RPCE] sect 2.2.2.13.3
691class DceRpcSecVTHeader2(Packet):
692 name = "rpc_sec_vt_header2"
693 fields_desc = [
694 ByteField("PTYPE", 0),
695 ByteField("Reserved1", 0),
696 LEShortField("Reserved2", 0),
697 LEIntField("drep", 0),
698 LEIntField("call_id", 0),
699 LEShortField("p_cont_id", 0),
700 LEShortField("opnum", 0),
701 ]
703 def default_payload_class(self, pkt):
704 return conf.padding_layer
707bind_layers(DceRpcSecVTCommand, DceRpcSecVTHeader2, Command=0x0003)
710class DceRpcSecVT(Packet):
711 name = "Verification trailer"
712 fields_desc = [
713 XStrFixedLenField("rpc_sec_verification_trailer", _SECTRAILER_MAGIC, length=8),
714 PacketListField("commands", [], DceRpcSecVTCommand),
715 ]
718class _VerifTrailerField(PacketField):
719 def getfield(
720 self,
721 pkt,
722 s,
723 ):
724 if _SECTRAILER_MAGIC in s:
725 # a bit ugly
726 ind = s.index(_SECTRAILER_MAGIC)
727 sectrailer_bytes, remain = bytes(s[:-ind]), bytes(s[-ind:])
728 vt_trailer = self.m2i(pkt, sectrailer_bytes)
729 if not isinstance(vt_trailer.payload, NoPayload):
730 # bad parse
731 return s, None
732 return remain, vt_trailer
733 return s, None
736# sect 12.6.3
739_DCE_RPC_5_FLAGS = {
740 0x01: "PFC_FIRST_FRAG",
741 0x02: "PFC_LAST_FRAG",
742 0x04: "PFC_PENDING_CANCEL",
743 0x08: "PFC_RESERVED_1",
744 0x10: "PFC_CONC_MPX",
745 0x20: "PFC_DID_NOT_EXECUTE",
746 0x40: "PFC_MAYBE",
747 0x80: "PFC_OBJECT_UUID",
748}
750# [MS-RPCE] sect 2.2.2.3
752_DCE_RPC_5_FLAGS_2 = _DCE_RPC_5_FLAGS.copy()
753_DCE_RPC_5_FLAGS_2[0x04] = "PFC_SUPPORT_HEADER_SIGN"
756_DCE_RPC_ERROR_CODES = {
757 # Win32
758 0x776: "OR_INVALID_OXID",
759 0x777: "OR_INVALID_OID",
760 0x778: "OR_INVALID_SET",
761 # Appendix N
762 0x1C010001: "nca_s_comm_failure",
763 0x1C010002: "nca_s_op_rng_error",
764 0x1C010003: "nca_s_unk_if",
765 0x1C010006: "nca_s_wrong_boot_time",
766 0x1C010009: "nca_s_you_crashed",
767 0x1C01000B: "nca_s_proto_error",
768 0x1C010013: "nca_s_out_args_too_big",
769 0x1C010014: "nca_s_server_too_busy",
770 0x1C010015: "nca_s_fault_string_too_long",
771 0x1C010017: "nca_s_unsupported_type",
772 0x1C000001: "nca_s_fault_int_div_by_zero",
773 0x1C000002: "nca_s_fault_addr_error",
774 0x1C000003: "nca_s_fault_fp_div_zero",
775 0x1C000004: "nca_s_fault_fp_underflow",
776 0x1C000005: "nca_s_fault_fp_overflow",
777 0x1C000006: "nca_s_fault_invalid_tag",
778 0x1C000007: "nca_s_fault_invalid_bound",
779 0x1C000008: "nca_s_rpc_version_mismatch",
780 0x1C000009: "nca_s_unspec_reject",
781 0x1C00000A: "nca_s_bad_actid",
782 0x1C00000B: "nca_s_who_are_you_failed",
783 0x1C00000C: "nca_s_manager_not_entered",
784 0x1C00000D: "nca_s_fault_cancel",
785 0x1C00000E: "nca_s_fault_ill_inst",
786 0x1C00000F: "nca_s_fault_fp_error",
787 0x1C000010: "nca_s_fault_int_overflow",
788 0x1C000012: "nca_s_fault_unspec",
789 0x1C000013: "nca_s_fault_remote_comm_failure",
790 0x1C000014: "nca_s_fault_pipe_empty",
791 0x1C000015: "nca_s_fault_pipe_closed",
792 0x1C000016: "nca_s_fault_pipe_order",
793 0x1C000017: "nca_s_fault_pipe_discipline",
794 0x1C000018: "nca_s_fault_pipe_comm_error",
795 0x1C000019: "nca_s_fault_pipe_memory",
796 0x1C00001A: "nca_s_fault_context_mismatch",
797 0x1C00001B: "nca_s_fault_remote_no_memory",
798 0x1C00001C: "nca_s_invalid_pres_context_id",
799 0x1C00001D: "nca_s_unsupported_authn_level",
800 0x1C00001F: "nca_s_invalid_checksum",
801 0x1C000020: "nca_s_invalid_crc",
802 0x1C000021: "nca_s_fault_user_defined",
803 0x1C000022: "nca_s_fault_tx_open_failed",
804 0x1C000023: "nca_s_fault_codeset_conv_error",
805 0x1C000024: "nca_s_fault_object_not_found",
806 0x1C000025: "nca_s_fault_no_client_stub",
807 # [MS-ERREF]
808 0x000006D3: "RPC_S_UNKNOWN_AUTHN_SERVICE",
809 0x000006D8: "EPT_S_CANT_PERFORM_OP",
810 0x000006F7: "RPC_X_BAD_STUB_DATA",
811 0x00000719: "RPC_S_NO_INTERFACES",
812 0x0000071A: "RPC_S_CALL_CANCELLED",
813 0x0000071B: "RPC_S_BINDING_INCOMPLETE",
814 0x0000071C: "RPC_S_COMM_FAILURE",
815 0x0000071D: "RPC_S_UNSUPPORTED_AUTHN_LEVEL",
816 0x0000071E: "RPC_S_NO_PRINC_NAME",
817 0x0000071F: "RPC_S_NOT_RPC_ERROR",
818 0x00000720: "RPC_S_UUID_LOCAL_ONLY",
819 0x00000721: "RPC_S_SEC_PKG_ERROR",
820 0x00000722: "RPC_S_NOT_CANCELLED",
821 0x0000076A: "RPC_S_GROUP_MEMBER_NOT_FOUND",
822 0x0000076C: "RPC_S_INVALID_OBJECT",
823 0x80004002: "E_NOINTERFACE",
824 0x80010107: "RPC_E_INVALIDMETHOD",
825 0x80010108: "RPC_E_DISCONNECTED",
826 0x80010109: "RPC_E_RETRY",
827 0x80040153: "REGDB_E_INVALIDVALUE",
828 0x80040154: "REGDB_E_CLASSNOTREG",
829 0x80040155: "REGDB_E_IIDNOTREG",
830 0x800706F7: "COM_X_BAD_STUB_DATA",
831}
833_DCE_RPC_REJECTION_REASONS = {
834 0: "REASON_NOT_SPECIFIED",
835 1: "TEMPORARY_CONGESTION",
836 2: "LOCAL_LIMIT_EXCEEDED",
837 3: "CALLED_PADDR_UNKNOWN",
838 4: "PROTOCOL_VERSION_NOT_SUPPORTED",
839 5: "DEFAULT_CONTEXT_NOT_SUPPORTED",
840 6: "USER_DATA_NOT_READABLE",
841 7: "NO_PSAP_AVAILABLE",
842 8: "AUTHENTICATION_TYPE_NOT_RECOGNIZED",
843 9: "INVALID_CHECKSUM",
844}
847class DceRpc5(DceRpc):
848 """
849 DCE/RPC v5 'connection-oriented' packet
850 """
852 name = "DCE/RPC v5"
853 fields_desc = (
854 [
855 ByteEnumField(
856 "rpc_vers", 5, {4: "4 (connection-less)", 5: "5 (connection-oriented)"}
857 ),
858 ByteField("rpc_vers_minor", 0),
859 ByteEnumField("ptype", 0, DCE_RPC_TYPE),
860 MultipleTypeField(
861 # [MS-RPCE] sect 2.2.2.3
862 [
863 (
864 FlagsField("pfc_flags", 0x3, 8, _DCE_RPC_5_FLAGS_2),
865 lambda pkt: pkt.ptype in [11, 12, 13, 14, 15, 16],
866 )
867 ],
868 FlagsField("pfc_flags", 0x3, 8, _DCE_RPC_5_FLAGS),
869 ),
870 ]
871 + _drep
872 + [
873 ByteField("reserved2", 0),
874 _EField(ShortField("frag_len", None)),
875 _EField(
876 FieldLenField(
877 "auth_len",
878 None,
879 fmt="H",
880 length_of="auth_verifier",
881 adjust=lambda _, x: 0 if not x else (x - 8),
882 )
883 ),
884 _EField(IntField("call_id", None)),
885 # Now let's proceed with trailer fields, i.e. at the end of the PACKET
886 # (below all payloads, etc.). Have a look at Figure 3 in sect 2.2.2.13
887 # of [MS-RPCE] but note the following:
888 # - auth_verifier includes sec_trailer + the authentication token
889 # - auth_padding is the authentication padding
890 # - vt_trailer is the verification trailer
891 ConditionalField(
892 TrailerField(
893 PacketLenField(
894 "auth_verifier",
895 None,
896 CommonAuthVerifier,
897 length_from=lambda pkt: pkt.auth_len + 8,
898 )
899 ),
900 lambda pkt: pkt.auth_len != 0,
901 ),
902 ConditionalField(
903 TrailerField(
904 StrLenField(
905 "auth_padding",
906 None,
907 length_from=lambda pkt: pkt.auth_verifier.auth_pad_length,
908 )
909 ),
910 lambda pkt: pkt.auth_len != 0,
911 ),
912 TrailerField(
913 _VerifTrailerField("vt_trailer", None, DceRpcSecVT),
914 ),
915 ]
916 )
918 def do_dissect(self, s):
919 # Overload do_dissect to only include the current layer in dissection.
920 # This allows to support TrailerFields, even in the case where multiple DceRpc5
921 # packets are concatenated
922 frag_len = self.get_field("frag_len").getfield(self, s[8:10])[1]
923 s, remain = s[:frag_len], s[frag_len:]
924 return super(DceRpc5, self).do_dissect(s) + remain
926 def extract_padding(self, s):
927 # Now, take any data that doesn't fit in the current fragment and make it
928 # padding. The caller is responsible for looking for eventual padding and
929 # creating the next fragment, etc.
930 pay_len = self.frag_len - len(self.original) + len(s)
931 return s[:pay_len], s[pay_len:]
933 def post_build(self, pkt, pay):
934 if (
935 self.auth_verifier
936 and self.auth_padding is None
937 and self.auth_verifier.auth_pad_length is None
938 ):
939 # Compute auth_len and add padding
940 auth_len = self.get_field("auth_len").getfield(self, pkt[10:12])[1] + 8
941 auth_verifier, pay = pay[-auth_len:], pay[:-auth_len]
942 pdu_len = len(pay)
943 if self.payload:
944 pdu_len -= len(self.payload.self_build())
945 padlen = (-pdu_len) % _COMMON_AUTH_PAD
946 auth_verifier = (
947 auth_verifier[:2] + struct.pack("B", padlen) + auth_verifier[3:]
948 )
949 pay = pay + (padlen * b"\x00") + auth_verifier
950 if self.frag_len is None:
951 # Compute frag_len
952 length = len(pkt) + len(pay)
953 pkt = (
954 pkt[:8]
955 + self.get_field("frag_len").addfield(self, b"", length)
956 + pkt[10:]
957 )
958 return pkt + pay
960 def answers(self, pkt):
961 return isinstance(pkt, DceRpc5) and pkt[DceRpc5].call_id == self.call_id
963 @classmethod
964 def tcp_reassemble(cls, data, _, session):
965 if data[0:1] != b"\x05":
966 return
967 endian = struct.unpack("!B", data[4:5])[0] >> 4
968 if endian not in [0, 1]:
969 return
970 length = struct.unpack(("<" if endian else ">") + "H", data[8:10])[0]
971 if len(data) >= length:
972 if conf.dcerpc_session_enable:
973 # If DCE/RPC sessions are enabled, use them !
974 if "dcerpcsess" not in session:
975 session["dcerpcsess"] = dcerpcsess = DceRpcSession()
976 else:
977 dcerpcsess = session["dcerpcsess"]
978 return dcerpcsess.process(DceRpc5(data))
979 return DceRpc5(data)
982# sec 12.6.3.1
985class DceRpc5AbstractSyntax(EPacket):
986 name = "Presentation Syntax (p_syntax_id_t)"
987 fields_desc = [
988 _EField(
989 UUIDEnumField(
990 "if_uuid",
991 None,
992 (
993 # Those are dynamic
994 DCE_RPC_INTERFACES_NAMES.get,
995 lambda x: DCE_RPC_INTERFACES_NAMES_rev.get(x.lower()),
996 ),
997 )
998 ),
999 _EField(IntField("if_version", 3)),
1000 ]
1003class DceRpc5TransferSyntax(EPacket):
1004 name = "Presentation Transfer Syntax (p_syntax_id_t)"
1005 fields_desc = [
1006 _EField(
1007 UUIDEnumField(
1008 "if_uuid",
1009 None,
1010 DCE_RPC_TRANSFER_SYNTAXES,
1011 )
1012 ),
1013 _EField(IntField("if_version", 3)),
1014 ]
1017class DceRpc5Context(EPacket):
1018 name = "Presentation Context (p_cont_elem_t)"
1019 fields_desc = [
1020 _EField(ShortField("cont_id", 0)),
1021 FieldLenField("n_transfer_syn", None, count_of="transfer_syntaxes", fmt="B"),
1022 ByteField("reserved", 0),
1023 EPacketField("abstract_syntax", None, DceRpc5AbstractSyntax),
1024 EPacketListField(
1025 "transfer_syntaxes",
1026 None,
1027 DceRpc5TransferSyntax,
1028 count_from=lambda pkt: pkt.n_transfer_syn,
1029 endianness_from=_dce_rpc_endianness,
1030 ),
1031 ]
1034class DceRpc5Result(EPacket):
1035 name = "Context negotiation Result"
1036 fields_desc = [
1037 _EField(
1038 ShortEnumField(
1039 "result", 0, ["acceptance", "user_rejection", "provider_rejection"]
1040 )
1041 ),
1042 _EField(
1043 ShortEnumField(
1044 "reason",
1045 0,
1046 _DCE_RPC_REJECTION_REASONS,
1047 )
1048 ),
1049 EPacketField("transfer_syntax", None, DceRpc5TransferSyntax),
1050 ]
1053class DceRpc5PortAny(EPacket):
1054 name = "Port Any (port_any_t)"
1055 fields_desc = [
1056 _EField(FieldLenField("length", None, length_of="port_spec", fmt="H")),
1057 _EField(StrLenField("port_spec", b"", length_from=lambda pkt: pkt.length)),
1058 ]
1061# sec 12.6.4.3
1064class DceRpc5Bind(_DceRpcPayload):
1065 name = "DCE/RPC v5 - Bind"
1066 fields_desc = [
1067 _EField(ShortField("max_xmit_frag", 5840)),
1068 _EField(ShortField("max_recv_frag", 8192)),
1069 _EField(IntField("assoc_group_id", 0)),
1070 # p_cont_list_t
1071 _EField(
1072 FieldLenField("n_context_elem", None, count_of="context_elem", fmt="B")
1073 ),
1074 StrFixedLenField("reserved", 0, length=3),
1075 EPacketListField(
1076 "context_elem",
1077 [],
1078 DceRpc5Context,
1079 endianness_from=_dce_rpc_endianness,
1080 count_from=lambda pkt: pkt.n_context_elem,
1081 ),
1082 ]
1085bind_layers(DceRpc5, DceRpc5Bind, ptype=11)
1087# sec 12.6.4.4
1090class DceRpc5BindAck(_DceRpcPayload):
1091 name = "DCE/RPC v5 - Bind Ack"
1092 fields_desc = [
1093 _EField(ShortField("max_xmit_frag", 5840)),
1094 _EField(ShortField("max_recv_frag", 8192)),
1095 _EField(IntField("assoc_group_id", 0)),
1096 PadField(
1097 EPacketField("sec_addr", None, DceRpc5PortAny),
1098 align=4,
1099 ),
1100 # p_result_list_t
1101 _EField(FieldLenField("n_results", None, count_of="results", fmt="B")),
1102 StrFixedLenField("reserved", 0, length=3),
1103 EPacketListField(
1104 "results",
1105 [],
1106 DceRpc5Result,
1107 endianness_from=_dce_rpc_endianness,
1108 count_from=lambda pkt: pkt.n_results,
1109 ),
1110 ]
1113bind_layers(DceRpc5, DceRpc5BindAck, ptype=12)
1115# sec 12.6.4.5
1118class DceRpc5Version(EPacket):
1119 name = "version_t"
1120 fields_desc = [
1121 ByteField("major", 0),
1122 ByteField("minor", 0),
1123 ]
1126class DceRpc5BindNak(_DceRpcPayload):
1127 name = "DCE/RPC v5 - Bind Nak"
1128 fields_desc = [
1129 _EField(
1130 ShortEnumField("provider_reject_reason", 0, _DCE_RPC_REJECTION_REASONS)
1131 ),
1132 # p_rt_versions_supported_t
1133 _EField(FieldLenField("n_protocols", None, count_of="protocols", fmt="B")),
1134 EPacketListField(
1135 "protocols",
1136 [],
1137 DceRpc5Version,
1138 count_from=lambda pkt: pkt.n_protocols,
1139 endianness_from=_dce_rpc_endianness,
1140 ),
1141 # [MS-RPCE] sect 2.2.2.9
1142 ConditionalField(
1143 ReversePadField(
1144 _EField(
1145 UUIDEnumField(
1146 "signature",
1147 None,
1148 {
1149 UUID(
1150 "90740320-fad0-11d3-82d7-009027b130ab"
1151 ): "Extended Error",
1152 },
1153 )
1154 ),
1155 align=8,
1156 ),
1157 lambda pkt: pkt.fields.get("signature", None)
1158 or (
1159 pkt.underlayer
1160 and pkt.underlayer.frag_len >= 24 + pkt.n_protocols * 2 + 16
1161 ),
1162 ),
1163 ]
1166bind_layers(DceRpc5, DceRpc5BindNak, ptype=13)
1169# sec 12.6.4.1
1172class DceRpc5AlterContext(_DceRpcPayload):
1173 name = "DCE/RPC v5 - AlterContext"
1174 fields_desc = DceRpc5Bind.fields_desc
1177bind_layers(DceRpc5, DceRpc5AlterContext, ptype=14)
1180# sec 12.6.4.2
1183class DceRpc5AlterContextResp(_DceRpcPayload):
1184 name = "DCE/RPC v5 - AlterContextResp"
1185 fields_desc = DceRpc5BindAck.fields_desc
1188bind_layers(DceRpc5, DceRpc5AlterContextResp, ptype=15)
1190# [MS-RPCE] sect 2.2.2.10 - rpc_auth_3
1193class DceRpc5Auth3(Packet):
1194 name = "DCE/RPC v5 - Auth3"
1195 fields_desc = [StrFixedLenField("pad", b"", length=4)]
1198bind_layers(DceRpc5, DceRpc5Auth3, ptype=16)
1200# sec 12.6.4.7
1203class DceRpc5Fault(_DceRpcPayload):
1204 name = "DCE/RPC v5 - Fault"
1205 fields_desc = [
1206 _EField(IntField("alloc_hint", 0)),
1207 _EField(ShortField("cont_id", 0)),
1208 ByteField("cancel_count", 0),
1209 FlagsField("reserved", 0, -8, {0x1: "RPC extended error"}),
1210 _EField(LEIntEnumField("status", 0, _DCE_RPC_ERROR_CODES)),
1211 IntField("reserved2", 0),
1212 ]
1215bind_layers(DceRpc5, DceRpc5Fault, ptype=3)
1218# sec 12.6.4.9
1221class DceRpc5Request(_DceRpcPayload):
1222 name = "DCE/RPC v5 - Request"
1223 fields_desc = [
1224 _EField(IntField("alloc_hint", 0)),
1225 _EField(ShortField("cont_id", 0)),
1226 _EField(ShortField("opnum", 0)),
1227 ConditionalField(
1228 PadField(
1229 _EField(UUIDField("object", None)),
1230 align=8,
1231 ),
1232 lambda pkt: pkt.underlayer and pkt.underlayer.pfc_flags.PFC_OBJECT_UUID,
1233 ),
1234 ]
1237bind_layers(DceRpc5, DceRpc5Request, ptype=0)
1239# sec 12.6.4.10
1242class DceRpc5Response(_DceRpcPayload):
1243 name = "DCE/RPC v5 - Response"
1244 fields_desc = [
1245 _EField(IntField("alloc_hint", 0)),
1246 _EField(ShortField("cont_id", 0)),
1247 ByteField("cancel_count", 0),
1248 ByteField("reserved", 0),
1249 ]
1252bind_layers(DceRpc5, DceRpc5Response, ptype=2)
1254# --- API
1256DceRpcOp = collections.namedtuple("DceRpcOp", ["request", "response"])
1257DCE_RPC_INTERFACES = {}
1260class DceRpcInterface:
1261 def __init__(self, name, uuid, version_tuple, if_version, opnums):
1262 self.name = name
1263 self.uuid = uuid
1264 self.major_version, self.minor_version = version_tuple
1265 self.if_version = if_version
1266 self.opnums = opnums
1268 def __repr__(self):
1269 return "<DCE/RPC Interface %s v%s.%s>" % (
1270 self.name,
1271 self.major_version,
1272 self.minor_version,
1273 )
1276def register_dcerpc_interface(name, uuid, version, opnums):
1277 """
1278 Register a DCE/RPC interface
1279 """
1280 version_tuple = tuple(map(int, version.split(".")))
1281 assert len(version_tuple) == 2, "Version should be in format 'X.X' !"
1282 if_version = (version_tuple[1] << 16) + version_tuple[0]
1283 if (uuid, if_version) in DCE_RPC_INTERFACES:
1284 # Interface is already registered.
1285 interface = DCE_RPC_INTERFACES[(uuid, if_version)]
1286 if interface.name == name:
1287 if set(opnums) - set(interface.opnums):
1288 # Interface is an extension of a previous interface
1289 interface.opnums.update(opnums)
1290 else:
1291 log_runtime.warning(
1292 "This interface is already registered: %s. Skip" % interface
1293 )
1294 return
1295 else:
1296 raise ValueError(
1297 "An interface with the same UUID is already registered: %s" % interface
1298 )
1299 else:
1300 # New interface
1301 DCE_RPC_INTERFACES_NAMES[uuid] = name
1302 DCE_RPC_INTERFACES_NAMES_rev[name.lower()] = uuid
1303 DCE_RPC_INTERFACES[(uuid, if_version)] = DceRpcInterface(
1304 name,
1305 uuid,
1306 version_tuple,
1307 if_version,
1308 opnums,
1309 )
1311 # bind for build
1312 for opnum, operations in opnums.items():
1313 bind_top_down(DceRpc5Request, operations.request, opnum=opnum)
1314 operations.request.opnum = opnum
1315 operations.request.intf = uuid
1318def find_dcerpc_interface(name) -> DceRpcInterface:
1319 """
1320 Find an interface object through the name in the IDL
1321 """
1322 try:
1323 return next(x for x in DCE_RPC_INTERFACES.values() if x.name == name)
1324 except StopIteration:
1325 raise AttributeError("Unknown interface !")
1328COM_INTERFACES = {}
1331class ComInterface:
1332 if_version = 0
1334 def __init__(self, name, uuid, opnums):
1335 self.name = name
1336 self.uuid = uuid
1337 self.opnums = opnums
1339 def __repr__(self):
1340 return "<COM Interface %s>" % (self.name,)
1343def register_com_interface(name, uuid, opnums):
1344 """
1345 Register a COM interface
1346 """
1347 COM_INTERFACES[uuid] = ComInterface(
1348 name,
1349 uuid,
1350 opnums,
1351 )
1352 # bind for build
1353 for opnum, operations in opnums.items():
1354 bind_top_down(DceRpc5Request, operations.request, opnum=opnum)
1355 COM_INTERFACES_NAMES[uuid] = name
1356 COM_INTERFACES_NAMES_rev[name.lower()] = uuid
1359def find_com_interface(name) -> ComInterface:
1360 """
1361 Find an interface object through the name in the IDL
1362 """
1363 try:
1364 return next(x for x in COM_INTERFACES.values() if x.name == name)
1365 except StopIteration:
1366 raise AttributeError("Unknown interface !")
1369# --- NDR fields - [C706] chap 14
1372def _set_ctx_on(f, obj):
1373 if isinstance(f, _NDRPacket):
1374 f.ndr64 = obj.ndr64
1375 f.ndrendian = obj.ndrendian
1376 if isinstance(f, list):
1377 for x in f:
1378 if isinstance(x, _NDRPacket):
1379 x.ndr64 = obj.ndr64
1380 x.ndrendian = obj.ndrendian
1383def _e(ndrendian):
1384 return {"big": ">", "little": "<"}[ndrendian]
1387class _NDRPacket(Packet):
1388 __slots__ = ["ndr64", "ndrendian", "deferred_pointers", "request_packet"]
1390 def __init__(self, *args, **kwargs):
1391 self.ndr64 = kwargs.pop("ndr64", conf.ndr64)
1392 self.ndrendian = kwargs.pop("ndrendian", "little")
1393 # request_packet is used in the session, so that a response packet
1394 # can resolve union arms if the case parameter is in the request.
1395 self.request_packet = kwargs.pop("request_packet", None)
1396 self.deferred_pointers = []
1397 super(_NDRPacket, self).__init__(*args, **kwargs)
1399 def do_dissect(self, s):
1400 _up = self.parent or self.underlayer
1401 if _up and isinstance(_up, _NDRPacket):
1402 self.ndr64 = _up.ndr64
1403 self.ndrendian = _up.ndrendian
1404 else:
1405 # See comment above NDRConstructedType
1406 return NDRConstructedType([]).read_deferred_pointers(
1407 self, super(_NDRPacket, self).do_dissect(s)
1408 )
1409 return super(_NDRPacket, self).do_dissect(s)
1411 def post_dissect(self, s):
1412 if self.deferred_pointers:
1413 # Can't trust the cache if there were deferred pointers
1414 self.raw_packet_cache = None
1415 return s
1417 def do_build(self):
1418 _up = self.parent or self.underlayer
1419 for f in self.fields.values():
1420 _set_ctx_on(f, self)
1421 if not _up or not isinstance(_up, _NDRPacket):
1422 # See comment above NDRConstructedType
1423 return NDRConstructedType([]).add_deferred_pointers(
1424 self, super(_NDRPacket, self).do_build()
1425 )
1426 return super(_NDRPacket, self).do_build()
1428 def default_payload_class(self, pkt):
1429 return conf.padding_layer
1431 def clone_with(self, *args, **kwargs):
1432 pkt = super(_NDRPacket, self).clone_with(*args, **kwargs)
1433 # We need to copy deferred_pointers to not break pointer deferral
1434 # on build.
1435 pkt.deferred_pointers = self.deferred_pointers
1436 pkt.ndr64 = self.ndr64
1437 pkt.ndrendian = self.ndrendian
1438 return pkt
1440 def copy(self):
1441 pkt = super(_NDRPacket, self).copy()
1442 pkt.deferred_pointers = self.deferred_pointers
1443 pkt.ndr64 = self.ndr64
1444 pkt.ndrendian = self.ndrendian
1445 return pkt
1447 def show2(self, dump=False, indent=3, lvl="", label_lvl=""):
1448 return self.__class__(
1449 bytes(self), ndr64=self.ndr64, ndrendian=self.ndrendian
1450 ).show(dump, indent, lvl, label_lvl)
1452 def getfield_and_val(self, attr):
1453 try:
1454 return Packet.getfield_and_val(self, attr)
1455 except ValueError:
1456 if self.request_packet:
1457 # Try to resolve the field from the request on failure
1458 try:
1459 return self.request_packet.getfield_and_val(attr)
1460 except AttributeError:
1461 pass
1462 raise
1464 def valueof(self, request: str):
1465 """
1466 Util to get the value of a NDRField, ignoring arrays, pointers, etc.
1467 """
1468 val = self
1469 for ndr_field in request.split("."):
1470 fld, fval = val.getfield_and_val(ndr_field)
1471 val = fld.valueof(val, fval)
1472 return val
1475class _NDRAlign:
1476 def padlen(self, flen, pkt):
1477 return -flen % self._align[pkt.ndr64]
1479 def original_length(self, pkt):
1480 # Find the length of the NDR frag to be able to pad properly
1481 while pkt:
1482 par = pkt.parent or pkt.underlayer
1483 if par and isinstance(par, _NDRPacket):
1484 pkt = par
1485 else:
1486 break
1487 return len(pkt.original)
1490class NDRAlign(_NDRAlign, ReversePadField):
1491 """
1492 ReversePadField modified to fit NDR.
1494 - If no align size is specified, use the one from the inner field
1495 - Size is calculated from the beginning of the NDR stream
1496 """
1498 def __init__(self, fld, align, padwith=None):
1499 super(NDRAlign, self).__init__(fld, align=align, padwith=padwith)
1502class _VirtualField(Field):
1503 # Hold a value but doesn't show up when building/dissecting
1504 def addfield(self, pkt, s, x):
1505 return s
1507 def getfield(self, pkt, s):
1508 return s, None
1511class _NDRPacketMetaclass(Packet_metaclass):
1512 def __new__(cls, name, bases, dct):
1513 newcls = super(_NDRPacketMetaclass, cls).__new__(cls, name, bases, dct)
1514 conformants = dct.get("DEPORTED_CONFORMANTS", [])
1515 if conformants:
1516 amount = len(conformants)
1517 if amount == 1:
1518 newcls.fields_desc.insert(
1519 0,
1520 _VirtualField("max_count", None),
1521 )
1522 else:
1523 newcls.fields_desc.insert(
1524 0,
1525 FieldListField(
1526 "max_counts",
1527 [],
1528 _VirtualField("", 0),
1529 count_from=lambda _: amount,
1530 ),
1531 )
1532 return newcls # type: ignore
1535class NDRPacket(_NDRPacket, metaclass=_NDRPacketMetaclass):
1536 """
1537 A NDR Packet. Handles pointer size & endianness
1538 """
1540 __slots__ = ["_align"]
1542 # NDR64 pad structures
1543 # [MS-RPCE] 2.2.5.3.4.1
1544 ALIGNMENT = (1, 1)
1545 # [C706] sect 14.3.7 - Conformants max_count can be added to the beginning
1546 DEPORTED_CONFORMANTS = []
1549# Primitive types
1552class _NDRValueOf:
1553 def valueof(self, pkt, x):
1554 return x
1557class _NDRLenField(_NDRValueOf, Field):
1558 """
1559 Field similar to FieldLenField that takes size_of and adjust as arguments,
1560 and take the value of a size on build.
1561 """
1563 __slots__ = ["size_of", "adjust"]
1565 def __init__(self, *args, **kwargs):
1566 self.size_of = kwargs.pop("size_of", None)
1567 self.adjust = kwargs.pop("adjust", lambda _, x: x)
1568 super(_NDRLenField, self).__init__(*args, **kwargs)
1570 def i2m(self, pkt, x):
1571 if x is None and pkt is not None and self.size_of is not None:
1572 fld, fval = pkt.getfield_and_val(self.size_of)
1573 f = fld.i2len(pkt, fval)
1574 x = self.adjust(pkt, f)
1575 elif x is None:
1576 x = 0
1577 return x
1580class NDRByteField(_NDRLenField, ByteField):
1581 pass
1584class NDRSignedByteField(_NDRLenField, SignedByteField):
1585 pass
1588class _NDRField(_NDRLenField):
1589 FMT = ""
1590 ALIGN = (0, 0)
1592 def getfield(self, pkt, s):
1593 return NDRAlign(
1594 Field("", 0, fmt=_e(pkt.ndrendian) + self.FMT), align=self.ALIGN
1595 ).getfield(pkt, s)
1597 def addfield(self, pkt, s, val):
1598 return NDRAlign(
1599 Field("", 0, fmt=_e(pkt.ndrendian) + self.FMT), align=self.ALIGN
1600 ).addfield(pkt, s, self.i2m(pkt, val))
1603class NDRShortField(_NDRField):
1604 FMT = "H"
1605 ALIGN = (2, 2)
1608class NDRSignedShortField(_NDRField):
1609 FMT = "h"
1610 ALIGN = (2, 2)
1613class NDRIntField(_NDRField):
1614 FMT = "I"
1615 ALIGN = (4, 4)
1618class NDRSignedIntField(_NDRField):
1619 FMT = "i"
1620 ALIGN = (4, 4)
1623class NDRLongField(_NDRField):
1624 FMT = "Q"
1625 ALIGN = (8, 8)
1628class NDRSignedLongField(_NDRField):
1629 FMT = "q"
1630 ALIGN = (8, 8)
1633class NDRIEEEFloatField(_NDRField):
1634 FMT = "f"
1635 ALIGN = (4, 4)
1638class NDRIEEEDoubleField(_NDRField):
1639 FMT = "d"
1640 ALIGN = (8, 8)
1643# Enum types
1646class _NDREnumField(_NDRValueOf, EnumField):
1647 # [MS-RPCE] sect 2.2.5.2 - Enums are 4 octets in NDR64
1648 FMTS = ["H", "I"]
1650 def getfield(self, pkt, s):
1651 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1652 return NDRAlign(Field("", 0, fmt=fmt), align=(2, 4)).getfield(pkt, s)
1654 def addfield(self, pkt, s, val):
1655 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1656 return NDRAlign(Field("", 0, fmt=fmt), align=(2, 4)).addfield(
1657 pkt, s, self.i2m(pkt, val)
1658 )
1661class NDRInt3264EnumField(NDRAlign):
1662 def __init__(self, *args, **kwargs):
1663 super(NDRInt3264EnumField, self).__init__(
1664 _NDREnumField(*args, **kwargs), align=(2, 4)
1665 )
1668class NDRIntEnumField(_NDRValueOf, NDRAlign):
1669 # v1_enum are always 4-octets, even in NDR32
1670 def __init__(self, *args, **kwargs):
1671 super(NDRIntEnumField, self).__init__(
1672 LEIntEnumField(*args, **kwargs), align=(4, 4)
1673 )
1676# Special types
1679class NDRInt3264Field(_NDRLenField):
1680 FMTS = ["I", "Q"]
1682 def getfield(self, pkt, s):
1683 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1684 return NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(pkt, s)
1686 def addfield(self, pkt, s, val):
1687 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1688 return NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(
1689 pkt, s, self.i2m(pkt, val)
1690 )
1693class NDRSignedInt3264Field(NDRInt3264Field):
1694 FMTS = ["i", "q"]
1697# Pointer types
1700class NDRPointer(_NDRPacket):
1701 fields_desc = [
1702 MultipleTypeField(
1703 [(XLELongField("referent_id", 1), lambda pkt: pkt and pkt.ndr64)],
1704 XLEIntField("referent_id", 1),
1705 ),
1706 PacketField("value", None, conf.raw_layer),
1707 ]
1710class NDRFullPointerField(_FieldContainer):
1711 """
1712 A NDR Full/Unique pointer field encapsulation.
1714 :param EMBEDDED: This pointer is embedded. This means that it's representation
1715 will not appear after the pointer (pointer deferral applies).
1716 See [C706] 14.3.12.3 - Algorithm for Deferral of Referents
1717 """
1719 EMBEDDED = False
1720 EMBEDDED_REF = False
1722 def __init__(self, fld, ref=False, fmt="I"):
1723 self.fld = fld
1724 self.ref = ref
1725 self.default = None
1727 def getfield(self, pkt, s):
1728 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
1729 remain, referent_id = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(
1730 pkt, s
1731 )
1733 # No value
1734 if referent_id == 0 and not self.EMBEDDED_REF:
1735 return remain, None
1737 # With value
1738 if self.EMBEDDED:
1739 # deferred
1740 ptr = NDRPointer(
1741 ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, referent_id=referent_id
1742 )
1743 pkt.deferred_pointers.append((ptr, partial(self.fld.getfield, pkt)))
1744 return remain, ptr
1746 remain, val = self.fld.getfield(pkt, remain)
1747 return remain, NDRPointer(
1748 ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, referent_id=referent_id, value=val
1749 )
1751 def addfield(self, pkt, s, val):
1752 if val is not None and not isinstance(val, NDRPointer):
1753 raise ValueError(
1754 "Expected NDRPointer in %s. You are using it wrong!" % self.name
1755 )
1756 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
1757 fld = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8))
1759 # No value
1760 if val is None and not self.EMBEDDED_REF:
1761 return fld.addfield(pkt, s, 0)
1763 # With value
1764 _set_ctx_on(val.value, pkt)
1765 s = fld.addfield(pkt, s, val.referent_id)
1766 if self.EMBEDDED:
1767 # deferred
1768 pkt.deferred_pointers.append(
1769 ((lambda s: self.fld.addfield(pkt, s, val.value)), val)
1770 )
1771 return s
1773 return self.fld.addfield(pkt, s, val.value)
1775 def any2i(self, pkt, x):
1776 # User-friendly helper
1777 if x is not None and not isinstance(x, NDRPointer):
1778 return NDRPointer(
1779 referent_id=0x20000,
1780 value=self.fld.any2i(pkt, x),
1781 )
1782 return x
1784 # Can't use i2repr = Field.i2repr and so on on PY2 :/
1785 def i2repr(self, pkt, val):
1786 return repr(val)
1788 def i2h(self, pkt, x):
1789 return x
1791 def h2i(self, pkt, x):
1792 return x
1794 def i2len(self, pkt, x):
1795 if x is None:
1796 return 0
1797 return self.fld.i2len(pkt, x.value)
1799 def valueof(self, pkt, x):
1800 if x is None:
1801 return x
1802 return self.fld.valueof(pkt, x.value)
1805class NDRFullEmbPointerField(NDRFullPointerField):
1806 """
1807 A NDR Embedded Full pointer.
1809 Same as NDRFullPointerField with EMBEDDED = True.
1810 """
1812 EMBEDDED = True
1815class NDRRefEmbPointerField(NDRFullPointerField):
1816 """
1817 A NDR Embedded Reference pointer.
1819 Same as NDRFullPointerField with EMBEDDED = True and EMBEDDED_REF = True.
1820 """
1822 EMBEDDED = True
1823 EMBEDDED_REF = True
1826# Constructed types
1829# Note: this is utterly complex and will drive you crazy
1831# If you have a NDRPacket that contains a deferred pointer on the top level
1832# (only happens in non DCE/RPC structures, such as in MS-PAC, where you have an NDR
1833# structure encapsulated in a non-NDR structure), there will be left-over deferred
1834# pointers when exiting dissection/build (deferred pointers are only computed when
1835# reaching a field that extends NDRConstructedType, which is normal: if you follow
1836# the DCE/RPC spec, pointers are never deferred in root structures)
1837# Therefore there is a special case forcing the build/dissection of any leftover
1838# pointers in NDRPacket, if Scapy detects that they won't be handled by any parent.
1840# Implementation notes: I chose to set 'handles_deferred' inside the FIELD, rather
1841# than inside the PACKET. This is faster to compute because whether a constructed type
1842# should handle deferral or not is computed only once when loading, therefore Scapy
1843# knows in advance whether to handle deferred pointers or not. But it is technically
1844# incorrect: with this approach, a structure (packet) cannot be used in 2 code paths
1845# that have different pointer managements. I mean by that that if there was a
1846# structure that was directly embedded in a RPC request without a pointer but also
1847# embedded with a pointer in another RPC request, it would break.
1848# Fortunately this isn't the case: structures are never reused for 2 purposes.
1849# (or at least I never seen that... <i hope this works>)
1852class NDRConstructedType(object):
1853 def __init__(self, fields):
1854 self.handles_deferred = False
1855 self.ndr_fields = fields
1856 self.rec_check_deferral()
1858 def rec_check_deferral(self):
1859 # We iterate through the fields within this constructed type.
1860 # If we have a pointer, mark this field as handling deferrance
1861 # and make all sub-constructed types not.
1862 for f in self.ndr_fields:
1863 if isinstance(f, NDRFullPointerField) and f.EMBEDDED:
1864 self.handles_deferred = True
1865 if isinstance(f, NDRConstructedType):
1866 f.rec_check_deferral()
1867 if f.handles_deferred:
1868 self.handles_deferred = True
1869 f.handles_deferred = False
1871 def getfield(self, pkt, s):
1872 s, fval = super(NDRConstructedType, self).getfield(pkt, s)
1873 if isinstance(fval, _NDRPacket):
1874 # If a sub-packet we just dissected has deferred pointers,
1875 # pass it to parent packet to propagate.
1876 pkt.deferred_pointers.extend(fval.deferred_pointers)
1877 del fval.deferred_pointers[:]
1878 if self.handles_deferred:
1879 # This field handles deferral !
1880 s = self.read_deferred_pointers(pkt, s)
1881 return s, fval
1883 def read_deferred_pointers(self, pkt, s):
1884 # Now read content of the pointers that were deferred
1885 q = collections.deque()
1886 q.extend(pkt.deferred_pointers)
1887 del pkt.deferred_pointers[:]
1888 while q:
1889 # Recursively resolve pointers that were deferred
1890 ptr, getfld = q.popleft()
1891 s, val = getfld(s)
1892 ptr.value = val
1893 if isinstance(val, _NDRPacket):
1894 # Pointer resolves to a packet.. that may have deferred pointers?
1895 q.extend(val.deferred_pointers)
1896 del val.deferred_pointers[:]
1897 return s
1899 def addfield(self, pkt, s, val):
1900 try:
1901 s = super(NDRConstructedType, self).addfield(pkt, s, val)
1902 except Exception as ex:
1903 try:
1904 ex.args = (
1905 "While building field '%s': " % self.name + ex.args[0],
1906 ) + ex.args[1:]
1907 except (AttributeError, IndexError):
1908 pass
1909 raise ex
1910 if isinstance(val, _NDRPacket):
1911 # If a sub-packet we just dissected has deferred pointers,
1912 # pass it to parent packet to propagate.
1913 pkt.deferred_pointers.extend(val.deferred_pointers)
1914 del val.deferred_pointers[:]
1915 if self.handles_deferred:
1916 # This field handles deferral !
1917 s = self.add_deferred_pointers(pkt, s)
1918 return s
1920 def add_deferred_pointers(self, pkt, s):
1921 # Now add content of pointers that were deferred
1922 q = collections.deque()
1923 q.extend(pkt.deferred_pointers)
1924 del pkt.deferred_pointers[:]
1925 while q:
1926 addfld, fval = q.popleft()
1927 s = addfld(s)
1928 if isinstance(fval, NDRPointer) and isinstance(fval.value, _NDRPacket):
1929 q.extend(fval.value.deferred_pointers)
1930 del fval.value.deferred_pointers[:]
1931 return s
1934class _NDRPacketField(_NDRValueOf, PacketField):
1935 def m2i(self, pkt, m):
1936 return self.cls(m, ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, _parent=pkt)
1939class _NDRPacketPadField(PadField):
1940 # [MS-RPCE] 2.2.5.3.4.1 Structure with Trailing Gap
1941 # Structures have extra alignment/padding in NDR64.
1942 def padlen(self, flen, pkt):
1943 if pkt.ndr64:
1944 return -flen % self._align[1]
1945 else:
1946 return 0
1949class NDRPacketField(NDRConstructedType, NDRAlign):
1950 def __init__(self, name, default, pkt_cls, **kwargs):
1951 self.DEPORTED_CONFORMANTS = pkt_cls.DEPORTED_CONFORMANTS
1952 self.fld = _NDRPacketField(name, default, pkt_cls=pkt_cls, **kwargs)
1954 # The inner _NDRPacketPadField handles NDR64's trailing gap in
1955 # the case where there a no inner conformants (see [MS-RPCE] 2.2.5.3.4.1)
1956 if self.DEPORTED_CONFORMANTS:
1957 innerfld = self.fld
1958 else:
1959 innerfld = _NDRPacketPadField(self.fld, align=pkt_cls.ALIGNMENT)
1961 # C706 14.3.2 Alignment of Constructed Types is handled by the
1962 # NDRAlign below.
1963 NDRAlign.__init__(
1964 self,
1965 innerfld,
1966 align=pkt_cls.ALIGNMENT,
1967 )
1968 NDRConstructedType.__init__(self, pkt_cls.fields_desc)
1970 def getfield(self, pkt, x):
1971 # Handle deformed conformants max_count here
1972 if self.DEPORTED_CONFORMANTS:
1973 # C706 14.3.2: "In other words, the size information precedes the
1974 # structure and is aligned independently of the structure alignment."
1975 fld = NDRInt3264Field("", 0)
1976 max_counts = []
1977 for _ in self.DEPORTED_CONFORMANTS:
1978 x, max_count = fld.getfield(pkt, x)
1979 max_counts.append(max_count)
1980 res, val = super(NDRPacketField, self).getfield(pkt, x)
1981 if len(max_counts) == 1:
1982 val.max_count = max_counts[0]
1983 else:
1984 val.max_counts = max_counts
1985 return res, val
1986 return super(NDRPacketField, self).getfield(pkt, x)
1988 def addfield(self, pkt, s, x):
1989 # Handle deformed conformants max_count here
1990 if self.DEPORTED_CONFORMANTS:
1991 mcfld = NDRInt3264Field("", 0)
1992 if len(self.DEPORTED_CONFORMANTS) == 1:
1993 max_counts = [x.max_count]
1994 else:
1995 max_counts = x.max_counts
1996 for fldname, max_count in zip(self.DEPORTED_CONFORMANTS, max_counts):
1997 if max_count is None:
1998 fld, val = x.getfield_and_val(fldname)
1999 max_count = fld.i2len(x, val)
2000 s = mcfld.addfield(pkt, s, max_count)
2001 return super(NDRPacketField, self).addfield(pkt, s, x)
2002 return super(NDRPacketField, self).addfield(pkt, s, x)
2005# Array types
2008class _NDRPacketListField(NDRConstructedType, PacketListField):
2009 """
2010 A PacketListField for NDR that can optionally pack the packets into NDRPointers
2011 """
2013 islist = 1
2014 holds_packets = 1
2016 __slots__ = ["ptr_lvl", "fld"]
2018 def __init__(self, name, default, pkt_cls, **kwargs):
2019 self.ptr_lvl = kwargs.pop("ptr_lvl", False)
2020 if self.ptr_lvl:
2021 # TODO: support more than 1 level ?
2022 self.fld = NDRFullEmbPointerField(NDRPacketField("", None, pkt_cls))
2023 else:
2024 self.fld = NDRPacketField("", None, pkt_cls)
2025 PacketListField.__init__(self, name, default, pkt_cls=pkt_cls, **kwargs)
2026 NDRConstructedType.__init__(self, [self.fld])
2028 def m2i(self, pkt, s):
2029 remain, val = self.fld.getfield(pkt, s)
2030 if val is None:
2031 val = NDRNone()
2032 # A mistake here would be to use / instead of add_payload. It adds a copy
2033 # which breaks pointer defferal. Same applies elsewhere
2034 val.add_payload(conf.padding_layer(remain))
2035 return val
2037 def any2i(self, pkt, x):
2038 # User-friendly helper
2039 if isinstance(x, list):
2040 x = [self.fld.any2i(pkt, y) for y in x]
2041 return super(_NDRPacketListField, self).any2i(pkt, x)
2043 def i2m(self, pkt, val):
2044 return self.fld.addfield(pkt, b"", val)
2046 def i2len(self, pkt, x):
2047 return len(x)
2049 def valueof(self, pkt, x):
2050 return [
2051 self.fld.valueof(pkt, y if not isinstance(y, NDRNone) else None) for y in x
2052 ]
2055class NDRFieldListField(NDRConstructedType, FieldListField):
2056 """
2057 A FieldListField for NDR
2058 """
2060 islist = 1
2062 def __init__(self, *args, **kwargs):
2063 if "length_is" in kwargs:
2064 kwargs["count_from"] = kwargs.pop("length_is")
2065 elif "size_is" in kwargs:
2066 kwargs["count_from"] = kwargs.pop("size_is")
2067 FieldListField.__init__(self, *args, **kwargs)
2068 NDRConstructedType.__init__(self, [self.field])
2070 def i2len(self, pkt, x):
2071 return len(x)
2073 def valueof(self, pkt, x):
2074 return [self.field.valueof(pkt, y) for y in x]
2077class NDRVaryingArray(_NDRPacket):
2078 fields_desc = [
2079 MultipleTypeField(
2080 [(LELongField("offset", 0), lambda pkt: pkt and pkt.ndr64)],
2081 LEIntField("offset", 0),
2082 ),
2083 MultipleTypeField(
2084 [
2085 (
2086 LELongField("actual_count", None),
2087 lambda pkt: pkt and pkt.ndr64,
2088 )
2089 ],
2090 LEIntField("actual_count", None),
2091 ),
2092 PacketField("value", None, conf.raw_layer),
2093 ]
2096class _NDRVarField:
2097 """
2098 NDR Varying Array / String field
2099 """
2101 LENGTH_FROM = False
2102 COUNT_FROM = False
2104 def __init__(self, *args, **kwargs):
2105 # We build the length_is function by taking into account both the
2106 # actual_count (from the varying field) and a potentially provided
2107 # length_is field.
2108 if "length_is" in kwargs:
2109 _length_is = kwargs.pop("length_is")
2110 length_is = lambda pkt: (_length_is(pkt.underlayer) or pkt.actual_count)
2111 else:
2112 length_is = lambda pkt: pkt.actual_count
2113 # Pass it to the sub-field (actually subclass)
2114 if self.LENGTH_FROM:
2115 kwargs["length_from"] = length_is
2116 elif self.COUNT_FROM:
2117 kwargs["count_from"] = length_is
2118 # TODO: For now, we do nothing with max_is
2119 if "max_is" in kwargs:
2120 kwargs.pop("max_is")
2121 super(_NDRVarField, self).__init__(*args, **kwargs)
2123 def getfield(self, pkt, s):
2124 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2125 remain, offset = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(pkt, s)
2126 remain, actual_count = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(
2127 pkt, remain
2128 )
2129 final = NDRVaryingArray(
2130 ndr64=pkt.ndr64,
2131 ndrendian=pkt.ndrendian,
2132 offset=offset,
2133 actual_count=actual_count,
2134 _underlayer=pkt,
2135 )
2136 remain, val = super(_NDRVarField, self).getfield(final, remain)
2137 final.value = super(_NDRVarField, self).i2h(pkt, val)
2138 return remain, final
2140 def addfield(self, pkt, s, val):
2141 if not isinstance(val, NDRVaryingArray):
2142 raise ValueError(
2143 "Expected NDRVaryingArray in %s. You are using it wrong!" % self.name
2144 )
2145 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2146 _set_ctx_on(val.value, pkt)
2147 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(pkt, s, val.offset)
2148 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(
2149 pkt,
2150 s,
2151 val.actual_count is None
2152 and super(_NDRVarField, self).i2len(pkt, val.value)
2153 or val.actual_count,
2154 )
2155 return super(_NDRVarField, self).addfield(
2156 pkt, s, super(_NDRVarField, self).h2i(pkt, val.value)
2157 )
2159 def i2len(self, pkt, x):
2160 return super(_NDRVarField, self).i2len(pkt, x.value)
2162 def any2i(self, pkt, x):
2163 # User-friendly helper
2164 if not isinstance(x, NDRVaryingArray):
2165 return NDRVaryingArray(
2166 value=super(_NDRVarField, self).any2i(pkt, x),
2167 )
2168 return x
2170 # Can't use i2repr = Field.i2repr and so on on PY2 :/
2171 def i2repr(self, pkt, val):
2172 return repr(val)
2174 def i2h(self, pkt, x):
2175 return x
2177 def h2i(self, pkt, x):
2178 return x
2180 def valueof(self, pkt, x):
2181 return super(_NDRVarField, self).valueof(pkt, x.value)
2184class NDRConformantArray(_NDRPacket):
2185 fields_desc = [
2186 MultipleTypeField(
2187 [(LELongField("max_count", None), lambda pkt: pkt and pkt.ndr64)],
2188 LEIntField("max_count", None),
2189 ),
2190 MultipleTypeField(
2191 [
2192 (
2193 PacketListField(
2194 "value",
2195 [],
2196 conf.raw_layer,
2197 count_from=lambda pkt: pkt.max_count,
2198 ),
2199 (
2200 lambda pkt: pkt.fields.get("value", None)
2201 and isinstance(pkt.fields["value"][0], Packet),
2202 lambda _, val: val and isinstance(val[0], Packet),
2203 ),
2204 )
2205 ],
2206 FieldListField(
2207 "value", [], LEIntField("", 0), count_from=lambda pkt: pkt.max_count
2208 ),
2209 ),
2210 ]
2213class NDRConformantString(_NDRPacket):
2214 fields_desc = [
2215 MultipleTypeField(
2216 [(LELongField("max_count", None), lambda pkt: pkt and pkt.ndr64)],
2217 LEIntField("max_count", None),
2218 ),
2219 StrField("value", ""),
2220 ]
2223class _NDRConfField:
2224 """
2225 NDR Conformant Array / String field
2226 """
2228 CONFORMANT_STRING = False
2229 LENGTH_FROM = False
2230 COUNT_FROM = False
2232 def __init__(self, *args, **kwargs):
2233 # when conformant_in_struct is True, we remove the level of abstraction
2234 # provided by NDRConformantString / NDRConformantArray because max_count
2235 # is a proper field in the parent packet.
2236 self.conformant_in_struct = kwargs.pop("conformant_in_struct", False)
2237 # size_is/max_is end up here, and is what defines a conformant field.
2238 if "size_is" in kwargs:
2239 size_is = kwargs.pop("size_is")
2240 if self.LENGTH_FROM:
2241 kwargs["length_from"] = size_is
2242 elif self.COUNT_FROM:
2243 kwargs["count_from"] = size_is
2244 # TODO: For now, we do nothing with max_is
2245 if "max_is" in kwargs:
2246 kwargs.pop("max_is")
2247 super(_NDRConfField, self).__init__(*args, **kwargs)
2249 def getfield(self, pkt, s):
2250 # [C706] - 14.3.7 Structures Containing Arrays
2251 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2252 if self.conformant_in_struct:
2253 # [MS-RPCE] 2.2.5.3.4.2 Structure Containing a Conformant Array
2254 # Padding is here: just before the Conformant content
2255 return NDRAlign(
2256 super(_NDRConfField, self),
2257 align=pkt.ALIGNMENT,
2258 ).getfield(pkt, s)
2260 # The max count is aligned as a primitive type
2261 remain, max_count = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(
2262 pkt, s
2263 )
2264 remain, val = super(_NDRConfField, self).getfield(pkt, remain)
2265 return remain, (
2266 NDRConformantString if self.CONFORMANT_STRING else NDRConformantArray
2267 )(ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, max_count=max_count, value=val)
2269 def addfield(self, pkt, s, val):
2270 if self.conformant_in_struct:
2271 # [MS-RPCE] 2.2.5.3.4.2 Structure Containing a Conformant Array
2272 # Padding is here: just before the Conformant content
2273 return NDRAlign(super(_NDRConfField, self), align=pkt.ALIGNMENT).addfield(
2274 pkt, s, val
2275 )
2277 if self.CONFORMANT_STRING and not isinstance(val, NDRConformantString):
2278 raise ValueError(
2279 "Expected NDRConformantString in %s. You are using it wrong!"
2280 % self.name
2281 )
2282 elif not self.CONFORMANT_STRING and not isinstance(val, NDRConformantArray):
2283 raise ValueError(
2284 "Expected NDRConformantArray in %s. You are using it wrong!" % self.name
2285 )
2286 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2287 _set_ctx_on(val.value, pkt)
2288 if val.value and isinstance(val.value[0], NDRVaryingArray):
2289 value = val.value[0]
2290 else:
2291 value = val.value
2292 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(
2293 pkt,
2294 s,
2295 val.max_count is None
2296 and super(_NDRConfField, self).i2len(pkt, value)
2297 or val.max_count,
2298 )
2299 return super(_NDRConfField, self).addfield(pkt, s, value)
2301 def _subval(self, x):
2302 if self.conformant_in_struct:
2303 value = x
2304 elif (
2305 not self.CONFORMANT_STRING
2306 and x.value
2307 and isinstance(x.value[0], NDRVaryingArray)
2308 ):
2309 value = x.value[0]
2310 else:
2311 value = x.value
2312 return value
2314 def i2len(self, pkt, x):
2315 return super(_NDRConfField, self).i2len(pkt, self._subval(x))
2317 def any2i(self, pkt, x):
2318 # User-friendly helper
2319 if self.conformant_in_struct:
2320 return super(_NDRConfField, self).any2i(pkt, x)
2321 if self.CONFORMANT_STRING and not isinstance(x, NDRConformantString):
2322 return NDRConformantString(
2323 value=super(_NDRConfField, self).any2i(pkt, x),
2324 )
2325 elif not isinstance(x, NDRConformantArray):
2326 return NDRConformantArray(
2327 value=super(_NDRConfField, self).any2i(pkt, x),
2328 )
2329 return x
2331 # Can't use i2repr = Field.i2repr and so on on PY2 :/
2332 def i2repr(self, pkt, val):
2333 return repr(val)
2335 def i2h(self, pkt, x):
2336 return x
2338 def h2i(self, pkt, x):
2339 return x
2341 def valueof(self, pkt, x):
2342 return super(_NDRConfField, self).valueof(pkt, self._subval(x))
2345class NDRVarPacketListField(_NDRVarField, _NDRPacketListField):
2346 """
2347 NDR Varying PacketListField. Unused
2348 """
2350 COUNT_FROM = True
2353class NDRConfPacketListField(_NDRConfField, _NDRPacketListField):
2354 """
2355 NDR Conformant PacketListField
2356 """
2358 COUNT_FROM = True
2361class NDRConfVarPacketListField(_NDRConfField, _NDRVarField, _NDRPacketListField):
2362 """
2363 NDR Conformant Varying PacketListField
2364 """
2366 COUNT_FROM = True
2369class NDRConfFieldListField(_NDRConfField, NDRFieldListField):
2370 """
2371 NDR Conformant FieldListField
2372 """
2374 COUNT_FROM = True
2377class NDRConfVarFieldListField(_NDRConfField, _NDRVarField, NDRFieldListField):
2378 """
2379 NDR Conformant Varying FieldListField
2380 """
2382 COUNT_FROM = True
2385# NDR String fields
2388class _NDRUtf16(Field):
2389 def h2i(self, pkt, x):
2390 encoding = {"big": "utf-16be", "little": "utf-16le"}[pkt.ndrendian]
2391 return plain_str(x).encode(encoding)
2393 def i2h(self, pkt, x):
2394 encoding = {"big": "utf-16be", "little": "utf-16le"}[pkt.ndrendian]
2395 return bytes_encode(x).decode(encoding, errors="replace")
2398class NDRConfStrLenField(_NDRConfField, _NDRValueOf, StrLenField):
2399 """
2400 NDR Conformant StrLenField.
2402 This is not a "string" per NDR, but an a conformant byte array
2403 (e.g. tower_octet_string). For ease of use, we implicitly convert
2404 it in specific cases.
2405 """
2407 CONFORMANT_STRING = True
2408 LENGTH_FROM = True
2411class NDRConfStrLenFieldUtf16(_NDRConfField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16):
2412 """
2413 NDR Conformant StrLenFieldUtf16.
2415 See NDRConfStrLenField for comment.
2416 """
2418 CONFORMANT_STRING = True
2419 ON_WIRE_SIZE_UTF16 = False
2420 LENGTH_FROM = True
2423class NDRVarStrNullField(_NDRVarField, _NDRValueOf, StrNullField):
2424 """
2425 NDR Varying StrNullField
2426 """
2428 NULLFIELD = True
2431class NDRVarStrNullFieldUtf16(_NDRVarField, _NDRValueOf, StrNullFieldUtf16, _NDRUtf16):
2432 """
2433 NDR Varying StrNullFieldUtf16
2434 """
2436 NULLFIELD = True
2439class NDRVarStrLenField(_NDRVarField, StrLenField):
2440 """
2441 NDR Varying StrLenField
2442 """
2444 LENGTH_FROM = True
2447class NDRVarStrLenFieldUtf16(_NDRVarField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16):
2448 """
2449 NDR Varying StrLenFieldUtf16
2450 """
2452 ON_WIRE_SIZE_UTF16 = False
2453 LENGTH_FROM = True
2456class NDRConfVarStrLenField(_NDRConfField, _NDRVarField, _NDRValueOf, StrLenField):
2457 """
2458 NDR Conformant Varying StrLenField
2459 """
2461 LENGTH_FROM = True
2464class NDRConfVarStrLenFieldUtf16(
2465 _NDRConfField, _NDRVarField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16
2466):
2467 """
2468 NDR Conformant Varying StrLenFieldUtf16
2469 """
2471 ON_WIRE_SIZE_UTF16 = False
2472 LENGTH_FROM = True
2475class NDRConfVarStrNullField(_NDRConfField, _NDRVarField, _NDRValueOf, StrNullField):
2476 """
2477 NDR Conformant Varying StrNullField
2478 """
2480 NULLFIELD = True
2483class NDRConfVarStrNullFieldUtf16(
2484 _NDRConfField, _NDRVarField, _NDRValueOf, StrNullFieldUtf16, _NDRUtf16
2485):
2486 """
2487 NDR Conformant Varying StrNullFieldUtf16
2488 """
2490 ON_WIRE_SIZE_UTF16 = False
2491 NULLFIELD = True
2494# Union type
2497class NDRUnion(_NDRPacket):
2498 fields_desc = [
2499 IntField("tag", 0),
2500 PacketField("value", None, conf.raw_layer),
2501 ]
2504class _NDRUnionField(MultipleTypeField):
2505 __slots__ = ["switch_fmt", "align"]
2507 def __init__(self, flds, dflt, align, switch_fmt):
2508 self.switch_fmt = switch_fmt
2509 self.align = align
2510 super(_NDRUnionField, self).__init__(flds, dflt)
2512 def getfield(self, pkt, s):
2513 fmt = _e(pkt.ndrendian) + self.switch_fmt[pkt.ndr64]
2514 remain, tag = NDRAlign(Field("", 0, fmt=fmt), align=self.align).getfield(pkt, s)
2515 fld, _ = super(_NDRUnionField, self)._find_fld_pkt_val(pkt, NDRUnion(tag=tag))
2516 remain, val = fld.getfield(pkt, remain)
2517 return remain, NDRUnion(
2518 tag=tag, value=val, ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, _parent=pkt
2519 )
2521 def addfield(self, pkt, s, val):
2522 fmt = _e(pkt.ndrendian) + self.switch_fmt[pkt.ndr64]
2523 if not isinstance(val, NDRUnion):
2524 raise ValueError(
2525 "Expected NDRUnion in %s. You are using it wrong!" % self.name
2526 )
2527 _set_ctx_on(val.value, pkt)
2528 # First, align the whole tag+union against the align param
2529 s = NDRAlign(Field("", 0, fmt=fmt), align=self.align).addfield(pkt, s, val.tag)
2530 # Then, compute the subfield with its own alignment
2531 return super(_NDRUnionField, self).addfield(pkt, s, val)
2533 def _find_fld_pkt_val(self, pkt, val):
2534 fld, val = super(_NDRUnionField, self)._find_fld_pkt_val(pkt, val)
2535 return fld, val.value
2537 # Can't use i2repr = Field.i2repr and so on on PY2 :/
2538 def i2repr(self, pkt, val):
2539 return repr(val)
2541 def i2h(self, pkt, x):
2542 return x
2544 def h2i(self, pkt, x):
2545 return x
2547 def valueof(self, pkt, x):
2548 fld, val = self._find_fld_pkt_val(pkt, x)
2549 return fld.valueof(pkt, x.value)
2552class NDRUnionField(NDRConstructedType, _NDRUnionField):
2553 def __init__(self, flds, dflt, align, switch_fmt):
2554 _NDRUnionField.__init__(self, flds, dflt, align=align, switch_fmt=switch_fmt)
2555 NDRConstructedType.__init__(self, [x[0] for x in flds] + [dflt])
2557 def any2i(self, pkt, x):
2558 # User-friendly helper
2559 if x:
2560 if not isinstance(x, NDRUnion):
2561 raise ValueError("Invalid value for %s; should be NDRUnion" % self.name)
2562 else:
2563 x.value = _NDRUnionField.any2i(self, pkt, x)
2564 return x
2567# Misc
2570class _ProxyArray:
2571 # Hack for recursive fields DEPORTED_CONFORMANTS field
2572 __slots__ = ["getfld"]
2574 def __init__(self, getfld):
2575 self.getfld = getfld
2577 def __len__(self):
2578 try:
2579 return len(self.getfld())
2580 except AttributeError:
2581 return 0
2583 def __iter__(self):
2584 try:
2585 return iter(self.getfld())
2586 except AttributeError:
2587 return iter([])
2590class _ProxyTuple:
2591 # Hack for recursive fields ALIGNMENT field
2592 __slots__ = ["getfld"]
2594 def __init__(self, getfld):
2595 self.getfld = getfld
2597 def __getitem__(self, name):
2598 try:
2599 return self.getfld()[name]
2600 except AttributeError:
2601 raise KeyError
2604def NDRRecursiveClass(clsname):
2605 """
2606 Return a special class that is used for pointer recursion
2607 """
2608 # Get module where this is called
2609 frame = inspect.currentframe().f_back
2610 mod = frame.f_globals["__loader__"].name
2611 getcls = lambda: getattr(importlib.import_module(mod), clsname)
2613 class _REC(NDRPacket):
2614 ALIGNMENT = _ProxyTuple(lambda: getattr(getcls(), "ALIGNMENT"))
2615 DEPORTED_CONFORMANTS = _ProxyArray(
2616 lambda: getattr(getcls(), "DEPORTED_CONFORMANTS")
2617 )
2619 @classmethod
2620 def dispatch_hook(cls, _pkt=None, *args, **kargs):
2621 return getcls()
2623 return _REC
2626# The very few NDR-specific structures
2629class NDRContextHandle(NDRPacket):
2630 ALIGNMENT = (4, 4)
2631 fields_desc = [
2632 LEIntField("attributes", 0),
2633 StrFixedLenField("uuid", b"", length=16),
2634 ]
2636 def guess_payload_class(self, payload):
2637 return conf.padding_layer
2640class NDRNone(NDRPacket):
2641 # This is only used in NDRPacketListField to act as a "None" pointer, and is
2642 # a workaround because the field doesn't support None as a value in the list.
2643 name = "None"
2644 ALIGNMENT = (4, 8)
2645 fields_desc = [
2646 NDRInt3264Field("ptr", 0),
2647 ]
2650# --- Type Serialization Version 1 - [MSRPCE] sect 2.2.6
2653def _get_ndrtype1_endian(pkt):
2654 if pkt.underlayer is None:
2655 return "<"
2656 return {0x00: ">", 0x10: "<"}.get(pkt.underlayer.Endianness, "<")
2659class NDRSerialization1Header(Packet):
2660 fields_desc = [
2661 ByteField("Version", 1),
2662 ByteEnumField("Endianness", 0x10, {0x00: "big", 0x10: "little"}),
2663 LEShortField("CommonHeaderLength", 8),
2664 XLEIntField("Filler", 0xCCCCCCCC),
2665 ]
2667 # Add a bit of goo so that valueof() goes through the header
2669 def _ndrlayer(self):
2670 cur = self
2671 while cur and not isinstance(cur, _NDRPacket) and cur.payload:
2672 cur = cur.payload
2673 if isinstance(cur, NDRPointer):
2674 cur = cur.value
2675 return cur
2677 def getfield_and_val(self, attr):
2678 try:
2679 return Packet.getfield_and_val(self, attr)
2680 except ValueError:
2681 return self._ndrlayer().getfield_and_val(attr)
2683 def valueof(self, name):
2684 return self._ndrlayer().valueof(name)
2687class NDRSerialization1PrivateHeader(Packet):
2688 fields_desc = [
2689 EField(
2690 LEIntField("ObjectBufferLength", 0), endianness_from=_get_ndrtype1_endian
2691 ),
2692 XLEIntField("Filler", 0),
2693 ]
2696def ndr_deserialize1(b, cls, ptr_pack=False):
2697 """
2698 Deserialize Type Serialization Version 1
2699 [MS-RPCE] sect 2.2.6
2701 :param ptr_pack: pack in a pointer to the structure.
2702 """
2703 if issubclass(cls, NDRPacket):
2704 # We use an intermediary class because it uses NDRPacketField which handles
2705 # deported conformant fields
2706 if ptr_pack:
2707 hdrlen = 20
2709 class _cls(NDRPacket):
2710 fields_desc = [NDRFullPointerField(NDRPacketField("pkt", None, cls))]
2712 else:
2713 hdrlen = 16
2715 class _cls(NDRPacket):
2716 fields_desc = [NDRPacketField("pkt", None, cls)]
2718 hdr = NDRSerialization1Header(b[:8]) / NDRSerialization1PrivateHeader(b[8:16])
2719 endian = {0x00: "big", 0x10: "little"}[hdr.Endianness]
2720 padlen = (-hdr.ObjectBufferLength) % _TYPE1_S_PAD
2721 # padlen should be 0 (pad included in length), but some implementations
2722 # implement apparently misread the spec
2723 return (
2724 hdr
2725 / _cls(
2726 b[16 : hdrlen + hdr.ObjectBufferLength],
2727 ndr64=False, # Only NDR32 is supported in Type 1
2728 ndrendian=endian,
2729 ).pkt
2730 / conf.padding_layer(b[hdrlen + padlen + hdr.ObjectBufferLength :])
2731 )
2732 return NDRSerialization1Header(b[:8]) / cls(b[8:])
2735def ndr_serialize1(pkt, ptr_pack=False):
2736 """
2737 Serialize Type Serialization Version 1
2738 [MS-RPCE] sect 2.2.6
2740 :param ptr_pack: pack in a pointer to the structure.
2741 """
2742 pkt = pkt.copy()
2743 endian = getattr(pkt, "ndrendian", "little")
2744 if not isinstance(pkt, NDRSerialization1Header):
2745 if not isinstance(pkt, NDRPacket):
2746 return bytes(NDRSerialization1Header(Endianness=endian) / pkt)
2747 if isinstance(pkt, NDRPointer):
2748 cls = pkt.value.__class__
2749 else:
2750 cls = pkt.__class__
2751 val = pkt
2752 pkt_len = len(pkt)
2753 # ObjectBufferLength:
2754 # > It MUST include the padding length and exclude the header itself
2755 pkt = NDRSerialization1Header(
2756 Endianness=endian
2757 ) / NDRSerialization1PrivateHeader(
2758 ObjectBufferLength=pkt_len + (-pkt_len) % _TYPE1_S_PAD
2759 )
2760 else:
2761 cls = pkt.value.__class__
2762 val = pkt.payload.payload
2763 pkt.payload.remove_payload()
2765 # See above about why we need an intermediary class
2766 if ptr_pack:
2768 class _cls(NDRPacket):
2769 fields_desc = [NDRFullPointerField(NDRPacketField("pkt", None, cls))]
2771 else:
2773 class _cls(NDRPacket):
2774 fields_desc = [NDRPacketField("pkt", None, cls)]
2776 ret = bytes(pkt / _cls(pkt=val, ndr64=False, ndrendian=endian))
2777 return ret + (-len(ret) % _TYPE1_S_PAD) * b"\x00"
2780class _NDRSerializeType1:
2781 def __init__(self, *args, **kwargs):
2782 self.ptr_pack = kwargs.pop("ptr_pack", False)
2783 super(_NDRSerializeType1, self).__init__(*args, **kwargs)
2785 def i2m(self, pkt, val):
2786 return ndr_serialize1(val, ptr_pack=self.ptr_pack)
2788 def m2i(self, pkt, s):
2789 return ndr_deserialize1(s, self.cls, ptr_pack=self.ptr_pack)
2791 def i2len(self, pkt, val):
2792 return len(self.i2m(pkt, val))
2795class NDRSerializeType1PacketField(_NDRSerializeType1, PacketField):
2796 __slots__ = ["ptr_pack"]
2799class NDRSerializeType1PacketLenField(_NDRSerializeType1, PacketLenField):
2800 __slots__ = ["ptr_pack"]
2803class NDRSerializeType1PacketListField(_NDRSerializeType1, PacketListField):
2804 __slots__ = ["ptr_pack"]
2806 def i2len(self, pkt, val):
2807 return sum(len(self.i2m(pkt, p)) for p in val)
2810# --- DCE/RPC session
2813class DceRpcSession(DefaultSession):
2814 """
2815 A DCE/RPC session within a TCP socket.
2816 """
2818 def __init__(self, *args, **kwargs):
2819 self.rpc_bind_interface: Union[DceRpcInterface, ComInterface] = None
2820 self.rpc_bind_is_com: bool = False
2821 self.ndr64 = False
2822 self.ndrendian = "little"
2823 self.support_header_signing = kwargs.pop("support_header_signing", True)
2824 self.header_sign = conf.dcerpc_force_header_signing
2825 self.ssp = kwargs.pop("ssp", None)
2826 self.sspcontext = kwargs.pop("sspcontext", None)
2827 self.auth_level = kwargs.pop("auth_level", None)
2828 self.sent_cont_ids = []
2829 self.cont_id = 0 # Currently selected context
2830 self.auth_context_id = 0 # Currently selected authentication context
2831 self.assoc_group_id = 0 # Currently selected association group
2832 self.map_callid_opnum = {}
2833 self.frags = collections.defaultdict(lambda: b"")
2834 self.sniffsspcontexts = {} # Unfinished contexts for passive
2835 if conf.dcerpc_session_enable and conf.winssps_passive:
2836 for ssp in conf.winssps_passive:
2837 self.sniffsspcontexts[ssp] = None
2838 super(DceRpcSession, self).__init__(*args, **kwargs)
2840 def _up_pkt(self, pkt):
2841 """
2842 Common function to handle the DCE/RPC session: what interfaces are bind,
2843 opnums, etc.
2844 """
2845 opnum = None
2846 opts = {}
2847 if DceRpc5Bind in pkt or DceRpc5AlterContext in pkt:
2848 # bind => get which RPC interface
2849 self.sent_cont_ids = [x.cont_id for x in pkt.context_elem]
2850 for ctx in pkt.context_elem:
2851 if_uuid = ctx.abstract_syntax.if_uuid
2852 if_version = ctx.abstract_syntax.if_version
2853 try:
2854 self.rpc_bind_interface = DCE_RPC_INTERFACES[(if_uuid, if_version)]
2855 self.rpc_bind_is_com = False
2856 except KeyError:
2857 try:
2858 self.rpc_bind_interface = COM_INTERFACES[if_uuid]
2859 self.rpc_bind_is_com = True
2860 except KeyError:
2861 self.rpc_bind_interface = None
2862 log_runtime.warning(
2863 "Unknown RPC interface %s. Try loading the IDL" % if_uuid
2864 )
2865 elif DceRpc5BindAck in pkt or DceRpc5AlterContextResp in pkt:
2866 # bind ack => is it NDR64
2867 for i, res in enumerate(pkt.results):
2868 if res.result == 0: # Accepted
2869 # Context
2870 try:
2871 self.cont_id = self.sent_cont_ids[i]
2872 except IndexError:
2873 self.cont_id = 0
2874 finally:
2875 self.sent_cont_ids = []
2877 self.assoc_group_id = pkt.assoc_group_id
2879 # Endianness
2880 self.ndrendian = {0: "big", 1: "little"}[pkt[DceRpc5].endian]
2882 # Transfer syntax
2883 if res.transfer_syntax.sprintf("%if_uuid%") == "NDR64":
2884 self.ndr64 = True
2885 elif DceRpc5Request in pkt:
2886 # request => match opnum with callID
2887 opnum = pkt.opnum
2888 uid = (self.assoc_group_id, pkt.call_id)
2889 if self.rpc_bind_is_com:
2890 self.map_callid_opnum[uid] = (
2891 opnum,
2892 pkt[DceRpc5Request].payload.payload,
2893 )
2894 else:
2895 self.map_callid_opnum[uid] = opnum, pkt[DceRpc5Request].payload
2896 elif DceRpc5Response in pkt:
2897 # response => get opnum from table
2898 uid = (self.assoc_group_id, pkt.call_id)
2899 try:
2900 opnum, opts["request_packet"] = self.map_callid_opnum[uid]
2901 del self.map_callid_opnum[uid]
2902 except KeyError:
2903 log_runtime.info("Unknown call_id %s in DCE/RPC session" % pkt.call_id)
2904 # Bind / Alter request/response specific
2905 if (
2906 DceRpc5Bind in pkt
2907 or DceRpc5AlterContext in pkt
2908 or DceRpc5BindAck in pkt
2909 or DceRpc5AlterContextResp in pkt
2910 ):
2911 # Detect if "Header Signing" is in use
2912 if pkt.pfc_flags & 0x04: # PFC_SUPPORT_HEADER_SIGN
2913 self.header_sign = True
2914 return opnum, opts
2916 # [C706] sect 12.6.2 - Fragmentation and Reassembly
2917 # Since the connection-oriented transport guarantees sequentiality, the receiver
2918 # will always receive the fragments in order.
2920 def _defragment(self, pkt, body=None):
2921 """
2922 Function to defragment DCE/RPC packets.
2923 """
2924 uid = (self.assoc_group_id, pkt.call_id)
2925 if pkt.pfc_flags.PFC_FIRST_FRAG and pkt.pfc_flags.PFC_LAST_FRAG:
2926 # Not fragmented
2927 return body
2928 if pkt.pfc_flags.PFC_FIRST_FRAG or uid in self.frags:
2929 # Packet is fragmented
2930 if body is None:
2931 body = pkt[DceRpc5].payload.payload.original
2932 self.frags[uid] += body
2933 if pkt.pfc_flags.PFC_LAST_FRAG:
2934 return self.frags[uid]
2935 else:
2936 # Not fragmented
2937 return body
2939 # C706 sect 12.5.2.15 - PDU Body Length
2940 # "The maximum PDU body size is 65528 bytes."
2941 MAX_PDU_BODY_SIZE = 4176
2943 def _fragment(self, pkt, body):
2944 """
2945 Function to fragment DCE/RPC packets.
2946 """
2947 if len(body) > self.MAX_PDU_BODY_SIZE:
2948 # Clear any PFC_*_FRAG flag
2949 pkt.pfc_flags &= 0xFC
2951 # Iterate through fragments
2952 cur = None
2953 while body:
2954 # Create a fragment
2955 pkt_frag = pkt.copy()
2957 if cur is None:
2958 # It's the first one
2959 pkt_frag.pfc_flags += "PFC_FIRST_FRAG"
2961 # Split
2962 cur, body = (
2963 body[: self.MAX_PDU_BODY_SIZE],
2964 body[self.MAX_PDU_BODY_SIZE :],
2965 )
2967 if not body:
2968 # It's the last one
2969 pkt_frag.pfc_flags += "PFC_LAST_FRAG"
2970 yield pkt_frag, cur
2971 else:
2972 yield pkt, body
2974 # [MS-RPCE] sect 3.3.1.5.2.2
2976 # The PDU header, PDU body, and sec_trailer MUST be passed in the input message, in
2977 # this order, to GSS_WrapEx, GSS_UnwrapEx, GSS_GetMICEx, and GSS_VerifyMICEx. For
2978 # integrity protection the sign flag for that PDU segment MUST be set to TRUE, else
2979 # it MUST be set to FALSE. For confidentiality protection, the conf_req_flag for
2980 # that PDU segment MUST be set to TRUE, else it MUST be set to FALSE.
2982 # If the authentication level is RPC_C_AUTHN_LEVEL_PKT_PRIVACY, the PDU body will
2983 # be encrypted.
2984 # The PDU body from the output message of GSS_UnwrapEx represents the plain text
2985 # version of the PDU body. The PDU header and sec_trailer output from the output
2986 # message SHOULD be ignored.
2987 # Similarly the signature output SHOULD be ignored.
2989 def in_pkt(self, pkt):
2990 # Check for encrypted payloads
2991 body = None
2992 if conf.raw_layer in pkt.payload:
2993 body = bytes(pkt.payload[conf.raw_layer])
2994 # If we are doing passive sniffing
2995 if conf.dcerpc_session_enable and conf.winssps_passive:
2996 # We have Windows SSPs, and no current context
2997 if pkt.auth_verifier and pkt.auth_verifier.is_ssp():
2998 # This is a bind/alter/auth3 req/resp
2999 for ssp in self.sniffsspcontexts:
3000 self.sniffsspcontexts[ssp], status = ssp.GSS_Passive(
3001 self.sniffsspcontexts[ssp],
3002 pkt.auth_verifier.auth_value,
3003 req_flags=GSS_S_FLAGS.GSS_S_ALLOW_MISSING_BINDINGS
3004 | GSS_C_FLAGS.GSS_C_DCE_STYLE,
3005 )
3006 if status == GSS_S_COMPLETE:
3007 self.auth_level = DCE_C_AUTHN_LEVEL(
3008 int(pkt.auth_verifier.auth_level)
3009 )
3010 self.ssp = ssp
3011 self.sspcontext = self.sniffsspcontexts[ssp]
3012 self.sniffsspcontexts[ssp] = None
3013 elif (
3014 self.sspcontext
3015 and pkt.auth_verifier
3016 and pkt.auth_verifier.is_protected()
3017 and body
3018 ):
3019 # This is a request/response
3020 if self.sspcontext.passive:
3021 self.ssp.GSS_Passive_set_Direction(
3022 self.sspcontext,
3023 IsAcceptor=DceRpc5Response in pkt,
3024 )
3025 if pkt.auth_verifier and pkt.auth_verifier.is_protected() and body:
3026 if self.sspcontext is None:
3027 return pkt
3028 if self.auth_level in (
3029 RPC_C_AUTHN_LEVEL.PKT_INTEGRITY,
3030 RPC_C_AUTHN_LEVEL.PKT_PRIVACY,
3031 ):
3032 # note: 'vt_trailer' is included in the pdu body
3033 # [MS-RPCE] sect 2.2.2.13
3034 # "The data structures MUST only appear in a request PDU, and they
3035 # SHOULD be placed in the PDU immediately after the stub data but
3036 # before the authentication padding octets. Therefore, for security
3037 # purposes, the verification trailer is considered part of the PDU
3038 # body."
3039 if pkt.vt_trailer:
3040 body += bytes(pkt.vt_trailer)
3041 # Account for padding when computing checksum/encryption
3042 if pkt.auth_padding:
3043 body += pkt.auth_padding
3045 # Build pdu_header and sec_trailer
3046 pdu_header = pkt.copy()
3047 sec_trailer = pdu_header.auth_verifier
3048 # sec_trailer: include the sec_trailer but not the Authentication token
3049 authval_len = len(sec_trailer.auth_value)
3050 # Discard everything out of the header
3051 pdu_header.auth_padding = None
3052 pdu_header.auth_verifier = None
3053 pdu_header.payload.payload = NoPayload()
3054 pdu_header.vt_trailer = None
3056 # [MS-RPCE] sect 2.2.2.12
3057 if self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY:
3058 _msgs = self.ssp.GSS_UnwrapEx(
3059 self.sspcontext,
3060 [
3061 # "PDU header"
3062 SSP.WRAP_MSG(
3063 conf_req_flag=False,
3064 sign=self.header_sign,
3065 data=bytes(pdu_header),
3066 ),
3067 # "PDU body"
3068 SSP.WRAP_MSG(
3069 conf_req_flag=True,
3070 sign=True,
3071 data=body,
3072 ),
3073 # "sec_trailer"
3074 SSP.WRAP_MSG(
3075 conf_req_flag=False,
3076 sign=self.header_sign,
3077 data=bytes(sec_trailer)[:-authval_len],
3078 ),
3079 ],
3080 pkt.auth_verifier.auth_value,
3081 )
3082 body = _msgs[1].data # PDU body
3083 elif self.auth_level == RPC_C_AUTHN_LEVEL.PKT_INTEGRITY:
3084 self.ssp.GSS_VerifyMICEx(
3085 self.sspcontext,
3086 [
3087 # "PDU header"
3088 SSP.MIC_MSG(
3089 sign=self.header_sign,
3090 data=bytes(pdu_header),
3091 ),
3092 # "PDU body"
3093 SSP.MIC_MSG(
3094 sign=True,
3095 data=body,
3096 ),
3097 # "sec_trailer"
3098 SSP.MIC_MSG(
3099 sign=self.header_sign,
3100 data=bytes(sec_trailer)[:-authval_len],
3101 ),
3102 ],
3103 pkt.auth_verifier.auth_value,
3104 )
3105 # Put padding back into the header
3106 if pkt.auth_padding:
3107 padlen = len(pkt.auth_padding)
3108 body, pkt.auth_padding = body[:-padlen], body[-padlen:]
3109 # Put back vt_trailer into the header, if present.
3110 if _SECTRAILER_MAGIC in body:
3111 body, pkt.vt_trailer = pkt.get_field("vt_trailer").getfield(
3112 pkt, body
3113 )
3114 # If it's a request / response, could be fragmented
3115 if isinstance(pkt.payload, (DceRpc5Request, DceRpc5Response)) and body:
3116 body = self._defragment(pkt, body)
3117 if not body:
3118 return
3119 # Get opnum and options
3120 opnum, opts = self._up_pkt(pkt)
3121 # Try to parse the payload
3122 if opnum is not None and self.rpc_bind_interface:
3123 # use opnum to parse the payload
3124 is_response = DceRpc5Response in pkt
3125 try:
3126 cls = self.rpc_bind_interface.opnums[opnum][is_response]
3127 except KeyError:
3128 log_runtime.warning(
3129 "Unknown opnum %s for interface %s"
3130 % (opnum, self.rpc_bind_interface)
3131 )
3132 pkt.payload[conf.raw_layer].load = body
3133 return pkt
3134 if body:
3135 orpc = None
3136 if self.rpc_bind_is_com:
3137 # If interface is a COM interface, start off by dissecting the
3138 # ORPCTHIS / ORPCTHAT argument
3139 from scapy.layers.msrpce.raw.ms_dcom import ORPCTHAT, ORPCTHIS
3141 # [MS-DCOM] sect 2.2.13
3142 # "ORPCTHIS and ORPCTHAT structures MUST be marshaled using
3143 # the NDR (32) Transfer Syntax"
3144 if is_response:
3145 orpc = ORPCTHAT(body, ndr64=False)
3146 else:
3147 orpc = ORPCTHIS(body, ndr64=False)
3148 body = orpc.load
3149 orpc.remove_payload()
3150 # Dissect payload using class
3151 try:
3152 payload = cls(
3153 body, ndr64=self.ndr64, ndrendian=self.ndrendian, **opts
3154 )
3155 except Exception:
3156 if conf.debug_dissector:
3157 log_runtime.error("%s dissector failed", cls.__name__)
3158 if cls is not None:
3159 raise
3160 payload = conf.raw_layer(body, _internal=1)
3161 pkt.payload[conf.raw_layer].underlayer.remove_payload()
3162 if conf.padding_layer in payload:
3163 # Most likely, dissection failed.
3164 log_runtime.warning(
3165 "Padding detected when dissecting %s. Looks wrong." % cls
3166 )
3167 pad = payload[conf.padding_layer]
3168 pad.underlayer.payload = conf.raw_layer(load=pad.load)
3169 if orpc is not None:
3170 pkt /= orpc
3171 pkt /= payload
3172 # If a request was encrypted, we need to re-register it once re-parsed.
3173 if not is_response and self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY:
3174 self._up_pkt(pkt)
3175 elif not cls.fields_desc:
3176 # Request class has no payload
3177 pkt /= cls(ndr64=self.ndr64, ndrendian=self.ndrendian, **opts)
3178 elif body:
3179 pkt.payload[conf.raw_layer].load = body
3180 return pkt
3182 def out_pkt(self, pkt):
3183 assert DceRpc5 in pkt
3184 # Register opnum and options
3185 self._up_pkt(pkt)
3187 # If it's a request / response, we can frag it
3188 if isinstance(pkt.payload, (DceRpc5Request, DceRpc5Response)):
3189 # The list of packet responses
3190 pkts = []
3191 # Take the body payload, and eventually split it
3192 body = bytes(pkt.payload.payload)
3194 for pkt, body in self._fragment(pkt, body):
3195 if pkt.auth_verifier is not None:
3196 # Verifier already set
3197 pkts.append(pkt)
3198 continue
3200 # Sign / Encrypt
3201 if self.sspcontext:
3202 signature = None
3203 if self.auth_level in (
3204 RPC_C_AUTHN_LEVEL.PKT_INTEGRITY,
3205 RPC_C_AUTHN_LEVEL.PKT_PRIVACY,
3206 ):
3207 # Remember that vt_trailer is included in the PDU
3208 if pkt.vt_trailer:
3209 body += bytes(pkt.vt_trailer)
3210 # Account for padding when computing checksum/encryption
3211 if pkt.auth_padding is None:
3212 padlen = (-len(body)) % _COMMON_AUTH_PAD # authdata padding
3213 pkt.auth_padding = b"\x00" * padlen
3214 else:
3215 padlen = len(pkt.auth_padding)
3216 # Remember that padding IS SIGNED & ENCRYPTED
3217 body += pkt.auth_padding
3218 # Add the auth_verifier
3219 pkt.auth_verifier = CommonAuthVerifier(
3220 auth_type=self.ssp.auth_type,
3221 auth_level=self.auth_level,
3222 auth_context_id=self.auth_context_id,
3223 auth_pad_length=padlen,
3224 # Note: auth_value should have the correct length because
3225 # when using PFC_SUPPORT_HEADER_SIGN, auth_len
3226 # (and frag_len) is included in the token.. but this
3227 # creates a dependency loop as you'd need to know the token
3228 # length to compute the token. Windows solves this by
3229 # setting the 'Maximum Signature Length' (or something
3230 # similar) beforehand, instead of the real length.
3231 # See `gensec_sig_size` in samba.
3232 auth_value=b"\x00"
3233 * self.ssp.MaximumSignatureLength(self.sspcontext),
3234 )
3235 # Build pdu_header and sec_trailer
3236 pdu_header = pkt.copy()
3237 pdu_header.auth_len = len(pdu_header.auth_verifier) - 8
3238 pdu_header.frag_len = len(pdu_header)
3239 sec_trailer = pdu_header.auth_verifier
3240 # sec_trailer: include the sec_trailer but not the
3241 # Authentication token
3242 authval_len = len(sec_trailer.auth_value)
3243 # sec_trailer.auth_value = None
3244 # Discard everything out of the header
3245 pdu_header.auth_padding = None
3246 pdu_header.auth_verifier = None
3247 pdu_header.payload.payload = NoPayload()
3248 pdu_header.vt_trailer = None
3249 signature = None
3250 # [MS-RPCE] sect 2.2.2.12
3251 if self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY:
3252 _msgs, signature = self.ssp.GSS_WrapEx(
3253 self.sspcontext,
3254 [
3255 # "PDU header"
3256 SSP.WRAP_MSG(
3257 conf_req_flag=False,
3258 sign=self.header_sign,
3259 data=bytes(pdu_header),
3260 ),
3261 # "PDU body"
3262 SSP.WRAP_MSG(
3263 conf_req_flag=True,
3264 sign=True,
3265 data=body,
3266 ),
3267 # "sec_trailer"
3268 SSP.WRAP_MSG(
3269 conf_req_flag=False,
3270 sign=self.header_sign,
3271 data=bytes(sec_trailer)[:-authval_len],
3272 ),
3273 ],
3274 )
3275 s = _msgs[1].data # PDU body
3276 elif self.auth_level == RPC_C_AUTHN_LEVEL.PKT_INTEGRITY:
3277 signature = self.ssp.GSS_GetMICEx(
3278 self.sspcontext,
3279 [
3280 # "PDU header"
3281 SSP.MIC_MSG(
3282 sign=self.header_sign,
3283 data=bytes(pdu_header),
3284 ),
3285 # "PDU body"
3286 SSP.MIC_MSG(
3287 sign=True,
3288 data=body,
3289 ),
3290 # "sec_trailer"
3291 SSP.MIC_MSG(
3292 sign=self.header_sign,
3293 data=bytes(sec_trailer)[:-authval_len],
3294 ),
3295 ],
3296 pkt.auth_verifier.auth_value,
3297 )
3298 s = body
3299 else:
3300 raise ValueError("Impossible")
3301 # Put padding back in the header
3302 if padlen:
3303 s, pkt.auth_padding = s[:-padlen], s[-padlen:]
3304 # Put back vt_trailer into the header
3305 if pkt.vt_trailer:
3306 vtlen = len(pkt.vt_trailer)
3307 s, pkt.vt_trailer = s[:-vtlen], s[-vtlen:]
3308 else:
3309 s = body
3311 # now inject the encrypted payload into the packet
3312 pkt.payload.payload = conf.raw_layer(load=s)
3313 # and the auth_value
3314 if signature:
3315 pkt.auth_verifier.auth_value = signature
3316 else:
3317 pkt.auth_verifier = None
3318 # Add to the list
3319 pkts.append(pkt)
3320 return pkts
3321 else:
3322 return [pkt]
3324 def process(self, pkt: Packet) -> Optional[Packet]:
3325 """
3326 Used when DceRpcSession is used for passive sniffing.
3327 """
3328 pkt = super(DceRpcSession, self).process(pkt)
3329 if pkt is not None and DceRpc5 in pkt:
3330 rpkt = self.in_pkt(pkt)
3331 if rpkt is None:
3332 # We are passively dissecting a fragmented packet. Return
3333 # just the header showing that it was indeed, fragmented.
3334 pkt[DceRpc5].payload.remove_payload()
3335 return pkt
3336 return rpkt
3337 return pkt
3340class DceRpcSocket(StreamSocket):
3341 """
3342 A Wrapper around StreamSocket that uses a DceRpcSession
3343 """
3345 def __init__(self, *args, **kwargs):
3346 self.transport = kwargs.pop("transport", None)
3347 self.session = DceRpcSession(
3348 ssp=kwargs.pop("ssp", None),
3349 auth_level=kwargs.pop("auth_level", None),
3350 support_header_signing=kwargs.pop("support_header_signing", True),
3351 )
3352 super(DceRpcSocket, self).__init__(*args, **kwargs)
3354 def send(self, x, **kwargs):
3355 for pkt in self.session.out_pkt(x):
3356 if self.transport == DCERPC_Transport.NCACN_NP:
3357 # In this case DceRpcSocket wraps a SMB_RPC_SOCKET, call it directly.
3358 self.ins.send(pkt, **kwargs)
3359 else:
3360 super(DceRpcSocket, self).send(pkt, **kwargs)
3362 def recv(self, x=None):
3363 pkt = super(DceRpcSocket, self).recv(x)
3364 if pkt is not None:
3365 return self.session.in_pkt(pkt)
3368# --- TODO cleanup below
3370# Heuristically way to find the payload class
3371#
3372# To add a possible payload to a DCE/RPC packet, one must first create the
3373# packet class, then instead of binding layers using bind_layers, he must
3374# call DceRpcPayload.register_possible_payload() with the payload class as
3375# parameter.
3376#
3377# To be able to decide if the payload class is capable of handling the rest of
3378# the dissection, the classmethod can_handle() should be implemented in the
3379# payload class. This method is given the rest of the string to dissect as
3380# first argument, and the DceRpc packet instance as second argument. Based on
3381# this information, the method must return True if the class is capable of
3382# handling the dissection, False otherwise
3385class DceRpc4Payload(Packet):
3386 """Dummy class which use the dispatch_hook to find the payload class"""
3388 _payload_class = []
3390 @classmethod
3391 def dispatch_hook(cls, _pkt, _underlayer=None, *args, **kargs):
3392 """dispatch_hook to choose among different registered payloads"""
3393 for klass in cls._payload_class:
3394 if hasattr(klass, "can_handle") and klass.can_handle(_pkt, _underlayer):
3395 return klass
3396 log_runtime.warning("DCE/RPC payload class not found or undefined (using Raw)")
3397 return Raw
3399 @classmethod
3400 def register_possible_payload(cls, pay):
3401 """Method to call from possible DCE/RPC endpoint to register it as
3402 possible payload"""
3403 cls._payload_class.append(pay)
3406bind_layers(DceRpc4, DceRpc4Payload)