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 if len(data) < 10:
968 return
969 endian = struct.unpack("!B", data[4:5])[0] >> 4
970 if endian not in [0, 1]:
971 return
972 length = struct.unpack(("<" if endian else ">") + "H", data[8:10])[0]
973 if len(data) >= length:
974 if conf.dcerpc_session_enable:
975 # If DCE/RPC sessions are enabled, use them !
976 if "dcerpcsess" not in session:
977 session["dcerpcsess"] = dcerpcsess = DceRpcSession()
978 else:
979 dcerpcsess = session["dcerpcsess"]
980 return dcerpcsess.process(DceRpc5(data))
981 return DceRpc5(data)
984# sec 12.6.3.1
987class DceRpc5AbstractSyntax(EPacket):
988 name = "Presentation Syntax (p_syntax_id_t)"
989 fields_desc = [
990 _EField(
991 UUIDEnumField(
992 "if_uuid",
993 None,
994 (
995 # Those are dynamic
996 DCE_RPC_INTERFACES_NAMES.get,
997 lambda x: DCE_RPC_INTERFACES_NAMES_rev.get(x.lower()),
998 ),
999 )
1000 ),
1001 _EField(IntField("if_version", 3)),
1002 ]
1005class DceRpc5TransferSyntax(EPacket):
1006 name = "Presentation Transfer Syntax (p_syntax_id_t)"
1007 fields_desc = [
1008 _EField(
1009 UUIDEnumField(
1010 "if_uuid",
1011 None,
1012 DCE_RPC_TRANSFER_SYNTAXES,
1013 )
1014 ),
1015 _EField(IntField("if_version", 3)),
1016 ]
1019class DceRpc5Context(EPacket):
1020 name = "Presentation Context (p_cont_elem_t)"
1021 fields_desc = [
1022 _EField(ShortField("cont_id", 0)),
1023 FieldLenField("n_transfer_syn", None, count_of="transfer_syntaxes", fmt="B"),
1024 ByteField("reserved", 0),
1025 EPacketField("abstract_syntax", None, DceRpc5AbstractSyntax),
1026 EPacketListField(
1027 "transfer_syntaxes",
1028 None,
1029 DceRpc5TransferSyntax,
1030 count_from=lambda pkt: pkt.n_transfer_syn,
1031 endianness_from=_dce_rpc_endianness,
1032 ),
1033 ]
1036class DceRpc5Result(EPacket):
1037 name = "Context negotiation Result"
1038 fields_desc = [
1039 _EField(
1040 ShortEnumField(
1041 "result", 0, ["acceptance", "user_rejection", "provider_rejection"]
1042 )
1043 ),
1044 _EField(
1045 ShortEnumField(
1046 "reason",
1047 0,
1048 _DCE_RPC_REJECTION_REASONS,
1049 )
1050 ),
1051 EPacketField("transfer_syntax", None, DceRpc5TransferSyntax),
1052 ]
1055class DceRpc5PortAny(EPacket):
1056 name = "Port Any (port_any_t)"
1057 fields_desc = [
1058 _EField(FieldLenField("length", None, length_of="port_spec", fmt="H")),
1059 _EField(StrLenField("port_spec", b"", length_from=lambda pkt: pkt.length)),
1060 ]
1063# sec 12.6.4.3
1066class DceRpc5Bind(_DceRpcPayload):
1067 name = "DCE/RPC v5 - Bind"
1068 fields_desc = [
1069 _EField(ShortField("max_xmit_frag", 5840)),
1070 _EField(ShortField("max_recv_frag", 8192)),
1071 _EField(IntField("assoc_group_id", 0)),
1072 # p_cont_list_t
1073 _EField(
1074 FieldLenField("n_context_elem", None, count_of="context_elem", fmt="B")
1075 ),
1076 StrFixedLenField("reserved", 0, length=3),
1077 EPacketListField(
1078 "context_elem",
1079 [],
1080 DceRpc5Context,
1081 endianness_from=_dce_rpc_endianness,
1082 count_from=lambda pkt: pkt.n_context_elem,
1083 ),
1084 ]
1087bind_layers(DceRpc5, DceRpc5Bind, ptype=11)
1089# sec 12.6.4.4
1092class DceRpc5BindAck(_DceRpcPayload):
1093 name = "DCE/RPC v5 - Bind Ack"
1094 fields_desc = [
1095 _EField(ShortField("max_xmit_frag", 5840)),
1096 _EField(ShortField("max_recv_frag", 8192)),
1097 _EField(IntField("assoc_group_id", 0)),
1098 PadField(
1099 EPacketField("sec_addr", None, DceRpc5PortAny),
1100 align=4,
1101 ),
1102 # p_result_list_t
1103 _EField(FieldLenField("n_results", None, count_of="results", fmt="B")),
1104 StrFixedLenField("reserved", 0, length=3),
1105 EPacketListField(
1106 "results",
1107 [],
1108 DceRpc5Result,
1109 endianness_from=_dce_rpc_endianness,
1110 count_from=lambda pkt: pkt.n_results,
1111 ),
1112 ]
1115bind_layers(DceRpc5, DceRpc5BindAck, ptype=12)
1117# sec 12.6.4.5
1120class DceRpc5Version(EPacket):
1121 name = "version_t"
1122 fields_desc = [
1123 ByteField("major", 0),
1124 ByteField("minor", 0),
1125 ]
1128class DceRpc5BindNak(_DceRpcPayload):
1129 name = "DCE/RPC v5 - Bind Nak"
1130 fields_desc = [
1131 _EField(
1132 ShortEnumField("provider_reject_reason", 0, _DCE_RPC_REJECTION_REASONS)
1133 ),
1134 # p_rt_versions_supported_t
1135 _EField(FieldLenField("n_protocols", None, count_of="protocols", fmt="B")),
1136 EPacketListField(
1137 "protocols",
1138 [],
1139 DceRpc5Version,
1140 count_from=lambda pkt: pkt.n_protocols,
1141 endianness_from=_dce_rpc_endianness,
1142 ),
1143 # [MS-RPCE] sect 2.2.2.9
1144 ConditionalField(
1145 ReversePadField(
1146 _EField(
1147 UUIDEnumField(
1148 "signature",
1149 None,
1150 {
1151 UUID(
1152 "90740320-fad0-11d3-82d7-009027b130ab"
1153 ): "Extended Error",
1154 },
1155 )
1156 ),
1157 align=8,
1158 ),
1159 lambda pkt: pkt.fields.get("signature", None)
1160 or (
1161 pkt.underlayer
1162 and pkt.n_protocols
1163 and pkt.underlayer.frag_len >= 24 + pkt.n_protocols * 2 + 16
1164 ),
1165 ),
1166 ]
1169bind_layers(DceRpc5, DceRpc5BindNak, ptype=13)
1172# sec 12.6.4.1
1175class DceRpc5AlterContext(_DceRpcPayload):
1176 name = "DCE/RPC v5 - AlterContext"
1177 fields_desc = DceRpc5Bind.fields_desc
1180bind_layers(DceRpc5, DceRpc5AlterContext, ptype=14)
1183# sec 12.6.4.2
1186class DceRpc5AlterContextResp(_DceRpcPayload):
1187 name = "DCE/RPC v5 - AlterContextResp"
1188 fields_desc = DceRpc5BindAck.fields_desc
1191bind_layers(DceRpc5, DceRpc5AlterContextResp, ptype=15)
1193# [MS-RPCE] sect 2.2.2.10 - rpc_auth_3
1196class DceRpc5Auth3(Packet):
1197 name = "DCE/RPC v5 - Auth3"
1198 fields_desc = [StrFixedLenField("pad", b"", length=4)]
1201bind_layers(DceRpc5, DceRpc5Auth3, ptype=16)
1203# sec 12.6.4.7
1206class DceRpc5Fault(_DceRpcPayload):
1207 name = "DCE/RPC v5 - Fault"
1208 fields_desc = [
1209 _EField(IntField("alloc_hint", 0)),
1210 _EField(ShortField("cont_id", 0)),
1211 ByteField("cancel_count", 0),
1212 FlagsField("reserved", 0, -8, {0x1: "RPC extended error"}),
1213 _EField(LEIntEnumField("status", 0, _DCE_RPC_ERROR_CODES)),
1214 IntField("reserved2", 0),
1215 ]
1218bind_layers(DceRpc5, DceRpc5Fault, ptype=3)
1221# sec 12.6.4.9
1224class DceRpc5Request(_DceRpcPayload):
1225 name = "DCE/RPC v5 - Request"
1226 fields_desc = [
1227 _EField(IntField("alloc_hint", 0)),
1228 _EField(ShortField("cont_id", 0)),
1229 _EField(ShortField("opnum", 0)),
1230 ConditionalField(
1231 PadField(
1232 _EField(UUIDField("object", None)),
1233 align=8,
1234 ),
1235 lambda pkt: pkt.underlayer and pkt.underlayer.pfc_flags.PFC_OBJECT_UUID,
1236 ),
1237 ]
1240bind_layers(DceRpc5, DceRpc5Request, ptype=0)
1242# sec 12.6.4.10
1245class DceRpc5Response(_DceRpcPayload):
1246 name = "DCE/RPC v5 - Response"
1247 fields_desc = [
1248 _EField(IntField("alloc_hint", 0)),
1249 _EField(ShortField("cont_id", 0)),
1250 ByteField("cancel_count", 0),
1251 ByteField("reserved", 0),
1252 ]
1255bind_layers(DceRpc5, DceRpc5Response, ptype=2)
1257# --- API
1259DceRpcOp = collections.namedtuple("DceRpcOp", ["request", "response"])
1260DCE_RPC_INTERFACES = {}
1263class DceRpcInterface:
1264 def __init__(self, name, uuid, version_tuple, if_version, opnums):
1265 self.name = name
1266 self.uuid = uuid
1267 self.major_version, self.minor_version = version_tuple
1268 self.if_version = if_version
1269 self.opnums = opnums
1271 def __repr__(self):
1272 return "<DCE/RPC Interface %s v%s.%s>" % (
1273 self.name,
1274 self.major_version,
1275 self.minor_version,
1276 )
1279def register_dcerpc_interface(name, uuid, version, opnums):
1280 """
1281 Register a DCE/RPC interface
1282 """
1283 version_tuple = tuple(map(int, version.split(".")))
1284 assert len(version_tuple) == 2, "Version should be in format 'X.X' !"
1285 if_version = (version_tuple[1] << 16) + version_tuple[0]
1286 if (uuid, if_version) in DCE_RPC_INTERFACES:
1287 # Interface is already registered.
1288 interface = DCE_RPC_INTERFACES[(uuid, if_version)]
1289 if interface.name == name:
1290 if set(opnums) - set(interface.opnums):
1291 # Interface is an extension of a previous interface
1292 interface.opnums.update(opnums)
1293 else:
1294 log_runtime.warning(
1295 "This interface is already registered: %s. Skip" % interface
1296 )
1297 return
1298 else:
1299 raise ValueError(
1300 "An interface with the same UUID is already registered: %s" % interface
1301 )
1302 else:
1303 # New interface
1304 DCE_RPC_INTERFACES_NAMES[uuid] = name
1305 DCE_RPC_INTERFACES_NAMES_rev[name.lower()] = uuid
1306 DCE_RPC_INTERFACES[(uuid, if_version)] = DceRpcInterface(
1307 name,
1308 uuid,
1309 version_tuple,
1310 if_version,
1311 opnums,
1312 )
1314 # bind for build
1315 for opnum, operations in opnums.items():
1316 bind_top_down(DceRpc5Request, operations.request, opnum=opnum)
1317 operations.request.opnum = opnum
1318 operations.request.intf = uuid
1321def find_dcerpc_interface(name) -> DceRpcInterface:
1322 """
1323 Find an interface object through the name in the IDL
1324 """
1325 try:
1326 return next(x for x in DCE_RPC_INTERFACES.values() if x.name == name)
1327 except StopIteration:
1328 raise AttributeError("Unknown interface !")
1331COM_INTERFACES = {}
1334class ComInterface:
1335 if_version = 0
1337 def __init__(self, name, uuid, opnums):
1338 self.name = name
1339 self.uuid = uuid
1340 self.opnums = opnums
1342 def __repr__(self):
1343 return "<COM Interface %s>" % (self.name,)
1346def register_com_interface(name, uuid, opnums):
1347 """
1348 Register a COM interface
1349 """
1350 COM_INTERFACES[uuid] = ComInterface(
1351 name,
1352 uuid,
1353 opnums,
1354 )
1355 # bind for build
1356 for opnum, operations in opnums.items():
1357 bind_top_down(DceRpc5Request, operations.request, opnum=opnum)
1358 COM_INTERFACES_NAMES[uuid] = name
1359 COM_INTERFACES_NAMES_rev[name.lower()] = uuid
1362def find_com_interface(name) -> ComInterface:
1363 """
1364 Find an interface object through the name in the IDL
1365 """
1366 try:
1367 return next(x for x in COM_INTERFACES.values() if x.name == name)
1368 except StopIteration:
1369 raise AttributeError("Unknown interface !")
1372# --- NDR fields - [C706] chap 14
1375def _set_ctx_on(f, obj):
1376 if isinstance(f, _NDRPacket):
1377 f.ndr64 = obj.ndr64
1378 f.ndrendian = obj.ndrendian
1379 if isinstance(f, list):
1380 for x in f:
1381 if isinstance(x, _NDRPacket):
1382 x.ndr64 = obj.ndr64
1383 x.ndrendian = obj.ndrendian
1386def _e(ndrendian):
1387 return {"big": ">", "little": "<"}[ndrendian]
1390class _NDRPacket(Packet):
1391 __slots__ = ["ndr64", "ndrendian", "deferred_pointers", "request_packet"]
1393 def __init__(self, *args, **kwargs):
1394 self.ndr64 = kwargs.pop("ndr64", conf.ndr64)
1395 self.ndrendian = kwargs.pop("ndrendian", "little")
1396 # request_packet is used in the session, so that a response packet
1397 # can resolve union arms if the case parameter is in the request.
1398 self.request_packet = kwargs.pop("request_packet", None)
1399 self.deferred_pointers = []
1400 super(_NDRPacket, self).__init__(*args, **kwargs)
1402 def do_dissect(self, s):
1403 _up = self.parent or self.underlayer
1404 if _up and isinstance(_up, _NDRPacket):
1405 self.ndr64 = _up.ndr64
1406 self.ndrendian = _up.ndrendian
1407 else:
1408 # See comment above NDRConstructedType
1409 return NDRConstructedType([]).read_deferred_pointers(
1410 self, super(_NDRPacket, self).do_dissect(s)
1411 )
1412 return super(_NDRPacket, self).do_dissect(s)
1414 def post_dissect(self, s):
1415 if self.deferred_pointers:
1416 # Can't trust the cache if there were deferred pointers
1417 self.raw_packet_cache = None
1418 return s
1420 def do_build(self):
1421 _up = self.parent or self.underlayer
1422 for f in self.fields.values():
1423 _set_ctx_on(f, self)
1424 if not _up or not isinstance(_up, _NDRPacket):
1425 # See comment above NDRConstructedType
1426 return NDRConstructedType([]).add_deferred_pointers(
1427 self, super(_NDRPacket, self).do_build()
1428 )
1429 return super(_NDRPacket, self).do_build()
1431 def default_payload_class(self, pkt):
1432 return conf.padding_layer
1434 def clone_with(self, *args, **kwargs):
1435 pkt = super(_NDRPacket, self).clone_with(*args, **kwargs)
1436 # We need to copy deferred_pointers to not break pointer deferral
1437 # on build.
1438 pkt.deferred_pointers = self.deferred_pointers
1439 pkt.ndr64 = self.ndr64
1440 pkt.ndrendian = self.ndrendian
1441 return pkt
1443 def copy(self):
1444 pkt = super(_NDRPacket, self).copy()
1445 pkt.deferred_pointers = self.deferred_pointers
1446 pkt.ndr64 = self.ndr64
1447 pkt.ndrendian = self.ndrendian
1448 return pkt
1450 def show2(self, dump=False, indent=3, lvl="", label_lvl=""):
1451 return self.__class__(
1452 bytes(self), ndr64=self.ndr64, ndrendian=self.ndrendian
1453 ).show(dump, indent, lvl, label_lvl)
1455 def getfield_and_val(self, attr):
1456 try:
1457 return Packet.getfield_and_val(self, attr)
1458 except ValueError:
1459 if self.request_packet:
1460 # Try to resolve the field from the request on failure
1461 try:
1462 return self.request_packet.getfield_and_val(attr)
1463 except AttributeError:
1464 pass
1465 raise
1467 def valueof(self, request: str):
1468 """
1469 Util to get the value of a NDRField, ignoring arrays, pointers, etc.
1470 """
1471 val = self
1472 for ndr_field in request.split("."):
1473 fld, fval = val.getfield_and_val(ndr_field)
1474 val = fld.valueof(val, fval)
1475 return val
1478class _NDRAlign:
1479 def padlen(self, flen, pkt):
1480 return -flen % self._align[pkt.ndr64]
1482 def original_length(self, pkt):
1483 # Find the length of the NDR frag to be able to pad properly
1484 while pkt:
1485 par = pkt.parent or pkt.underlayer
1486 if par and isinstance(par, _NDRPacket):
1487 pkt = par
1488 else:
1489 break
1490 return len(pkt.original)
1493class NDRAlign(_NDRAlign, ReversePadField):
1494 """
1495 ReversePadField modified to fit NDR.
1497 - If no align size is specified, use the one from the inner field
1498 - Size is calculated from the beginning of the NDR stream
1499 """
1501 def __init__(self, fld, align, padwith=None):
1502 super(NDRAlign, self).__init__(fld, align=align, padwith=padwith)
1505class _VirtualField(Field):
1506 # Hold a value but doesn't show up when building/dissecting
1507 def addfield(self, pkt, s, x):
1508 return s
1510 def getfield(self, pkt, s):
1511 return s, None
1514class _NDRPacketMetaclass(Packet_metaclass):
1515 def __new__(cls, name, bases, dct):
1516 newcls = super(_NDRPacketMetaclass, cls).__new__(cls, name, bases, dct)
1517 conformants = dct.get("DEPORTED_CONFORMANTS", [])
1518 if conformants:
1519 amount = len(conformants)
1520 if amount == 1:
1521 newcls.fields_desc.insert(
1522 0,
1523 _VirtualField("max_count", None),
1524 )
1525 else:
1526 newcls.fields_desc.insert(
1527 0,
1528 FieldListField(
1529 "max_counts",
1530 [],
1531 _VirtualField("", 0),
1532 count_from=lambda _: amount,
1533 ),
1534 )
1535 return newcls # type: ignore
1538class NDRPacket(_NDRPacket, metaclass=_NDRPacketMetaclass):
1539 """
1540 A NDR Packet. Handles pointer size & endianness
1541 """
1543 __slots__ = ["_align"]
1545 # NDR64 pad structures
1546 # [MS-RPCE] 2.2.5.3.4.1
1547 ALIGNMENT = (1, 1)
1548 # [C706] sect 14.3.7 - Conformants max_count can be added to the beginning
1549 DEPORTED_CONFORMANTS = []
1552# Primitive types
1555class _NDRValueOf:
1556 def valueof(self, pkt, x):
1557 return x
1560class _NDRLenField(_NDRValueOf, Field):
1561 """
1562 Field similar to FieldLenField that takes size_of and adjust as arguments,
1563 and take the value of a size on build.
1564 """
1566 __slots__ = ["size_of", "adjust"]
1568 def __init__(self, *args, **kwargs):
1569 self.size_of = kwargs.pop("size_of", None)
1570 self.adjust = kwargs.pop("adjust", lambda _, x: x)
1571 super(_NDRLenField, self).__init__(*args, **kwargs)
1573 def i2m(self, pkt, x):
1574 if x is None and pkt is not None and self.size_of is not None:
1575 fld, fval = pkt.getfield_and_val(self.size_of)
1576 f = fld.i2len(pkt, fval)
1577 x = self.adjust(pkt, f)
1578 elif x is None:
1579 x = 0
1580 return x
1583class NDRByteField(_NDRLenField, ByteField):
1584 pass
1587class NDRSignedByteField(_NDRLenField, SignedByteField):
1588 pass
1591class _NDRField(_NDRLenField):
1592 FMT = ""
1593 ALIGN = (0, 0)
1595 def getfield(self, pkt, s):
1596 return NDRAlign(
1597 Field("", 0, fmt=_e(pkt.ndrendian) + self.FMT), align=self.ALIGN
1598 ).getfield(pkt, s)
1600 def addfield(self, pkt, s, val):
1601 return NDRAlign(
1602 Field("", 0, fmt=_e(pkt.ndrendian) + self.FMT), align=self.ALIGN
1603 ).addfield(pkt, s, self.i2m(pkt, val))
1606class NDRShortField(_NDRField):
1607 FMT = "H"
1608 ALIGN = (2, 2)
1611class NDRSignedShortField(_NDRField):
1612 FMT = "h"
1613 ALIGN = (2, 2)
1616class NDRIntField(_NDRField):
1617 FMT = "I"
1618 ALIGN = (4, 4)
1621class NDRSignedIntField(_NDRField):
1622 FMT = "i"
1623 ALIGN = (4, 4)
1626class NDRLongField(_NDRField):
1627 FMT = "Q"
1628 ALIGN = (8, 8)
1631class NDRSignedLongField(_NDRField):
1632 FMT = "q"
1633 ALIGN = (8, 8)
1636class NDRIEEEFloatField(_NDRField):
1637 FMT = "f"
1638 ALIGN = (4, 4)
1641class NDRIEEEDoubleField(_NDRField):
1642 FMT = "d"
1643 ALIGN = (8, 8)
1646# Enum types
1649class _NDREnumField(_NDRValueOf, EnumField):
1650 # [MS-RPCE] sect 2.2.5.2 - Enums are 4 octets in NDR64
1651 FMTS = ["H", "I"]
1653 def getfield(self, pkt, s):
1654 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1655 return NDRAlign(Field("", 0, fmt=fmt), align=(2, 4)).getfield(pkt, s)
1657 def addfield(self, pkt, s, val):
1658 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1659 return NDRAlign(Field("", 0, fmt=fmt), align=(2, 4)).addfield(
1660 pkt, s, self.i2m(pkt, val)
1661 )
1664class NDRInt3264EnumField(NDRAlign):
1665 def __init__(self, *args, **kwargs):
1666 super(NDRInt3264EnumField, self).__init__(
1667 _NDREnumField(*args, **kwargs), align=(2, 4)
1668 )
1671class NDRIntEnumField(_NDRValueOf, NDRAlign):
1672 # v1_enum are always 4-octets, even in NDR32
1673 def __init__(self, *args, **kwargs):
1674 super(NDRIntEnumField, self).__init__(
1675 LEIntEnumField(*args, **kwargs), align=(4, 4)
1676 )
1679# Special types
1682class NDRInt3264Field(_NDRLenField):
1683 FMTS = ["I", "Q"]
1685 def getfield(self, pkt, s):
1686 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1687 return NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(pkt, s)
1689 def addfield(self, pkt, s, val):
1690 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1691 return NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(
1692 pkt, s, self.i2m(pkt, val)
1693 )
1696class NDRSignedInt3264Field(NDRInt3264Field):
1697 FMTS = ["i", "q"]
1700# Pointer types
1703class NDRPointer(_NDRPacket):
1704 fields_desc = [
1705 MultipleTypeField(
1706 [(XLELongField("referent_id", 1), lambda pkt: pkt and pkt.ndr64)],
1707 XLEIntField("referent_id", 1),
1708 ),
1709 PacketField("value", None, conf.raw_layer),
1710 ]
1713class NDRFullPointerField(_FieldContainer):
1714 """
1715 A NDR Full/Unique pointer field encapsulation.
1717 :param EMBEDDED: This pointer is embedded. This means that it's representation
1718 will not appear after the pointer (pointer deferral applies).
1719 See [C706] 14.3.12.3 - Algorithm for Deferral of Referents
1720 """
1722 EMBEDDED = False
1723 EMBEDDED_REF = False
1725 def __init__(self, fld, ref=False, fmt="I"):
1726 self.fld = fld
1727 self.ref = ref
1728 self.default = None
1730 def getfield(self, pkt, s):
1731 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
1732 remain, referent_id = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(
1733 pkt, s
1734 )
1736 # No value
1737 if referent_id == 0 and not self.EMBEDDED_REF:
1738 return remain, None
1740 # With value
1741 if self.EMBEDDED:
1742 # deferred
1743 ptr = NDRPointer(
1744 ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, referent_id=referent_id
1745 )
1746 pkt.deferred_pointers.append((ptr, partial(self.fld.getfield, pkt)))
1747 return remain, ptr
1749 remain, val = self.fld.getfield(pkt, remain)
1750 return remain, NDRPointer(
1751 ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, referent_id=referent_id, value=val
1752 )
1754 def addfield(self, pkt, s, val):
1755 if val is not None and not isinstance(val, NDRPointer):
1756 raise ValueError(
1757 "Expected NDRPointer in %s. You are using it wrong!" % self.name
1758 )
1759 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
1760 fld = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8))
1762 # No value
1763 if val is None and not self.EMBEDDED_REF:
1764 return fld.addfield(pkt, s, 0)
1766 # With value
1767 _set_ctx_on(val.value, pkt)
1768 s = fld.addfield(pkt, s, val.referent_id)
1769 if self.EMBEDDED:
1770 # deferred
1771 pkt.deferred_pointers.append(
1772 ((lambda s: self.fld.addfield(pkt, s, val.value)), val)
1773 )
1774 return s
1776 return self.fld.addfield(pkt, s, val.value)
1778 def any2i(self, pkt, x):
1779 # User-friendly helper
1780 if x is not None and not isinstance(x, NDRPointer):
1781 return NDRPointer(
1782 referent_id=0x20000,
1783 value=self.fld.any2i(pkt, x),
1784 )
1785 return x
1787 # Can't use i2repr = Field.i2repr and so on on PY2 :/
1788 def i2repr(self, pkt, val):
1789 return repr(val)
1791 def i2h(self, pkt, x):
1792 return x
1794 def h2i(self, pkt, x):
1795 return x
1797 def i2len(self, pkt, x):
1798 if x is None:
1799 return 0
1800 return self.fld.i2len(pkt, x.value)
1802 def valueof(self, pkt, x):
1803 if x is None:
1804 return x
1805 return self.fld.valueof(pkt, x.value)
1808class NDRFullEmbPointerField(NDRFullPointerField):
1809 """
1810 A NDR Embedded Full pointer.
1812 Same as NDRFullPointerField with EMBEDDED = True.
1813 """
1815 EMBEDDED = True
1818class NDRRefEmbPointerField(NDRFullPointerField):
1819 """
1820 A NDR Embedded Reference pointer.
1822 Same as NDRFullPointerField with EMBEDDED = True and EMBEDDED_REF = True.
1823 """
1825 EMBEDDED = True
1826 EMBEDDED_REF = True
1829# Constructed types
1832# Note: this is utterly complex and will drive you crazy
1834# If you have a NDRPacket that contains a deferred pointer on the top level
1835# (only happens in non DCE/RPC structures, such as in MS-PAC, where you have an NDR
1836# structure encapsulated in a non-NDR structure), there will be left-over deferred
1837# pointers when exiting dissection/build (deferred pointers are only computed when
1838# reaching a field that extends NDRConstructedType, which is normal: if you follow
1839# the DCE/RPC spec, pointers are never deferred in root structures)
1840# Therefore there is a special case forcing the build/dissection of any leftover
1841# pointers in NDRPacket, if Scapy detects that they won't be handled by any parent.
1843# Implementation notes: I chose to set 'handles_deferred' inside the FIELD, rather
1844# than inside the PACKET. This is faster to compute because whether a constructed type
1845# should handle deferral or not is computed only once when loading, therefore Scapy
1846# knows in advance whether to handle deferred pointers or not. But it is technically
1847# incorrect: with this approach, a structure (packet) cannot be used in 2 code paths
1848# that have different pointer managements. I mean by that that if there was a
1849# structure that was directly embedded in a RPC request without a pointer but also
1850# embedded with a pointer in another RPC request, it would break.
1851# Fortunately this isn't the case: structures are never reused for 2 purposes.
1852# (or at least I never seen that... <i hope this works>)
1855class NDRConstructedType(object):
1856 def __init__(self, fields):
1857 self.handles_deferred = False
1858 self.ndr_fields = fields
1859 self.rec_check_deferral()
1861 def rec_check_deferral(self):
1862 # We iterate through the fields within this constructed type.
1863 # If we have a pointer, mark this field as handling deferrance
1864 # and make all sub-constructed types not.
1865 for f in self.ndr_fields:
1866 if isinstance(f, NDRFullPointerField) and f.EMBEDDED:
1867 self.handles_deferred = True
1868 if isinstance(f, NDRConstructedType):
1869 f.rec_check_deferral()
1870 if f.handles_deferred:
1871 self.handles_deferred = True
1872 f.handles_deferred = False
1874 def getfield(self, pkt, s):
1875 s, fval = super(NDRConstructedType, self).getfield(pkt, s)
1876 if isinstance(fval, _NDRPacket):
1877 # If a sub-packet we just dissected has deferred pointers,
1878 # pass it to parent packet to propagate.
1879 pkt.deferred_pointers.extend(fval.deferred_pointers)
1880 del fval.deferred_pointers[:]
1881 if self.handles_deferred:
1882 # This field handles deferral !
1883 s = self.read_deferred_pointers(pkt, s)
1884 return s, fval
1886 def read_deferred_pointers(self, pkt, s):
1887 # Now read content of the pointers that were deferred
1888 q = collections.deque()
1889 q.extend(pkt.deferred_pointers)
1890 del pkt.deferred_pointers[:]
1891 while q:
1892 # Recursively resolve pointers that were deferred
1893 ptr, getfld = q.popleft()
1894 s, val = getfld(s)
1895 ptr.value = val
1896 if isinstance(val, _NDRPacket):
1897 # Pointer resolves to a packet.. that may have deferred pointers?
1898 q.extend(val.deferred_pointers)
1899 del val.deferred_pointers[:]
1900 return s
1902 def addfield(self, pkt, s, val):
1903 try:
1904 s = super(NDRConstructedType, self).addfield(pkt, s, val)
1905 except Exception as ex:
1906 try:
1907 ex.args = (
1908 "While building field '%s': " % self.name + ex.args[0],
1909 ) + ex.args[1:]
1910 except (AttributeError, IndexError):
1911 pass
1912 raise ex
1913 if isinstance(val, _NDRPacket):
1914 # If a sub-packet we just dissected has deferred pointers,
1915 # pass it to parent packet to propagate.
1916 pkt.deferred_pointers.extend(val.deferred_pointers)
1917 del val.deferred_pointers[:]
1918 if self.handles_deferred:
1919 # This field handles deferral !
1920 s = self.add_deferred_pointers(pkt, s)
1921 return s
1923 def add_deferred_pointers(self, pkt, s):
1924 # Now add content of pointers that were deferred
1925 q = collections.deque()
1926 q.extend(pkt.deferred_pointers)
1927 del pkt.deferred_pointers[:]
1928 while q:
1929 addfld, fval = q.popleft()
1930 s = addfld(s)
1931 if isinstance(fval, NDRPointer) and isinstance(fval.value, _NDRPacket):
1932 q.extend(fval.value.deferred_pointers)
1933 del fval.value.deferred_pointers[:]
1934 return s
1937class _NDRPacketField(_NDRValueOf, PacketField):
1938 def m2i(self, pkt, m):
1939 return self.cls(m, ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, _parent=pkt)
1942class _NDRPacketPadField(PadField):
1943 # [MS-RPCE] 2.2.5.3.4.1 Structure with Trailing Gap
1944 # Structures have extra alignment/padding in NDR64.
1945 def padlen(self, flen, pkt):
1946 if pkt.ndr64:
1947 return -flen % self._align[1]
1948 else:
1949 return 0
1952class NDRPacketField(NDRConstructedType, NDRAlign):
1953 def __init__(self, name, default, pkt_cls, **kwargs):
1954 self.DEPORTED_CONFORMANTS = pkt_cls.DEPORTED_CONFORMANTS
1955 self.fld = _NDRPacketField(name, default, pkt_cls=pkt_cls, **kwargs)
1957 # The inner _NDRPacketPadField handles NDR64's trailing gap in
1958 # the case where there a no inner conformants (see [MS-RPCE] 2.2.5.3.4.1)
1959 if self.DEPORTED_CONFORMANTS:
1960 innerfld = self.fld
1961 else:
1962 innerfld = _NDRPacketPadField(self.fld, align=pkt_cls.ALIGNMENT)
1964 # C706 14.3.2 Alignment of Constructed Types is handled by the
1965 # NDRAlign below.
1966 NDRAlign.__init__(
1967 self,
1968 innerfld,
1969 align=pkt_cls.ALIGNMENT,
1970 )
1971 NDRConstructedType.__init__(self, pkt_cls.fields_desc)
1973 def getfield(self, pkt, x):
1974 # Handle deformed conformants max_count here
1975 if self.DEPORTED_CONFORMANTS:
1976 # C706 14.3.2: "In other words, the size information precedes the
1977 # structure and is aligned independently of the structure alignment."
1978 fld = NDRInt3264Field("", 0)
1979 max_counts = []
1980 for _ in self.DEPORTED_CONFORMANTS:
1981 x, max_count = fld.getfield(pkt, x)
1982 max_counts.append(max_count)
1983 res, val = super(NDRPacketField, self).getfield(pkt, x)
1984 if len(max_counts) == 1:
1985 val.max_count = max_counts[0]
1986 else:
1987 val.max_counts = max_counts
1988 return res, val
1989 return super(NDRPacketField, self).getfield(pkt, x)
1991 def addfield(self, pkt, s, x):
1992 # Handle deformed conformants max_count here
1993 if self.DEPORTED_CONFORMANTS:
1994 mcfld = NDRInt3264Field("", 0)
1995 if len(self.DEPORTED_CONFORMANTS) == 1:
1996 max_counts = [x.max_count]
1997 else:
1998 max_counts = x.max_counts
1999 for fldname, max_count in zip(self.DEPORTED_CONFORMANTS, max_counts):
2000 if max_count is None:
2001 fld, val = x.getfield_and_val(fldname)
2002 max_count = fld.i2len(x, val)
2003 s = mcfld.addfield(pkt, s, max_count)
2004 return super(NDRPacketField, self).addfield(pkt, s, x)
2005 return super(NDRPacketField, self).addfield(pkt, s, x)
2008# Array types
2011class _NDRPacketListField(NDRConstructedType, PacketListField):
2012 """
2013 A PacketListField for NDR that can optionally pack the packets into NDRPointers
2014 """
2016 islist = 1
2017 holds_packets = 1
2019 __slots__ = ["ptr_lvl", "fld"]
2021 def __init__(self, name, default, pkt_cls, **kwargs):
2022 self.ptr_lvl = kwargs.pop("ptr_lvl", False)
2023 if self.ptr_lvl:
2024 # TODO: support more than 1 level ?
2025 self.fld = NDRFullEmbPointerField(NDRPacketField("", None, pkt_cls))
2026 else:
2027 self.fld = NDRPacketField("", None, pkt_cls)
2028 PacketListField.__init__(self, name, default, pkt_cls=pkt_cls, **kwargs)
2029 NDRConstructedType.__init__(self, [self.fld])
2031 def m2i(self, pkt, s):
2032 remain, val = self.fld.getfield(pkt, s)
2033 if val is None:
2034 val = NDRNone()
2035 # A mistake here would be to use / instead of add_payload. It adds a copy
2036 # which breaks pointer defferal. Same applies elsewhere
2037 val.add_payload(conf.padding_layer(remain))
2038 return val
2040 def any2i(self, pkt, x):
2041 # User-friendly helper
2042 if isinstance(x, list):
2043 x = [self.fld.any2i(pkt, y) for y in x]
2044 return super(_NDRPacketListField, self).any2i(pkt, x)
2046 def i2m(self, pkt, val):
2047 return self.fld.addfield(pkt, b"", val)
2049 def i2len(self, pkt, x):
2050 return len(x)
2052 def valueof(self, pkt, x):
2053 return [
2054 self.fld.valueof(pkt, y if not isinstance(y, NDRNone) else None) for y in x
2055 ]
2058class NDRFieldListField(NDRConstructedType, FieldListField):
2059 """
2060 A FieldListField for NDR
2061 """
2063 islist = 1
2065 def __init__(self, *args, **kwargs):
2066 if "length_is" in kwargs:
2067 kwargs["count_from"] = kwargs.pop("length_is")
2068 elif "size_is" in kwargs:
2069 kwargs["count_from"] = kwargs.pop("size_is")
2070 FieldListField.__init__(self, *args, **kwargs)
2071 NDRConstructedType.__init__(self, [self.field])
2073 def i2len(self, pkt, x):
2074 return len(x)
2076 def valueof(self, pkt, x):
2077 return [self.field.valueof(pkt, y) for y in x]
2080class NDRVaryingArray(_NDRPacket):
2081 fields_desc = [
2082 MultipleTypeField(
2083 [(LELongField("offset", 0), lambda pkt: pkt and pkt.ndr64)],
2084 LEIntField("offset", 0),
2085 ),
2086 MultipleTypeField(
2087 [
2088 (
2089 LELongField("actual_count", None),
2090 lambda pkt: pkt and pkt.ndr64,
2091 )
2092 ],
2093 LEIntField("actual_count", None),
2094 ),
2095 PacketField("value", None, conf.raw_layer),
2096 ]
2099class _NDRVarField:
2100 """
2101 NDR Varying Array / String field
2102 """
2104 LENGTH_FROM = False
2105 COUNT_FROM = False
2107 def __init__(self, *args, **kwargs):
2108 # We build the length_is function by taking into account both the
2109 # actual_count (from the varying field) and a potentially provided
2110 # length_is field.
2111 if "length_is" in kwargs:
2112 _length_is = kwargs.pop("length_is")
2113 length_is = lambda pkt: (_length_is(pkt.underlayer) or pkt.actual_count)
2114 else:
2115 length_is = lambda pkt: pkt.actual_count
2116 # Pass it to the sub-field (actually subclass)
2117 if self.LENGTH_FROM:
2118 kwargs["length_from"] = length_is
2119 elif self.COUNT_FROM:
2120 kwargs["count_from"] = length_is
2121 # TODO: For now, we do nothing with max_is
2122 if "max_is" in kwargs:
2123 kwargs.pop("max_is")
2124 super(_NDRVarField, self).__init__(*args, **kwargs)
2126 def getfield(self, pkt, s):
2127 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2128 remain, offset = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(pkt, s)
2129 remain, actual_count = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(
2130 pkt, remain
2131 )
2132 final = NDRVaryingArray(
2133 ndr64=pkt.ndr64,
2134 ndrendian=pkt.ndrendian,
2135 offset=offset,
2136 actual_count=actual_count,
2137 _underlayer=pkt,
2138 )
2139 remain, val = super(_NDRVarField, self).getfield(final, remain)
2140 final.value = super(_NDRVarField, self).i2h(pkt, val)
2141 return remain, final
2143 def addfield(self, pkt, s, val):
2144 if not isinstance(val, NDRVaryingArray):
2145 raise ValueError(
2146 "Expected NDRVaryingArray in %s. You are using it wrong!" % self.name
2147 )
2148 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2149 _set_ctx_on(val.value, pkt)
2150 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(pkt, s, val.offset)
2151 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(
2152 pkt,
2153 s,
2154 val.actual_count is None
2155 and super(_NDRVarField, self).i2len(pkt, val.value)
2156 or val.actual_count,
2157 )
2158 return super(_NDRVarField, self).addfield(
2159 pkt, s, super(_NDRVarField, self).h2i(pkt, val.value)
2160 )
2162 def i2len(self, pkt, x):
2163 return super(_NDRVarField, self).i2len(pkt, x.value)
2165 def any2i(self, pkt, x):
2166 # User-friendly helper
2167 if not isinstance(x, NDRVaryingArray):
2168 return NDRVaryingArray(
2169 value=super(_NDRVarField, self).any2i(pkt, x),
2170 )
2171 return x
2173 # Can't use i2repr = Field.i2repr and so on on PY2 :/
2174 def i2repr(self, pkt, val):
2175 return repr(val)
2177 def i2h(self, pkt, x):
2178 return x
2180 def h2i(self, pkt, x):
2181 return x
2183 def valueof(self, pkt, x):
2184 return super(_NDRVarField, self).valueof(pkt, x.value)
2187class NDRConformantArray(_NDRPacket):
2188 fields_desc = [
2189 MultipleTypeField(
2190 [(LELongField("max_count", None), lambda pkt: pkt and pkt.ndr64)],
2191 LEIntField("max_count", None),
2192 ),
2193 MultipleTypeField(
2194 [
2195 (
2196 PacketListField(
2197 "value",
2198 [],
2199 conf.raw_layer,
2200 count_from=lambda pkt: pkt.max_count,
2201 ),
2202 (
2203 lambda pkt: pkt.fields.get("value", None)
2204 and isinstance(pkt.fields["value"][0], Packet),
2205 lambda _, val: val and isinstance(val[0], Packet),
2206 ),
2207 )
2208 ],
2209 FieldListField(
2210 "value", [], LEIntField("", 0), count_from=lambda pkt: pkt.max_count
2211 ),
2212 ),
2213 ]
2216class NDRConformantString(_NDRPacket):
2217 fields_desc = [
2218 MultipleTypeField(
2219 [(LELongField("max_count", None), lambda pkt: pkt and pkt.ndr64)],
2220 LEIntField("max_count", None),
2221 ),
2222 StrField("value", ""),
2223 ]
2226class _NDRConfField:
2227 """
2228 NDR Conformant Array / String field
2229 """
2231 CONFORMANT_STRING = False
2232 LENGTH_FROM = False
2233 COUNT_FROM = False
2235 def __init__(self, *args, **kwargs):
2236 # when conformant_in_struct is True, we remove the level of abstraction
2237 # provided by NDRConformantString / NDRConformantArray because max_count
2238 # is a proper field in the parent packet.
2239 self.conformant_in_struct = kwargs.pop("conformant_in_struct", False)
2240 # size_is/max_is end up here, and is what defines a conformant field.
2241 if "size_is" in kwargs:
2242 size_is = kwargs.pop("size_is")
2243 if self.LENGTH_FROM:
2244 kwargs["length_from"] = size_is
2245 elif self.COUNT_FROM:
2246 kwargs["count_from"] = size_is
2247 # TODO: For now, we do nothing with max_is
2248 if "max_is" in kwargs:
2249 kwargs.pop("max_is")
2250 super(_NDRConfField, self).__init__(*args, **kwargs)
2252 def getfield(self, pkt, s):
2253 # [C706] - 14.3.7 Structures Containing Arrays
2254 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2255 if self.conformant_in_struct:
2256 # [MS-RPCE] 2.2.5.3.4.2 Structure Containing a Conformant Array
2257 # Padding is here: just before the Conformant content
2258 return NDRAlign(
2259 super(_NDRConfField, self),
2260 align=pkt.ALIGNMENT,
2261 ).getfield(pkt, s)
2263 # The max count is aligned as a primitive type
2264 remain, max_count = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(
2265 pkt, s
2266 )
2267 remain, val = super(_NDRConfField, self).getfield(pkt, remain)
2268 return remain, (
2269 NDRConformantString if self.CONFORMANT_STRING else NDRConformantArray
2270 )(ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, max_count=max_count, value=val)
2272 def addfield(self, pkt, s, val):
2273 if self.conformant_in_struct:
2274 # [MS-RPCE] 2.2.5.3.4.2 Structure Containing a Conformant Array
2275 # Padding is here: just before the Conformant content
2276 return NDRAlign(super(_NDRConfField, self), align=pkt.ALIGNMENT).addfield(
2277 pkt, s, val
2278 )
2280 if self.CONFORMANT_STRING and not isinstance(val, NDRConformantString):
2281 raise ValueError(
2282 "Expected NDRConformantString in %s. You are using it wrong!"
2283 % self.name
2284 )
2285 elif not self.CONFORMANT_STRING and not isinstance(val, NDRConformantArray):
2286 raise ValueError(
2287 "Expected NDRConformantArray in %s. You are using it wrong!" % self.name
2288 )
2289 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2290 _set_ctx_on(val.value, pkt)
2291 if val.value and isinstance(val.value[0], NDRVaryingArray):
2292 value = val.value[0]
2293 else:
2294 value = val.value
2295 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(
2296 pkt,
2297 s,
2298 val.max_count is None
2299 and super(_NDRConfField, self).i2len(pkt, value)
2300 or val.max_count,
2301 )
2302 return super(_NDRConfField, self).addfield(pkt, s, value)
2304 def _subval(self, x):
2305 if self.conformant_in_struct:
2306 value = x
2307 elif (
2308 not self.CONFORMANT_STRING
2309 and x.value
2310 and isinstance(x.value[0], NDRVaryingArray)
2311 ):
2312 value = x.value[0]
2313 else:
2314 value = x.value
2315 return value
2317 def i2len(self, pkt, x):
2318 return super(_NDRConfField, self).i2len(pkt, self._subval(x))
2320 def any2i(self, pkt, x):
2321 # User-friendly helper
2322 if self.conformant_in_struct:
2323 return super(_NDRConfField, self).any2i(pkt, x)
2324 if self.CONFORMANT_STRING and not isinstance(x, NDRConformantString):
2325 return NDRConformantString(
2326 value=super(_NDRConfField, self).any2i(pkt, x),
2327 )
2328 elif not isinstance(x, NDRConformantArray):
2329 return NDRConformantArray(
2330 value=super(_NDRConfField, self).any2i(pkt, x),
2331 )
2332 return x
2334 # Can't use i2repr = Field.i2repr and so on on PY2 :/
2335 def i2repr(self, pkt, val):
2336 return repr(val)
2338 def i2h(self, pkt, x):
2339 return x
2341 def h2i(self, pkt, x):
2342 return x
2344 def valueof(self, pkt, x):
2345 return super(_NDRConfField, self).valueof(pkt, self._subval(x))
2348class NDRVarPacketListField(_NDRVarField, _NDRPacketListField):
2349 """
2350 NDR Varying PacketListField. Unused
2351 """
2353 COUNT_FROM = True
2356class NDRConfPacketListField(_NDRConfField, _NDRPacketListField):
2357 """
2358 NDR Conformant PacketListField
2359 """
2361 COUNT_FROM = True
2364class NDRConfVarPacketListField(_NDRConfField, _NDRVarField, _NDRPacketListField):
2365 """
2366 NDR Conformant Varying PacketListField
2367 """
2369 COUNT_FROM = True
2372class NDRConfFieldListField(_NDRConfField, NDRFieldListField):
2373 """
2374 NDR Conformant FieldListField
2375 """
2377 COUNT_FROM = True
2380class NDRConfVarFieldListField(_NDRConfField, _NDRVarField, NDRFieldListField):
2381 """
2382 NDR Conformant Varying FieldListField
2383 """
2385 COUNT_FROM = True
2388# NDR String fields
2391class _NDRUtf16(Field):
2392 def h2i(self, pkt, x):
2393 encoding = {"big": "utf-16be", "little": "utf-16le"}[pkt.ndrendian]
2394 return plain_str(x).encode(encoding)
2396 def i2h(self, pkt, x):
2397 encoding = {"big": "utf-16be", "little": "utf-16le"}[pkt.ndrendian]
2398 return bytes_encode(x).decode(encoding, errors="replace")
2401class NDRConfStrLenField(_NDRConfField, _NDRValueOf, StrLenField):
2402 """
2403 NDR Conformant StrLenField.
2405 This is not a "string" per NDR, but an a conformant byte array
2406 (e.g. tower_octet_string). For ease of use, we implicitly convert
2407 it in specific cases.
2408 """
2410 CONFORMANT_STRING = True
2411 LENGTH_FROM = True
2414class NDRConfStrLenFieldUtf16(_NDRConfField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16):
2415 """
2416 NDR Conformant StrLenFieldUtf16.
2418 See NDRConfStrLenField for comment.
2419 """
2421 CONFORMANT_STRING = True
2422 ON_WIRE_SIZE_UTF16 = False
2423 LENGTH_FROM = True
2426class NDRVarStrNullField(_NDRVarField, _NDRValueOf, StrNullField):
2427 """
2428 NDR Varying StrNullField
2429 """
2431 NULLFIELD = True
2434class NDRVarStrNullFieldUtf16(_NDRVarField, _NDRValueOf, StrNullFieldUtf16, _NDRUtf16):
2435 """
2436 NDR Varying StrNullFieldUtf16
2437 """
2439 NULLFIELD = True
2442class NDRVarStrLenField(_NDRVarField, StrLenField):
2443 """
2444 NDR Varying StrLenField
2445 """
2447 LENGTH_FROM = True
2450class NDRVarStrLenFieldUtf16(_NDRVarField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16):
2451 """
2452 NDR Varying StrLenFieldUtf16
2453 """
2455 ON_WIRE_SIZE_UTF16 = False
2456 LENGTH_FROM = True
2459class NDRConfVarStrLenField(_NDRConfField, _NDRVarField, _NDRValueOf, StrLenField):
2460 """
2461 NDR Conformant Varying StrLenField
2462 """
2464 LENGTH_FROM = True
2467class NDRConfVarStrLenFieldUtf16(
2468 _NDRConfField, _NDRVarField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16
2469):
2470 """
2471 NDR Conformant Varying StrLenFieldUtf16
2472 """
2474 ON_WIRE_SIZE_UTF16 = False
2475 LENGTH_FROM = True
2478class NDRConfVarStrNullField(_NDRConfField, _NDRVarField, _NDRValueOf, StrNullField):
2479 """
2480 NDR Conformant Varying StrNullField
2481 """
2483 NULLFIELD = True
2486class NDRConfVarStrNullFieldUtf16(
2487 _NDRConfField, _NDRVarField, _NDRValueOf, StrNullFieldUtf16, _NDRUtf16
2488):
2489 """
2490 NDR Conformant Varying StrNullFieldUtf16
2491 """
2493 ON_WIRE_SIZE_UTF16 = False
2494 NULLFIELD = True
2497# Union type
2500class NDRUnion(_NDRPacket):
2501 fields_desc = [
2502 IntField("tag", 0),
2503 PacketField("value", None, conf.raw_layer),
2504 ]
2507class _NDRUnionField(MultipleTypeField):
2508 __slots__ = ["switch_fmt", "align"]
2510 def __init__(self, flds, dflt, align, switch_fmt):
2511 self.switch_fmt = switch_fmt
2512 self.align = align
2513 super(_NDRUnionField, self).__init__(flds, dflt)
2515 def getfield(self, pkt, s):
2516 fmt = _e(pkt.ndrendian) + self.switch_fmt[pkt.ndr64]
2517 remain, tag = NDRAlign(Field("", 0, fmt=fmt), align=self.align).getfield(pkt, s)
2518 fld, _ = super(_NDRUnionField, self)._find_fld_pkt_val(pkt, NDRUnion(tag=tag))
2519 remain, val = fld.getfield(pkt, remain)
2520 return remain, NDRUnion(
2521 tag=tag, value=val, ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, _parent=pkt
2522 )
2524 def addfield(self, pkt, s, val):
2525 fmt = _e(pkt.ndrendian) + self.switch_fmt[pkt.ndr64]
2526 if not isinstance(val, NDRUnion):
2527 raise ValueError(
2528 "Expected NDRUnion in %s. You are using it wrong!" % self.name
2529 )
2530 _set_ctx_on(val.value, pkt)
2531 # First, align the whole tag+union against the align param
2532 s = NDRAlign(Field("", 0, fmt=fmt), align=self.align).addfield(pkt, s, val.tag)
2533 # Then, compute the subfield with its own alignment
2534 return super(_NDRUnionField, self).addfield(pkt, s, val)
2536 def _find_fld_pkt_val(self, pkt, val):
2537 fld, val = super(_NDRUnionField, self)._find_fld_pkt_val(pkt, val)
2538 return fld, val.value
2540 # Can't use i2repr = Field.i2repr and so on on PY2 :/
2541 def i2repr(self, pkt, val):
2542 return repr(val)
2544 def i2h(self, pkt, x):
2545 return x
2547 def h2i(self, pkt, x):
2548 return x
2550 def valueof(self, pkt, x):
2551 fld, val = self._find_fld_pkt_val(pkt, x)
2552 return fld.valueof(pkt, x.value)
2555class NDRUnionField(NDRConstructedType, _NDRUnionField):
2556 def __init__(self, flds, dflt, align, switch_fmt):
2557 _NDRUnionField.__init__(self, flds, dflt, align=align, switch_fmt=switch_fmt)
2558 NDRConstructedType.__init__(self, [x[0] for x in flds] + [dflt])
2560 def any2i(self, pkt, x):
2561 # User-friendly helper
2562 if x:
2563 if not isinstance(x, NDRUnion):
2564 raise ValueError("Invalid value for %s; should be NDRUnion" % self.name)
2565 else:
2566 x.value = _NDRUnionField.any2i(self, pkt, x)
2567 return x
2570# Misc
2573class _ProxyArray:
2574 # Hack for recursive fields DEPORTED_CONFORMANTS field
2575 __slots__ = ["getfld"]
2577 def __init__(self, getfld):
2578 self.getfld = getfld
2580 def __len__(self):
2581 try:
2582 return len(self.getfld())
2583 except AttributeError:
2584 return 0
2586 def __iter__(self):
2587 try:
2588 return iter(self.getfld())
2589 except AttributeError:
2590 return iter([])
2593class _ProxyTuple:
2594 # Hack for recursive fields ALIGNMENT field
2595 __slots__ = ["getfld"]
2597 def __init__(self, getfld):
2598 self.getfld = getfld
2600 def __getitem__(self, name):
2601 try:
2602 return self.getfld()[name]
2603 except AttributeError:
2604 raise KeyError
2607def NDRRecursiveClass(clsname):
2608 """
2609 Return a special class that is used for pointer recursion
2610 """
2611 # Get module where this is called
2612 frame = inspect.currentframe().f_back
2613 mod = frame.f_globals["__loader__"].name
2614 getcls = lambda: getattr(importlib.import_module(mod), clsname)
2616 class _REC(NDRPacket):
2617 ALIGNMENT = _ProxyTuple(lambda: getattr(getcls(), "ALIGNMENT"))
2618 DEPORTED_CONFORMANTS = _ProxyArray(
2619 lambda: getattr(getcls(), "DEPORTED_CONFORMANTS")
2620 )
2622 @classmethod
2623 def dispatch_hook(cls, _pkt=None, *args, **kargs):
2624 return getcls()
2626 return _REC
2629# The very few NDR-specific structures
2632class NDRContextHandle(NDRPacket):
2633 ALIGNMENT = (4, 4)
2634 fields_desc = [
2635 LEIntField("attributes", 0),
2636 StrFixedLenField("uuid", b"", length=16),
2637 ]
2639 def guess_payload_class(self, payload):
2640 return conf.padding_layer
2643class NDRNone(NDRPacket):
2644 # This is only used in NDRPacketListField to act as a "None" pointer, and is
2645 # a workaround because the field doesn't support None as a value in the list.
2646 name = "None"
2647 ALIGNMENT = (4, 8)
2648 fields_desc = [
2649 NDRInt3264Field("ptr", 0),
2650 ]
2653# --- Type Serialization Version 1 - [MSRPCE] sect 2.2.6
2656def _get_ndrtype1_endian(pkt):
2657 if pkt.underlayer is None:
2658 return "<"
2659 return {0x00: ">", 0x10: "<"}.get(pkt.underlayer.Endianness, "<")
2662class NDRSerialization1Header(Packet):
2663 fields_desc = [
2664 ByteField("Version", 1),
2665 ByteEnumField("Endianness", 0x10, {0x00: "big", 0x10: "little"}),
2666 LEShortField("CommonHeaderLength", 8),
2667 XLEIntField("Filler", 0xCCCCCCCC),
2668 ]
2670 # Add a bit of goo so that valueof() goes through the header
2672 def _ndrlayer(self):
2673 cur = self
2674 while cur and not isinstance(cur, _NDRPacket) and cur.payload:
2675 cur = cur.payload
2676 if isinstance(cur, NDRPointer):
2677 cur = cur.value
2678 return cur
2680 def getfield_and_val(self, attr):
2681 try:
2682 return Packet.getfield_and_val(self, attr)
2683 except ValueError:
2684 return self._ndrlayer().getfield_and_val(attr)
2686 def valueof(self, name):
2687 return self._ndrlayer().valueof(name)
2690class NDRSerialization1PrivateHeader(Packet):
2691 fields_desc = [
2692 EField(
2693 LEIntField("ObjectBufferLength", 0), endianness_from=_get_ndrtype1_endian
2694 ),
2695 XLEIntField("Filler", 0),
2696 ]
2699def ndr_deserialize1(b, cls, ptr_pack=False):
2700 """
2701 Deserialize Type Serialization Version 1
2702 [MS-RPCE] sect 2.2.6
2704 :param ptr_pack: pack in a pointer to the structure.
2705 """
2706 if issubclass(cls, NDRPacket):
2707 # We use an intermediary class because it uses NDRPacketField which handles
2708 # deported conformant fields
2709 if ptr_pack:
2710 hdrlen = 20
2712 class _cls(NDRPacket):
2713 fields_desc = [NDRFullPointerField(NDRPacketField("pkt", None, cls))]
2715 else:
2716 hdrlen = 16
2718 class _cls(NDRPacket):
2719 fields_desc = [NDRPacketField("pkt", None, cls)]
2721 hdr = NDRSerialization1Header(b[:8]) / NDRSerialization1PrivateHeader(b[8:16])
2722 endian = {0x00: "big", 0x10: "little"}[hdr.Endianness]
2723 padlen = (-hdr.ObjectBufferLength) % _TYPE1_S_PAD
2724 # padlen should be 0 (pad included in length), but some implementations
2725 # implement apparently misread the spec
2726 return (
2727 hdr
2728 / _cls(
2729 b[16 : hdrlen + hdr.ObjectBufferLength],
2730 ndr64=False, # Only NDR32 is supported in Type 1
2731 ndrendian=endian,
2732 ).pkt
2733 / conf.padding_layer(b[hdrlen + padlen + hdr.ObjectBufferLength :])
2734 )
2735 return NDRSerialization1Header(b[:8]) / cls(b[8:])
2738def ndr_serialize1(pkt, ptr_pack=False):
2739 """
2740 Serialize Type Serialization Version 1
2741 [MS-RPCE] sect 2.2.6
2743 :param ptr_pack: pack in a pointer to the structure.
2744 """
2745 pkt = pkt.copy()
2746 endian = getattr(pkt, "ndrendian", "little")
2747 if not isinstance(pkt, NDRSerialization1Header):
2748 if not isinstance(pkt, NDRPacket):
2749 return bytes(NDRSerialization1Header(Endianness=endian) / pkt)
2750 if isinstance(pkt, NDRPointer):
2751 cls = pkt.value.__class__
2752 else:
2753 cls = pkt.__class__
2754 val = pkt
2755 pkt_len = len(pkt)
2756 # ObjectBufferLength:
2757 # > It MUST include the padding length and exclude the header itself
2758 pkt = NDRSerialization1Header(
2759 Endianness=endian
2760 ) / NDRSerialization1PrivateHeader(
2761 ObjectBufferLength=pkt_len + (-pkt_len) % _TYPE1_S_PAD
2762 )
2763 else:
2764 cls = pkt.value.__class__
2765 val = pkt.payload.payload
2766 pkt.payload.remove_payload()
2768 # See above about why we need an intermediary class
2769 if ptr_pack:
2771 class _cls(NDRPacket):
2772 fields_desc = [NDRFullPointerField(NDRPacketField("pkt", None, cls))]
2774 else:
2776 class _cls(NDRPacket):
2777 fields_desc = [NDRPacketField("pkt", None, cls)]
2779 ret = bytes(pkt / _cls(pkt=val, ndr64=False, ndrendian=endian))
2780 return ret + (-len(ret) % _TYPE1_S_PAD) * b"\x00"
2783class _NDRSerializeType1:
2784 def __init__(self, *args, **kwargs):
2785 self.ptr_pack = kwargs.pop("ptr_pack", False)
2786 super(_NDRSerializeType1, self).__init__(*args, **kwargs)
2788 def i2m(self, pkt, val):
2789 return ndr_serialize1(val, ptr_pack=self.ptr_pack)
2791 def m2i(self, pkt, s):
2792 return ndr_deserialize1(s, self.cls, ptr_pack=self.ptr_pack)
2794 def i2len(self, pkt, val):
2795 return len(self.i2m(pkt, val))
2798class NDRSerializeType1PacketField(_NDRSerializeType1, PacketField):
2799 __slots__ = ["ptr_pack"]
2802class NDRSerializeType1PacketLenField(_NDRSerializeType1, PacketLenField):
2803 __slots__ = ["ptr_pack"]
2806class NDRSerializeType1PacketListField(_NDRSerializeType1, PacketListField):
2807 __slots__ = ["ptr_pack"]
2809 def i2len(self, pkt, val):
2810 return sum(len(self.i2m(pkt, p)) for p in val)
2813# --- DCE/RPC session
2816class DceRpcSession(DefaultSession):
2817 """
2818 A DCE/RPC session within a TCP socket.
2819 """
2821 def __init__(self, *args, **kwargs):
2822 self.rpc_bind_interface: Union[DceRpcInterface, ComInterface] = None
2823 self.rpc_bind_is_com: bool = False
2824 self.rpc_bind_interface_commit: Union[DceRpcInterface, ComInterface] = None
2825 self.rpc_bind_is_com_commit: bool = False
2826 self.ndr64 = False
2827 self.ndrendian = "little"
2828 self.support_header_signing = kwargs.pop("support_header_signing", True)
2829 self.header_sign = conf.dcerpc_force_header_signing
2830 self.ssp = kwargs.pop("ssp", None)
2831 self.sspcontext = kwargs.pop("sspcontext", None)
2832 self.auth_level = kwargs.pop("auth_level", None)
2833 self.sent_cont_ids = []
2834 self.cont_id = 0 # Currently selected context
2835 self.auth_context_id = 0 # Currently selected authentication context
2836 self.assoc_group_id = 0 # Currently selected association group
2837 self.map_callid_opnum = {}
2838 self.frags = collections.defaultdict(lambda: b"")
2839 self.sniffsspcontexts = {} # Unfinished contexts for passive
2840 if conf.dcerpc_session_enable and conf.winssps_passive:
2841 for ssp in conf.winssps_passive:
2842 self.sniffsspcontexts[ssp] = None
2843 super(DceRpcSession, self).__init__(*args, **kwargs)
2845 def commit_rpc_interface(self):
2846 """
2847 Called by the client/server when the current context is accepted
2848 """
2849 self.rpc_bind_interface = self.rpc_bind_interface_commit
2850 self.rpc_bind_is_com = self.rpc_bind_is_com_commit
2851 self.rpc_bind_interface_commit = None
2852 self.rpc_bind_is_com_commit = False
2854 def _up_pkt(self, pkt, commit=True):
2855 """
2856 Common function to handle the DCE/RPC session: what interfaces are bind,
2857 opnums, etc.
2859 :param commit: whether to commit bind information immediately, or wait for the
2860 parent to call commit_rpc_interface().
2861 """
2862 opnum = None
2863 opts = {}
2864 if DceRpc5Bind in pkt or DceRpc5AlterContext in pkt:
2865 # bind => get which RPC interface
2866 self.sent_cont_ids = [x.cont_id for x in pkt.context_elem]
2867 for ctx in pkt.context_elem:
2868 if_uuid = ctx.abstract_syntax.if_uuid
2869 if_version = ctx.abstract_syntax.if_version
2870 try:
2871 self.rpc_bind_interface_commit = DCE_RPC_INTERFACES[
2872 (if_uuid, if_version)
2873 ]
2874 self.rpc_bind_is_com_commit = False
2875 except KeyError:
2876 try:
2877 self.rpc_bind_interface_commit = COM_INTERFACES[if_uuid]
2878 self.rpc_bind_is_com_commit = True
2879 except KeyError:
2880 self.rpc_bind_interface_commit = None
2881 log_runtime.warning(
2882 "Unknown RPC interface %s. Try loading the IDL" % if_uuid
2883 )
2884 if commit:
2885 self.commit_rpc_interface()
2886 elif DceRpc5BindAck in pkt or DceRpc5AlterContextResp in pkt:
2887 # bind ack => is it NDR64
2888 for i, res in enumerate(pkt.results):
2889 if res.result == 0: # Accepted
2890 # Context
2891 try:
2892 self.cont_id = self.sent_cont_ids[i]
2893 except IndexError:
2894 self.cont_id = 0
2895 finally:
2896 self.sent_cont_ids = []
2898 self.assoc_group_id = pkt.assoc_group_id
2900 # Endianness
2901 self.ndrendian = {0: "big", 1: "little"}[pkt[DceRpc5].endian]
2903 # Transfer syntax
2904 if res.transfer_syntax.sprintf("%if_uuid%") == "NDR64":
2905 self.ndr64 = True
2906 elif DceRpc5Request in pkt:
2907 # request => match opnum with callID
2908 opnum = pkt.opnum
2909 uid = (self.assoc_group_id, pkt.call_id)
2910 if self.rpc_bind_is_com:
2911 self.map_callid_opnum[uid] = (
2912 opnum,
2913 pkt[DceRpc5Request].payload.payload,
2914 )
2915 else:
2916 self.map_callid_opnum[uid] = opnum, pkt[DceRpc5Request].payload
2917 elif DceRpc5Response in pkt:
2918 # response => get opnum from table
2919 uid = (self.assoc_group_id, pkt.call_id)
2920 try:
2921 opnum, opts["request_packet"] = self.map_callid_opnum[uid]
2922 del self.map_callid_opnum[uid]
2923 except KeyError:
2924 log_runtime.info("Unknown call_id %s in DCE/RPC session" % pkt.call_id)
2925 # Bind / Alter request/response specific
2926 if (
2927 DceRpc5Bind in pkt
2928 or DceRpc5AlterContext in pkt
2929 or DceRpc5BindAck in pkt
2930 or DceRpc5AlterContextResp in pkt
2931 ):
2932 # Detect if "Header Signing" is in use
2933 if pkt.pfc_flags & 0x04: # PFC_SUPPORT_HEADER_SIGN
2934 self.header_sign = True
2935 return opnum, opts
2937 # [C706] sect 12.6.2 - Fragmentation and Reassembly
2938 # Since the connection-oriented transport guarantees sequentiality, the receiver
2939 # will always receive the fragments in order.
2941 def _defragment(self, pkt, body=None):
2942 """
2943 Function to defragment DCE/RPC packets.
2944 """
2945 uid = (self.assoc_group_id, pkt.call_id)
2946 if pkt.pfc_flags.PFC_FIRST_FRAG and pkt.pfc_flags.PFC_LAST_FRAG:
2947 # Not fragmented
2948 return body
2949 if pkt.pfc_flags.PFC_FIRST_FRAG or uid in self.frags:
2950 # Packet is fragmented
2951 if body is None:
2952 body = pkt[DceRpc5].payload.payload.original
2953 self.frags[uid] += body
2954 if pkt.pfc_flags.PFC_LAST_FRAG:
2955 return self.frags[uid]
2956 else:
2957 # Not fragmented
2958 return body
2960 # C706 sect 12.5.2.15 - PDU Body Length
2961 # "The maximum PDU body size is 65528 bytes."
2962 MAX_PDU_BODY_SIZE = 4176
2964 def _fragment(self, pkt, body):
2965 """
2966 Function to fragment DCE/RPC packets.
2967 """
2968 if len(body) > self.MAX_PDU_BODY_SIZE:
2969 # Clear any PFC_*_FRAG flag
2970 pkt.pfc_flags &= 0xFC
2972 # Iterate through fragments
2973 cur = None
2974 while body:
2975 # Create a fragment
2976 pkt_frag = pkt.copy()
2978 if cur is None:
2979 # It's the first one
2980 pkt_frag.pfc_flags += "PFC_FIRST_FRAG"
2982 # Split
2983 cur, body = (
2984 body[: self.MAX_PDU_BODY_SIZE],
2985 body[self.MAX_PDU_BODY_SIZE :],
2986 )
2988 if not body:
2989 # It's the last one
2990 pkt_frag.pfc_flags += "PFC_LAST_FRAG"
2991 else:
2992 # [MS-RPCE] sect 2.2.2.13 - Verification Trailer
2993 # "only the last PDU of the request MUST have a verification
2994 # trailer"
2995 pkt_frag.vt_trailer = None
2997 # Update payload for frag_len calculation
2998 pkt_frag.payload.payload = conf.raw_layer(load=b"\x00" * len(cur))
3000 yield pkt_frag, cur
3001 else:
3002 yield pkt, body
3004 # [MS-RPCE] sect 3.3.1.5.2.2
3006 # The PDU header, PDU body, and sec_trailer MUST be passed in the input message, in
3007 # this order, to GSS_WrapEx, GSS_UnwrapEx, GSS_GetMICEx, and GSS_VerifyMICEx. For
3008 # integrity protection the sign flag for that PDU segment MUST be set to TRUE, else
3009 # it MUST be set to FALSE. For confidentiality protection, the conf_req_flag for
3010 # that PDU segment MUST be set to TRUE, else it MUST be set to FALSE.
3012 # If the authentication level is RPC_C_AUTHN_LEVEL_PKT_PRIVACY, the PDU body will
3013 # be encrypted.
3014 # The PDU body from the output message of GSS_UnwrapEx represents the plain text
3015 # version of the PDU body. The PDU header and sec_trailer output from the output
3016 # message SHOULD be ignored.
3017 # Similarly the signature output SHOULD be ignored.
3019 def in_pkt(self, pkt, commit=True):
3020 # Check for encrypted payloads
3021 body = None
3022 if conf.raw_layer in pkt.payload:
3023 body = bytes(pkt.payload[conf.raw_layer])
3024 if (
3025 self.sspcontext is not None
3026 and self.auth_level
3027 in (
3028 RPC_C_AUTHN_LEVEL.PKT_INTEGRITY,
3029 RPC_C_AUTHN_LEVEL.PKT_PRIVACY,
3030 )
3031 and isinstance(pkt.payload, (DceRpc5Request, DceRpc5Response))
3032 and not (pkt.auth_verifier and pkt.auth_verifier.is_protected())
3033 ):
3034 raise ValueError("DCE/RPC packet protection is required !")
3035 # If we are doing passive sniffing
3036 if conf.dcerpc_session_enable and conf.winssps_passive:
3037 # We have Windows SSPs, and no current context
3038 if pkt.auth_verifier and pkt.auth_verifier.is_ssp():
3039 # This is a bind/alter/auth3 req/resp
3040 for ssp in self.sniffsspcontexts:
3041 self.sniffsspcontexts[ssp], status = ssp.GSS_Passive(
3042 self.sniffsspcontexts[ssp],
3043 pkt.auth_verifier.auth_value,
3044 req_flags=GSS_S_FLAGS.GSS_S_ALLOW_MISSING_BINDINGS
3045 | GSS_C_FLAGS.GSS_C_DCE_STYLE,
3046 )
3047 if status == GSS_S_COMPLETE:
3048 self.auth_level = DCE_C_AUTHN_LEVEL(
3049 int(pkt.auth_verifier.auth_level)
3050 )
3051 self.ssp = ssp
3052 self.sspcontext = self.sniffsspcontexts[ssp]
3053 self.sniffsspcontexts[ssp] = None
3054 elif (
3055 self.sspcontext
3056 and pkt.auth_verifier
3057 and pkt.auth_verifier.is_protected()
3058 and body
3059 ):
3060 # This is a request/response
3061 if self.sspcontext.passive:
3062 self.ssp.GSS_Passive_set_Direction(
3063 self.sspcontext,
3064 IsAcceptor=DceRpc5Response in pkt,
3065 )
3066 if pkt.auth_verifier and pkt.auth_verifier.is_protected() and body:
3067 if self.sspcontext is None:
3068 return pkt
3069 if self.auth_level in (
3070 RPC_C_AUTHN_LEVEL.PKT_INTEGRITY,
3071 RPC_C_AUTHN_LEVEL.PKT_PRIVACY,
3072 ):
3073 # note: 'vt_trailer' is included in the pdu body
3074 # [MS-RPCE] sect 2.2.2.13
3075 # "The data structures MUST only appear in a request PDU, and they
3076 # SHOULD be placed in the PDU immediately after the stub data but
3077 # before the authentication padding octets. Therefore, for security
3078 # purposes, the verification trailer is considered part of the PDU
3079 # body."
3080 if pkt.vt_trailer:
3081 body += bytes(pkt.vt_trailer)
3082 # Account for padding when computing checksum/encryption
3083 if pkt.auth_padding:
3084 body += pkt.auth_padding
3086 # Build pdu_header and sec_trailer
3087 pdu_header = pkt.copy()
3088 sec_trailer = pdu_header.auth_verifier
3089 # sec_trailer: include the sec_trailer but not the Authentication token
3090 authval_len = len(sec_trailer.auth_value)
3091 # Discard everything out of the header
3092 pdu_header.auth_padding = None
3093 pdu_header.auth_verifier = None
3094 pdu_header.payload.payload = NoPayload()
3095 pdu_header.vt_trailer = None
3097 # [MS-RPCE] sect 2.2.2.12
3098 if self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY:
3099 _msgs = self.ssp.GSS_UnwrapEx(
3100 self.sspcontext,
3101 [
3102 # "PDU header"
3103 SSP.WRAP_MSG(
3104 conf_req_flag=False,
3105 sign=self.header_sign,
3106 data=bytes(pdu_header),
3107 ),
3108 # "PDU body"
3109 SSP.WRAP_MSG(
3110 conf_req_flag=True,
3111 sign=True,
3112 data=body,
3113 ),
3114 # "sec_trailer"
3115 SSP.WRAP_MSG(
3116 conf_req_flag=False,
3117 sign=self.header_sign,
3118 data=bytes(sec_trailer)[:-authval_len],
3119 ),
3120 ],
3121 pkt.auth_verifier.auth_value,
3122 )
3123 body = _msgs[1].data # PDU body
3124 elif self.auth_level == RPC_C_AUTHN_LEVEL.PKT_INTEGRITY:
3125 self.ssp.GSS_VerifyMICEx(
3126 self.sspcontext,
3127 [
3128 # "PDU header"
3129 SSP.MIC_MSG(
3130 sign=self.header_sign,
3131 data=bytes(pdu_header),
3132 ),
3133 # "PDU body"
3134 SSP.MIC_MSG(
3135 sign=True,
3136 data=body,
3137 ),
3138 # "sec_trailer"
3139 SSP.MIC_MSG(
3140 sign=self.header_sign,
3141 data=bytes(sec_trailer)[:-authval_len],
3142 ),
3143 ],
3144 pkt.auth_verifier.auth_value,
3145 )
3146 # Put padding back into the header
3147 if pkt.auth_padding:
3148 padlen = len(pkt.auth_padding)
3149 body, pkt.auth_padding = body[:-padlen], body[-padlen:]
3150 # Put back vt_trailer into the header, if present.
3151 if _SECTRAILER_MAGIC in body:
3152 body, pkt.vt_trailer = pkt.get_field("vt_trailer").getfield(
3153 pkt, body
3154 )
3155 # If it's a request / response, could be fragmented
3156 if isinstance(pkt.payload, (DceRpc5Request, DceRpc5Response)) and body:
3157 body = self._defragment(pkt, body)
3158 if not body:
3159 return
3160 # Get opnum and options
3161 opnum, opts = self._up_pkt(pkt, commit=commit)
3162 # Try to parse the payload
3163 if opnum is not None and self.rpc_bind_interface:
3164 # use opnum to parse the payload
3165 is_response = DceRpc5Response in pkt
3166 try:
3167 cls = self.rpc_bind_interface.opnums[opnum][is_response]
3168 except KeyError:
3169 log_runtime.warning(
3170 "Unknown opnum %s for interface %s"
3171 % (opnum, self.rpc_bind_interface)
3172 )
3173 pkt.payload[conf.raw_layer].load = body
3174 return pkt
3175 if body:
3176 orpc = None
3177 if self.rpc_bind_is_com:
3178 # If interface is a COM interface, start off by dissecting the
3179 # ORPCTHIS / ORPCTHAT argument
3180 from scapy.layers.msrpce.raw.ms_dcom import ORPCTHAT, ORPCTHIS
3182 # [MS-DCOM] sect 2.2.13
3183 # "ORPCTHIS and ORPCTHAT structures MUST be marshaled using
3184 # the NDR (32) Transfer Syntax"
3185 if is_response:
3186 orpc = ORPCTHAT(body, ndr64=False)
3187 else:
3188 orpc = ORPCTHIS(body, ndr64=False)
3189 body = orpc.load
3190 orpc.remove_payload()
3191 # Dissect payload using class
3192 try:
3193 payload = cls(
3194 body, ndr64=self.ndr64, ndrendian=self.ndrendian, **opts
3195 )
3196 except Exception:
3197 if conf.debug_dissector:
3198 log_runtime.error("%s dissector failed", cls.__name__)
3199 if cls is not None:
3200 raise
3201 payload = conf.raw_layer(body, _internal=1)
3202 pkt.payload[conf.raw_layer].underlayer.remove_payload()
3203 if conf.padding_layer in payload:
3204 # Most likely, dissection failed.
3205 log_runtime.warning(
3206 "Padding detected when dissecting %s. Looks wrong." % cls
3207 )
3208 pad = payload[conf.padding_layer]
3209 pad.underlayer.payload = conf.raw_layer(load=pad.load)
3210 if orpc is not None:
3211 pkt /= orpc
3212 pkt /= payload
3213 # If a request was encrypted, we need to re-register it once re-parsed.
3214 if not is_response and self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY:
3215 self._up_pkt(pkt, commit=commit)
3216 elif not cls.fields_desc:
3217 # Request class has no payload
3218 pkt /= cls(ndr64=self.ndr64, ndrendian=self.ndrendian, **opts)
3219 elif body:
3220 pkt.payload[conf.raw_layer].load = body
3221 return pkt
3223 def out_pkt(self, pkt):
3224 assert DceRpc5 in pkt
3225 # Register opnum and options
3226 self._up_pkt(pkt)
3228 # If it's a request / response, we can frag it
3229 if isinstance(pkt.payload, (DceRpc5Request, DceRpc5Response)):
3230 # The list of packet responses
3231 pkts = []
3232 # Take the body payload, and eventually split it
3233 body = bytes(pkt.payload.payload)
3235 for pkt, body in self._fragment(pkt, body):
3236 if pkt.auth_verifier is not None:
3237 # Verifier already set
3238 pkts.append(pkt)
3239 continue
3241 # Sign / Encrypt
3242 if self.sspcontext:
3243 signature = None
3244 if self.auth_level in (
3245 RPC_C_AUTHN_LEVEL.PKT_INTEGRITY,
3246 RPC_C_AUTHN_LEVEL.PKT_PRIVACY,
3247 ):
3248 # Remember that vt_trailer is included in the PDU
3249 if pkt.vt_trailer:
3250 body += bytes(pkt.vt_trailer)
3251 # Account for padding when computing checksum/encryption
3252 if pkt.auth_padding is None:
3253 padlen = (-len(body)) % _COMMON_AUTH_PAD # authdata padding
3254 pkt.auth_padding = b"\x00" * padlen
3255 else:
3256 padlen = len(pkt.auth_padding)
3257 # Remember that padding IS SIGNED & ENCRYPTED
3258 body += pkt.auth_padding
3259 # Add the auth_verifier
3260 pkt.auth_verifier = CommonAuthVerifier(
3261 auth_type=self.ssp.auth_type,
3262 auth_level=self.auth_level,
3263 auth_context_id=self.auth_context_id,
3264 auth_pad_length=padlen,
3265 # Note: auth_value should have the correct length because
3266 # when using PFC_SUPPORT_HEADER_SIGN, auth_len
3267 # (and frag_len) is included in the token.. but this
3268 # creates a dependency loop as you'd need to know the token
3269 # length to compute the token. Windows solves this by
3270 # setting the 'Maximum Signature Length' (or something
3271 # similar) beforehand, instead of the real length.
3272 # See `gensec_sig_size` in samba.
3273 auth_value=b"\x00"
3274 * self.ssp.MaximumSignatureLength(self.sspcontext),
3275 )
3276 # Build pdu_header and sec_trailer
3277 pdu_header = pkt.copy()
3278 pdu_header.auth_len = len(pdu_header.auth_verifier) - 8
3279 pdu_header.frag_len = len(pdu_header)
3280 sec_trailer = pdu_header.auth_verifier
3281 # sec_trailer: include the sec_trailer but not the
3282 # Authentication token
3283 authval_len = len(sec_trailer.auth_value)
3284 # sec_trailer.auth_value = None
3285 # Discard everything out of the header
3286 pdu_header.auth_padding = None
3287 pdu_header.auth_verifier = None
3288 pdu_header.payload.payload = NoPayload()
3289 pdu_header.vt_trailer = None
3290 signature = None
3291 # [MS-RPCE] sect 2.2.2.12
3292 if self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY:
3293 _msgs, signature = self.ssp.GSS_WrapEx(
3294 self.sspcontext,
3295 [
3296 # "PDU header"
3297 SSP.WRAP_MSG(
3298 conf_req_flag=False,
3299 sign=self.header_sign,
3300 data=bytes(pdu_header),
3301 ),
3302 # "PDU body"
3303 SSP.WRAP_MSG(
3304 conf_req_flag=True,
3305 sign=True,
3306 data=body,
3307 ),
3308 # "sec_trailer"
3309 SSP.WRAP_MSG(
3310 conf_req_flag=False,
3311 sign=self.header_sign,
3312 data=bytes(sec_trailer)[:-authval_len],
3313 ),
3314 ],
3315 )
3316 s = _msgs[1].data # PDU body
3317 elif self.auth_level == RPC_C_AUTHN_LEVEL.PKT_INTEGRITY:
3318 signature = self.ssp.GSS_GetMICEx(
3319 self.sspcontext,
3320 [
3321 # "PDU header"
3322 SSP.MIC_MSG(
3323 sign=self.header_sign,
3324 data=bytes(pdu_header),
3325 ),
3326 # "PDU body"
3327 SSP.MIC_MSG(
3328 sign=True,
3329 data=body,
3330 ),
3331 # "sec_trailer"
3332 SSP.MIC_MSG(
3333 sign=self.header_sign,
3334 data=bytes(sec_trailer)[:-authval_len],
3335 ),
3336 ],
3337 pkt.auth_verifier.auth_value,
3338 )
3339 s = body
3340 else:
3341 raise ValueError("Impossible")
3342 # Put padding back in the header
3343 if padlen:
3344 s, pkt.auth_padding = s[:-padlen], s[-padlen:]
3345 # Put back vt_trailer into the header
3346 if pkt.vt_trailer:
3347 vtlen = len(pkt.vt_trailer)
3348 s, pkt.vt_trailer = s[:-vtlen], s[-vtlen:]
3349 else:
3350 s = body
3352 # now inject the encrypted payload into the packet
3353 pkt.payload.payload = conf.raw_layer(load=s)
3354 # and the auth_value
3355 if signature:
3356 pkt.auth_verifier.auth_value = signature
3357 else:
3358 pkt.auth_verifier = None
3359 # Add to the list
3360 pkts.append(pkt)
3361 return pkts
3362 else:
3363 return [pkt]
3365 def process(self, pkt: Packet) -> Optional[Packet]:
3366 """
3367 Used when DceRpcSession is used for passive sniffing.
3368 """
3369 pkt = super(DceRpcSession, self).process(pkt)
3370 if pkt is not None and DceRpc5 in pkt:
3371 rpkt = self.in_pkt(pkt)
3372 if rpkt is None:
3373 # We are passively dissecting a fragmented packet. Return
3374 # just the header showing that it was indeed, fragmented.
3375 pkt[DceRpc5].payload.remove_payload()
3376 return pkt
3377 return rpkt
3378 return pkt
3381class DceRpcSocket(StreamSocket):
3382 """
3383 A Wrapper around StreamSocket that uses a DceRpcSession
3384 """
3386 def __init__(self, *args, **kwargs):
3387 self.transport = kwargs.pop("transport", None)
3388 self.session = DceRpcSession(
3389 ssp=kwargs.pop("ssp", None),
3390 auth_level=kwargs.pop("auth_level", None),
3391 support_header_signing=kwargs.pop("support_header_signing", True),
3392 )
3393 super(DceRpcSocket, self).__init__(*args, **kwargs)
3395 def send(self, x, is_sr1=False, **kwargs):
3396 for pkt in self.session.out_pkt(x):
3397 if self.transport == DCERPC_Transport.NCACN_NP:
3398 # In this case DceRpcSocket wraps a SMB_RPC_SOCKET, call it directly.
3399 self.ins.send(pkt, is_sr1=is_sr1, **kwargs)
3400 else:
3401 super(DceRpcSocket, self).send(pkt, **kwargs)
3403 def sr1(self, *args, **kwargs):
3404 # We allow to use IOCTL only when sr1() is used, as we expect an answer.
3405 return super(DceRpcSocket, self).sr1(*args, is_sr1=True, **kwargs)
3407 def recv(self, x=None):
3408 pkt = super(DceRpcSocket, self).recv(x)
3409 if pkt is not None:
3410 return self.session.in_pkt(pkt)
3413# --- TODO cleanup below
3415# Heuristically way to find the payload class
3416#
3417# To add a possible payload to a DCE/RPC packet, one must first create the
3418# packet class, then instead of binding layers using bind_layers, he must
3419# call DceRpcPayload.register_possible_payload() with the payload class as
3420# parameter.
3421#
3422# To be able to decide if the payload class is capable of handling the rest of
3423# the dissection, the classmethod can_handle() should be implemented in the
3424# payload class. This method is given the rest of the string to dissect as
3425# first argument, and the DceRpc packet instance as second argument. Based on
3426# this information, the method must return True if the class is capable of
3427# handling the dissection, False otherwise
3430class DceRpc4Payload(Packet):
3431 """Dummy class which use the dispatch_hook to find the payload class"""
3433 _payload_class = []
3435 @classmethod
3436 def dispatch_hook(cls, _pkt, _underlayer=None, *args, **kargs):
3437 """dispatch_hook to choose among different registered payloads"""
3438 for klass in cls._payload_class:
3439 if hasattr(klass, "can_handle") and klass.can_handle(_pkt, _underlayer):
3440 return klass
3441 log_runtime.warning("DCE/RPC payload class not found or undefined (using Raw)")
3442 return Raw
3444 @classmethod
3445 def register_possible_payload(cls, pay):
3446 """Method to call from possible DCE/RPC endpoint to register it as
3447 possible payload"""
3448 cls._payload_class.append(pay)
3451bind_layers(DceRpc4, DceRpc4Payload)