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 = {}
185class DCERPC_Transport(IntEnum):
186 """
187 Protocols identifiers currently supported by Scapy
188 """
190 NCACN_IP_TCP = 0x07
191 NCACN_NP = 0x0F
192 # TODO: add more.. if people use them?
195# [C706] Appendix I with names from Appendix B
196DCE_RPC_PROTOCOL_IDENTIFIERS = {
197 0x0: "OSI OID", # Special
198 0x0D: "UUID", # Special
199 # Transports
200 # 0x2: "DNA Session Control",
201 # 0x3: "DNA Session Control V3",
202 # 0x4: "DNA NSP Transport",
203 # 0x5: "OSI TP4",
204 0x06: "NCADG_OSI_CLSN", # [C706]
205 0x07: "NCACN_IP_TCP", # [C706]
206 0x08: "NCADG_IP_UDP", # [C706]
207 0x09: "IP", # [C706]
208 0x0A: "RPC connectionless protocol", # [C706]
209 0x0B: "RPC connection-oriented protocol", # [C706]
210 0x0C: "NCALRPC",
211 0x0F: "NCACN_NP", # [MS-RPCE]
212 0x11: "NCACN_NB", # [C706]
213 0x12: "NCACN_NB_NB", # [MS-RPCE]
214 0x13: "NCACN_SPX", # [C706]
215 0x14: "NCADG_IPX", # [C706]
216 0x16: "NCACN_AT_DSP", # [C706]
217 0x17: "NCADG_AT_DSP", # [C706]
218 0x19: "NCADG_NB", # [C706]
219 0x1A: "NCACN_VNS_SPP", # [C706]
220 0x1B: "NCADG_VNS_IPC", # [C706]
221 0x1F: "NCACN_HTTP", # [MS-RPCE]
222}
225def _dce_rpc_endianness(pkt):
226 """
227 Determine the right endianness sign for a given DCE/RPC packet
228 """
229 if pkt.endian == 0: # big endian
230 return ">"
231 elif pkt.endian == 1: # little endian
232 return "<"
233 else:
234 return "!"
237class _EField(EField):
238 def __init__(self, fld):
239 super(_EField, self).__init__(fld, endianness_from=_dce_rpc_endianness)
242class DceRpc(Packet):
243 """DCE/RPC packet"""
245 @classmethod
246 def dispatch_hook(cls, _pkt=None, *args, **kargs):
247 if _pkt and len(_pkt) >= 1:
248 ver = ord(_pkt[0:1])
249 if ver == 4:
250 return DceRpc4
251 elif ver == 5:
252 return DceRpc5
253 return DceRpc5
255 @classmethod
256 def tcp_reassemble(cls, data, metadata, session):
257 if data[0:1] == b"\x05":
258 return DceRpc5.tcp_reassemble(data, metadata, session)
259 return DceRpc(data)
262bind_bottom_up(TCP, DceRpc, sport=135)
263bind_layers(TCP, DceRpc, dport=135)
266class _DceRpcPayload(Packet):
267 @property
268 def endianness(self):
269 if not self.underlayer:
270 return "!"
271 return _dce_rpc_endianness(self.underlayer)
274# sect 12.5
276_drep = [
277 BitEnumField("endian", 1, 4, ["big", "little"]),
278 BitEnumField("encoding", 0, 4, ["ASCII", "EBCDIC"]),
279 ByteEnumField("float", 0, ["IEEE", "VAX", "CRAY", "IBM"]),
280 ByteField("reserved1", 0),
281]
284class DceRpc4(DceRpc):
285 """
286 DCE/RPC v4 'connection-less' packet
287 """
289 name = "DCE/RPC v4"
290 fields_desc = (
291 [
292 ByteEnumField(
293 "rpc_vers", 4, {4: "4 (connection-less)", 5: "5 (connection-oriented)"}
294 ),
295 ByteEnumField("ptype", 0, DCE_RPC_TYPE),
296 FlagsField("flags1", 0, 8, _DCE_RPC_4_FLAGS1),
297 FlagsField("flags2", 0, 8, _DCE_RPC_4_FLAGS2),
298 ]
299 + _drep
300 + [
301 XByteField("serial_hi", 0),
302 _EField(UUIDField("object", None)),
303 _EField(UUIDField("if_id", None)),
304 _EField(UUIDField("act_id", None)),
305 _EField(IntField("server_boot", 0)),
306 _EField(IntField("if_vers", 1)),
307 _EField(IntField("seqnum", 0)),
308 _EField(ShortField("opnum", 0)),
309 _EField(XShortField("ihint", 0xFFFF)),
310 _EField(XShortField("ahint", 0xFFFF)),
311 _EField(LenField("len", None, fmt="H")),
312 _EField(ShortField("fragnum", 0)),
313 ByteEnumField("auth_proto", 0, ["none", "OSF DCE Private Key"]),
314 XByteField("serial_lo", 0),
315 ]
316 )
319# Exceptionally, we define those 3 here.
322class NL_AUTH_MESSAGE(Packet):
323 # [MS-NRPC] sect 2.2.1.3.1
324 name = "NL_AUTH_MESSAGE"
325 fields_desc = [
326 LEIntEnumField(
327 "MessageType",
328 0x00000000,
329 {
330 0x00000000: "Request",
331 0x00000001: "Response",
332 },
333 ),
334 FlagsField(
335 "Flags",
336 0,
337 -32,
338 [
339 "NETBIOS_DOMAIN_NAME",
340 "NETBIOS_COMPUTER_NAME",
341 "DNS_DOMAIN_NAME",
342 "DNS_HOST_NAME",
343 "NETBIOS_COMPUTER_NAME_UTF8",
344 ],
345 ),
346 ConditionalField(
347 StrNullField("NetbiosDomainName", ""),
348 lambda pkt: pkt.Flags.NETBIOS_DOMAIN_NAME,
349 ),
350 ConditionalField(
351 StrNullField("NetbiosComputerName", ""),
352 lambda pkt: pkt.Flags.NETBIOS_COMPUTER_NAME,
353 ),
354 ConditionalField(
355 DNSStrField("DnsDomainName", ""),
356 lambda pkt: pkt.Flags.DNS_DOMAIN_NAME,
357 ),
358 ConditionalField(
359 DNSStrField("DnsHostName", ""),
360 lambda pkt: pkt.Flags.DNS_HOST_NAME,
361 ),
362 ConditionalField(
363 # What the fuck? Why are they doing this
364 # The spec is just wrong
365 DNSStrField("NetbiosComputerNameUtf8", ""),
366 lambda pkt: pkt.Flags.NETBIOS_COMPUTER_NAME_UTF8,
367 ),
368 ]
371class NL_AUTH_SIGNATURE(Packet):
372 # [MS-NRPC] sect 2.2.1.3.2/2.2.1.3.3
373 name = "NL_AUTH_(SHA2_)SIGNATURE"
374 fields_desc = [
375 LEShortEnumField(
376 "SignatureAlgorithm",
377 0x0077,
378 {
379 0x0077: "HMAC-MD5",
380 0x0013: "HMAC-SHA256",
381 },
382 ),
383 LEShortEnumField(
384 "SealAlgorithm",
385 0xFFFF,
386 {
387 0xFFFF: "Unencrypted",
388 0x007A: "RC4",
389 0x001A: "AES-128",
390 },
391 ),
392 XLEShortField("Pad", 0xFFFF),
393 ShortField("Flags", 0),
394 XStrFixedLenField("SequenceNumber", b"", length=8),
395 XStrFixedLenField("Checksum", b"", length=8),
396 ConditionalField(
397 XStrFixedLenField("Confounder", b"", length=8),
398 lambda pkt: pkt.SealAlgorithm != 0xFFFF,
399 ),
400 MultipleTypeField(
401 [
402 (
403 StrFixedLenField("Reserved2", b"", length=24),
404 lambda pkt: pkt.SignatureAlgorithm == 0x0013,
405 ),
406 ],
407 StrField("Reserved2", b""),
408 ),
409 ]
412# [MS-RPCE] sect 2.2.1.1.7
413# https://learn.microsoft.com/en-us/windows/win32/rpc/authentication-service-constants
414# rpcdce.h
417class RPC_C_AUTHN(IntEnum):
418 NONE = 0x00
419 DCE_PRIVATE = 0x01
420 DCE_PUBLIC = 0x02
421 DEC_PUBLIC = 0x04
422 GSS_NEGOTIATE = 0x09
423 WINNT = 0x0A
424 GSS_SCHANNEL = 0x0E
425 GSS_KERBEROS = 0x10
426 DPA = 0x11
427 MSN = 0x12
428 KERNEL = 0x14
429 DIGEST = 0x15
430 NEGO_EXTENDED = 0x1E
431 PKU2U = 0x1F
432 LIVE_SSP = 0x20
433 LIVEXP_SSP = 0x23
434 CLOUD_AP = 0x24
435 NETLOGON = 0x44
436 MSONLINE = 0x52
437 MQ = 0x64
438 DEFAULT = 0xFFFFFFFF
441class RPC_C_AUTHN_LEVEL(IntEnum):
442 DEFAULT = 0x0
443 NONE = 0x1
444 CONNECT = 0x2
445 CALL = 0x3
446 PKT = 0x4
447 PKT_INTEGRITY = 0x5
448 PKT_PRIVACY = 0x6
451DCE_C_AUTHN_LEVEL = RPC_C_AUTHN_LEVEL # C706 name
454class RPC_C_IMP_LEVEL(IntEnum):
455 DEFAULT = 0x0
456 ANONYMOUS = 0x1
457 IDENTIFY = 0x2
458 IMPERSONATE = 0x3
459 DELEGATE = 0x4
462# C706 sect 13.2.6.1
465class CommonAuthVerifier(Packet):
466 name = "Common Authentication Verifier"
467 fields_desc = [
468 ByteEnumField(
469 "auth_type",
470 0,
471 RPC_C_AUTHN,
472 ),
473 ByteEnumField("auth_level", 0, RPC_C_AUTHN_LEVEL),
474 ByteField("auth_pad_length", None),
475 ByteField("auth_reserved", 0),
476 XLEIntField("auth_context_id", 0),
477 MultipleTypeField(
478 [
479 # SPNEGO
480 (
481 PacketLenField(
482 "auth_value",
483 GSSAPI_BLOB(),
484 GSSAPI_BLOB,
485 length_from=lambda pkt: pkt.parent.auth_len,
486 ),
487 lambda pkt: pkt.auth_type == 0x09 and pkt.parent and
488 # Bind/Alter
489 pkt.parent.ptype in [11, 12, 13, 14, 15, 16],
490 ),
491 (
492 PacketLenField(
493 "auth_value",
494 GSSAPI_BLOB_SIGNATURE(),
495 GSSAPI_BLOB_SIGNATURE,
496 length_from=lambda pkt: pkt.parent.auth_len,
497 ),
498 lambda pkt: pkt.auth_type == 0x09
499 and pkt.parent
500 and (
501 # Other
502 not pkt.parent
503 or pkt.parent.ptype not in [11, 12, 13, 14, 15, 16]
504 ),
505 ),
506 # Kerberos
507 (
508 PacketLenField(
509 "auth_value",
510 Kerberos(),
511 Kerberos,
512 length_from=lambda pkt: pkt.parent.auth_len,
513 ),
514 lambda pkt: pkt.auth_type == 0x10 and pkt.parent and
515 # Bind/Alter
516 pkt.parent.ptype in [11, 12, 13, 14, 15, 16],
517 ),
518 (
519 PacketLenField(
520 "auth_value",
521 KRB_InnerToken(),
522 KRB_InnerToken,
523 length_from=lambda pkt: pkt.parent.auth_len,
524 ),
525 lambda pkt: pkt.auth_type == 0x10
526 and pkt.parent
527 and (
528 # Other
529 not pkt.parent
530 or pkt.parent.ptype not in [11, 12, 13, 14, 15, 16]
531 ),
532 ),
533 # NTLM
534 (
535 PacketLenField(
536 "auth_value",
537 NTLM_Header(),
538 NTLM_Header,
539 length_from=lambda pkt: pkt.parent.auth_len,
540 ),
541 lambda pkt: pkt.auth_type in [0x0A, 0xFF] and pkt.parent and
542 # Bind/Alter
543 pkt.parent.ptype in [11, 12, 13, 14, 15, 16],
544 ),
545 (
546 PacketLenField(
547 "auth_value",
548 NTLMSSP_MESSAGE_SIGNATURE(),
549 NTLMSSP_MESSAGE_SIGNATURE,
550 length_from=lambda pkt: pkt.parent.auth_len,
551 ),
552 lambda pkt: pkt.auth_type in [0x0A, 0xFF]
553 and pkt.parent
554 and (
555 # Other
556 not pkt.parent
557 or pkt.parent.ptype not in [11, 12, 13, 14, 15, 16]
558 ),
559 ),
560 # NetLogon
561 (
562 PacketLenField(
563 "auth_value",
564 NL_AUTH_MESSAGE(),
565 NL_AUTH_MESSAGE,
566 length_from=lambda pkt: pkt.parent.auth_len,
567 ),
568 lambda pkt: pkt.auth_type == 0x44 and pkt.parent and
569 # Bind/Alter
570 pkt.parent.ptype in [11, 12, 13, 14, 15],
571 ),
572 (
573 PacketLenField(
574 "auth_value",
575 NL_AUTH_SIGNATURE(),
576 NL_AUTH_SIGNATURE,
577 length_from=lambda pkt: pkt.parent.auth_len,
578 ),
579 lambda pkt: pkt.auth_type == 0x44
580 and (
581 # Other
582 not pkt.parent
583 or pkt.parent.ptype not in [11, 12, 13, 14, 15]
584 ),
585 ),
586 ],
587 PacketLenField(
588 "auth_value",
589 None,
590 conf.raw_layer,
591 length_from=lambda pkt: pkt.parent and pkt.parent.auth_len or 0,
592 ),
593 ),
594 ]
596 def is_protected(self):
597 if not self.auth_value:
598 return False
599 if self.parent and self.parent.ptype in [11, 12, 13, 14, 15, 16]:
600 return False
601 return True
603 def is_ssp(self):
604 if not self.auth_value:
605 return False
606 if self.parent and self.parent.ptype not in [11, 12, 13, 14, 15, 16]:
607 return False
608 return True
610 def default_payload_class(self, pkt):
611 return conf.padding_layer
614# [MS-RPCE] sect 2.2.2.13 - Verification Trailer
615_SECTRAILER_MAGIC = b"\x8a\xe3\x13\x71\x02\xf4\x36\x71"
618class DceRpcSecVTCommand(Packet):
619 name = "Verification trailer command"
620 fields_desc = [
621 BitField("SEC_VT_MUST_PROCESS_COMMAND", 0, 1, tot_size=-2),
622 BitField("SEC_VT_COMMAND_END", 0, 1),
623 BitEnumField(
624 "Command",
625 0,
626 -14,
627 {
628 0x0001: "SEC_VT_COMMAND_BITMASK_1",
629 0x0002: "SEC_VT_COMMAND_PCONTEXT",
630 0x0003: "SEC_VT_COMMAND_HEADER2",
631 },
632 end_tot_size=-2,
633 ),
634 LenField("Length", None, fmt="<H"),
635 ]
638# [MS-RPCE] sect 2.2.2.13.2
641class DceRpcSecVTBitmask(Packet):
642 name = "rpc_sec_vt_bitmask"
643 fields_desc = [
644 LEIntField("bits", 1),
645 ]
647 def default_payload_class(self, pkt):
648 return conf.padding_layer
651bind_layers(DceRpcSecVTCommand, DceRpcSecVTBitmask, Command=0x0001)
654# [MS-RPCE] sect 2.2.2.13.4
657class DceRpcSecVTPcontext(Packet):
658 name = "rpc_sec_vt_pcontext"
659 fields_desc = [
660 UUIDEnumField(
661 "InterfaceId",
662 None,
663 (
664 DCE_RPC_INTERFACES_NAMES.get,
665 lambda x: DCE_RPC_INTERFACES_NAMES_rev.get(x.lower()),
666 ),
667 uuid_fmt=UUIDField.FORMAT_LE,
668 ),
669 LEIntField("Version", 0),
670 UUIDEnumField(
671 "TransferSyntax",
672 None,
673 DCE_RPC_TRANSFER_SYNTAXES,
674 uuid_fmt=UUIDField.FORMAT_LE,
675 ),
676 LEIntField("TransferVersion", 0),
677 ]
679 def default_payload_class(self, pkt):
680 return conf.padding_layer
683bind_layers(DceRpcSecVTCommand, DceRpcSecVTPcontext, Command=0x0002)
686# [MS-RPCE] sect 2.2.2.13.3
689class DceRpcSecVTHeader2(Packet):
690 name = "rpc_sec_vt_header2"
691 fields_desc = [
692 ByteField("PTYPE", 0),
693 ByteField("Reserved1", 0),
694 LEShortField("Reserved2", 0),
695 LEIntField("drep", 0),
696 LEIntField("call_id", 0),
697 LEShortField("p_cont_id", 0),
698 LEShortField("opnum", 0),
699 ]
701 def default_payload_class(self, pkt):
702 return conf.padding_layer
705bind_layers(DceRpcSecVTCommand, DceRpcSecVTHeader2, Command=0x0003)
708class DceRpcSecVT(Packet):
709 name = "Verification trailer"
710 fields_desc = [
711 XStrFixedLenField("rpc_sec_verification_trailer", _SECTRAILER_MAGIC, length=8),
712 PacketListField("commands", [], DceRpcSecVTCommand),
713 ]
716class _VerifTrailerField(PacketField):
717 def getfield(
718 self,
719 pkt,
720 s,
721 ):
722 if _SECTRAILER_MAGIC in s:
723 # a bit ugly
724 ind = s.index(_SECTRAILER_MAGIC)
725 sectrailer_bytes, remain = bytes(s[:-ind]), bytes(s[-ind:])
726 vt_trailer = self.m2i(pkt, sectrailer_bytes)
727 if not isinstance(vt_trailer.payload, NoPayload):
728 # bad parse
729 return s, None
730 return remain, vt_trailer
731 return s, None
734# sect 12.6.3
737_DCE_RPC_5_FLAGS = {
738 0x01: "PFC_FIRST_FRAG",
739 0x02: "PFC_LAST_FRAG",
740 0x04: "PFC_PENDING_CANCEL",
741 0x08: "PFC_RESERVED_1",
742 0x10: "PFC_CONC_MPX",
743 0x20: "PFC_DID_NOT_EXECUTE",
744 0x40: "PFC_MAYBE",
745 0x80: "PFC_OBJECT_UUID",
746}
748# [MS-RPCE] sect 2.2.2.3
750_DCE_RPC_5_FLAGS_2 = _DCE_RPC_5_FLAGS.copy()
751_DCE_RPC_5_FLAGS_2[0x04] = "PFC_SUPPORT_HEADER_SIGN"
754_DCE_RPC_ERROR_CODES = {
755 # Win32
756 0x776: "OR_INVALID_OXID",
757 0x777: "OR_INVALID_OID",
758 0x778: "OR_INVALID_SET",
759 # Appendix N
760 0x1C010001: "nca_s_comm_failure",
761 0x1C010002: "nca_s_op_rng_error",
762 0x1C010003: "nca_s_unk_if",
763 0x1C010006: "nca_s_wrong_boot_time",
764 0x1C010009: "nca_s_you_crashed",
765 0x1C01000B: "nca_s_proto_error",
766 0x1C010013: "nca_s_out_args_too_big",
767 0x1C010014: "nca_s_server_too_busy",
768 0x1C010015: "nca_s_fault_string_too_long",
769 0x1C010017: "nca_s_unsupported_type",
770 0x1C000001: "nca_s_fault_int_div_by_zero",
771 0x1C000002: "nca_s_fault_addr_error",
772 0x1C000003: "nca_s_fault_fp_div_zero",
773 0x1C000004: "nca_s_fault_fp_underflow",
774 0x1C000005: "nca_s_fault_fp_overflow",
775 0x1C000006: "nca_s_fault_invalid_tag",
776 0x1C000007: "nca_s_fault_invalid_bound",
777 0x1C000008: "nca_s_rpc_version_mismatch",
778 0x1C000009: "nca_s_unspec_reject",
779 0x1C00000A: "nca_s_bad_actid",
780 0x1C00000B: "nca_s_who_are_you_failed",
781 0x1C00000C: "nca_s_manager_not_entered",
782 0x1C00000D: "nca_s_fault_cancel",
783 0x1C00000E: "nca_s_fault_ill_inst",
784 0x1C00000F: "nca_s_fault_fp_error",
785 0x1C000010: "nca_s_fault_int_overflow",
786 0x1C000012: "nca_s_fault_unspec",
787 0x1C000013: "nca_s_fault_remote_comm_failure",
788 0x1C000014: "nca_s_fault_pipe_empty",
789 0x1C000015: "nca_s_fault_pipe_closed",
790 0x1C000016: "nca_s_fault_pipe_order",
791 0x1C000017: "nca_s_fault_pipe_discipline",
792 0x1C000018: "nca_s_fault_pipe_comm_error",
793 0x1C000019: "nca_s_fault_pipe_memory",
794 0x1C00001A: "nca_s_fault_context_mismatch",
795 0x1C00001B: "nca_s_fault_remote_no_memory",
796 0x1C00001C: "nca_s_invalid_pres_context_id",
797 0x1C00001D: "nca_s_unsupported_authn_level",
798 0x1C00001F: "nca_s_invalid_checksum",
799 0x1C000020: "nca_s_invalid_crc",
800 0x1C000021: "nca_s_fault_user_defined",
801 0x1C000022: "nca_s_fault_tx_open_failed",
802 0x1C000023: "nca_s_fault_codeset_conv_error",
803 0x1C000024: "nca_s_fault_object_not_found",
804 0x1C000025: "nca_s_fault_no_client_stub",
805 # [MS-ERREF]
806 0x000006D3: "RPC_S_UNKNOWN_AUTHN_SERVICE",
807 0x000006D8: "EPT_S_CANT_PERFORM_OP",
808 0x000006F7: "RPC_X_BAD_STUB_DATA",
809 0x00000719: "RPC_S_NO_INTERFACES",
810 0x0000071A: "RPC_S_CALL_CANCELLED",
811 0x0000071B: "RPC_S_BINDING_INCOMPLETE",
812 0x0000071C: "RPC_S_COMM_FAILURE",
813 0x0000071D: "RPC_S_UNSUPPORTED_AUTHN_LEVEL",
814 0x0000071E: "RPC_S_NO_PRINC_NAME",
815 0x0000071F: "RPC_S_NOT_RPC_ERROR",
816 0x00000720: "RPC_S_UUID_LOCAL_ONLY",
817 0x00000721: "RPC_S_SEC_PKG_ERROR",
818 0x00000722: "RPC_S_NOT_CANCELLED",
819 0x0000076A: "RPC_S_GROUP_MEMBER_NOT_FOUND",
820 0x0000076C: "RPC_S_INVALID_OBJECT",
821 0x80004002: "E_NOINTERFACE",
822 0x80010107: "RPC_E_INVALIDMETHOD",
823 0x80010108: "RPC_E_DISCONNECTED",
824 0x80010109: "RPC_E_RETRY",
825 0x80040153: "REGDB_E_INVALIDVALUE",
826 0x80040154: "REGDB_E_CLASSNOTREG",
827 0x80040155: "REGDB_E_IIDNOTREG",
828 0x800706F7: "COM_X_BAD_STUB_DATA",
829}
831_DCE_RPC_REJECTION_REASONS = {
832 0: "REASON_NOT_SPECIFIED",
833 1: "TEMPORARY_CONGESTION",
834 2: "LOCAL_LIMIT_EXCEEDED",
835 3: "CALLED_PADDR_UNKNOWN",
836 4: "PROTOCOL_VERSION_NOT_SUPPORTED",
837 5: "DEFAULT_CONTEXT_NOT_SUPPORTED",
838 6: "USER_DATA_NOT_READABLE",
839 7: "NO_PSAP_AVAILABLE",
840 8: "AUTHENTICATION_TYPE_NOT_RECOGNIZED",
841 9: "INVALID_CHECKSUM",
842}
845class DceRpc5(DceRpc):
846 """
847 DCE/RPC v5 'connection-oriented' packet
848 """
850 name = "DCE/RPC v5"
851 fields_desc = (
852 [
853 ByteEnumField(
854 "rpc_vers", 5, {4: "4 (connection-less)", 5: "5 (connection-oriented)"}
855 ),
856 ByteField("rpc_vers_minor", 0),
857 ByteEnumField("ptype", 0, DCE_RPC_TYPE),
858 MultipleTypeField(
859 # [MS-RPCE] sect 2.2.2.3
860 [
861 (
862 FlagsField("pfc_flags", 0x3, 8, _DCE_RPC_5_FLAGS_2),
863 lambda pkt: pkt.ptype in [11, 12, 13, 14, 15, 16],
864 )
865 ],
866 FlagsField("pfc_flags", 0x3, 8, _DCE_RPC_5_FLAGS),
867 ),
868 ]
869 + _drep
870 + [
871 ByteField("reserved2", 0),
872 _EField(ShortField("frag_len", None)),
873 _EField(
874 FieldLenField(
875 "auth_len",
876 None,
877 fmt="H",
878 length_of="auth_verifier",
879 adjust=lambda _, x: 0 if not x else (x - 8),
880 )
881 ),
882 _EField(IntField("call_id", None)),
883 # Now let's proceed with trailer fields, i.e. at the end of the PACKET
884 # (below all payloads, etc.). Have a look at Figure 3 in sect 2.2.2.13
885 # of [MS-RPCE] but note the following:
886 # - auth_verifier includes sec_trailer + the authentication token
887 # - auth_padding is the authentication padding
888 # - vt_trailer is the verification trailer
889 ConditionalField(
890 TrailerField(
891 PacketLenField(
892 "auth_verifier",
893 None,
894 CommonAuthVerifier,
895 length_from=lambda pkt: pkt.auth_len + 8,
896 )
897 ),
898 lambda pkt: pkt.auth_len != 0,
899 ),
900 ConditionalField(
901 TrailerField(
902 StrLenField(
903 "auth_padding",
904 None,
905 length_from=lambda pkt: pkt.auth_verifier.auth_pad_length,
906 )
907 ),
908 lambda pkt: pkt.auth_len != 0,
909 ),
910 TrailerField(
911 _VerifTrailerField("vt_trailer", None, DceRpcSecVT),
912 ),
913 ]
914 )
916 def do_dissect(self, s):
917 # Overload do_dissect to only include the current layer in dissection.
918 # This allows to support TrailerFields, even in the case where multiple DceRpc5
919 # packets are concatenated
920 frag_len = self.get_field("frag_len").getfield(self, s[8:10])[1]
921 s, remain = s[:frag_len], s[frag_len:]
922 return super(DceRpc5, self).do_dissect(s) + remain
924 def extract_padding(self, s):
925 # Now, take any data that doesn't fit in the current fragment and make it
926 # padding. The caller is responsible for looking for eventual padding and
927 # creating the next fragment, etc.
928 pay_len = self.frag_len - len(self.original) + len(s)
929 return s[:pay_len], s[pay_len:]
931 def post_build(self, pkt, pay):
932 if (
933 self.auth_verifier
934 and self.auth_padding is None
935 and self.auth_verifier.auth_pad_length is None
936 ):
937 # Compute auth_len and add padding
938 auth_len = self.get_field("auth_len").getfield(self, pkt[10:12])[1] + 8
939 auth_verifier, pay = pay[-auth_len:], pay[:-auth_len]
940 pdu_len = len(pay)
941 if self.payload:
942 pdu_len -= len(self.payload.self_build())
943 padlen = (-pdu_len) % _COMMON_AUTH_PAD
944 auth_verifier = (
945 auth_verifier[:2] + struct.pack("B", padlen) + auth_verifier[3:]
946 )
947 pay = pay + (padlen * b"\x00") + auth_verifier
948 if self.frag_len is None:
949 # Compute frag_len
950 length = len(pkt) + len(pay)
951 pkt = (
952 pkt[:8]
953 + self.get_field("frag_len").addfield(self, b"", length)
954 + pkt[10:]
955 )
956 return pkt + pay
958 def answers(self, pkt):
959 return isinstance(pkt, DceRpc5) and pkt[DceRpc5].call_id == self.call_id
961 @classmethod
962 def tcp_reassemble(cls, data, _, session):
963 if data[0:1] != b"\x05":
964 return
965 endian = struct.unpack("!B", data[4:5])[0] >> 4
966 if endian not in [0, 1]:
967 return
968 length = struct.unpack(("<" if endian else ">") + "H", data[8:10])[0]
969 if len(data) >= length:
970 if conf.dcerpc_session_enable:
971 # If DCE/RPC sessions are enabled, use them !
972 if "dcerpcsess" not in session:
973 session["dcerpcsess"] = dcerpcsess = DceRpcSession()
974 else:
975 dcerpcsess = session["dcerpcsess"]
976 return dcerpcsess.process(DceRpc5(data))
977 return DceRpc5(data)
980# sec 12.6.3.1
983class DceRpc5AbstractSyntax(EPacket):
984 name = "Presentation Syntax (p_syntax_id_t)"
985 fields_desc = [
986 _EField(
987 UUIDEnumField(
988 "if_uuid",
989 None,
990 (
991 # Those are dynamic
992 DCE_RPC_INTERFACES_NAMES.get,
993 lambda x: DCE_RPC_INTERFACES_NAMES_rev.get(x.lower()),
994 ),
995 )
996 ),
997 _EField(IntField("if_version", 3)),
998 ]
1001class DceRpc5TransferSyntax(EPacket):
1002 name = "Presentation Transfer Syntax (p_syntax_id_t)"
1003 fields_desc = [
1004 _EField(
1005 UUIDEnumField(
1006 "if_uuid",
1007 None,
1008 DCE_RPC_TRANSFER_SYNTAXES,
1009 )
1010 ),
1011 _EField(IntField("if_version", 3)),
1012 ]
1015class DceRpc5Context(EPacket):
1016 name = "Presentation Context (p_cont_elem_t)"
1017 fields_desc = [
1018 _EField(ShortField("cont_id", 0)),
1019 FieldLenField("n_transfer_syn", None, count_of="transfer_syntaxes", fmt="B"),
1020 ByteField("reserved", 0),
1021 EPacketField("abstract_syntax", None, DceRpc5AbstractSyntax),
1022 EPacketListField(
1023 "transfer_syntaxes",
1024 None,
1025 DceRpc5TransferSyntax,
1026 count_from=lambda pkt: pkt.n_transfer_syn,
1027 endianness_from=_dce_rpc_endianness,
1028 ),
1029 ]
1032class DceRpc5Result(EPacket):
1033 name = "Context negotiation Result"
1034 fields_desc = [
1035 _EField(
1036 ShortEnumField(
1037 "result", 0, ["acceptance", "user_rejection", "provider_rejection"]
1038 )
1039 ),
1040 _EField(
1041 ShortEnumField(
1042 "reason",
1043 0,
1044 _DCE_RPC_REJECTION_REASONS,
1045 )
1046 ),
1047 EPacketField("transfer_syntax", None, DceRpc5TransferSyntax),
1048 ]
1051class DceRpc5PortAny(EPacket):
1052 name = "Port Any (port_any_t)"
1053 fields_desc = [
1054 _EField(FieldLenField("length", None, length_of="port_spec", fmt="H")),
1055 _EField(StrLenField("port_spec", b"", length_from=lambda pkt: pkt.length)),
1056 ]
1059# sec 12.6.4.3
1062class DceRpc5Bind(_DceRpcPayload):
1063 name = "DCE/RPC v5 - Bind"
1064 fields_desc = [
1065 _EField(ShortField("max_xmit_frag", 5840)),
1066 _EField(ShortField("max_recv_frag", 8192)),
1067 _EField(IntField("assoc_group_id", 0)),
1068 # p_cont_list_t
1069 _EField(
1070 FieldLenField("n_context_elem", None, count_of="context_elem", fmt="B")
1071 ),
1072 StrFixedLenField("reserved", 0, length=3),
1073 EPacketListField(
1074 "context_elem",
1075 [],
1076 DceRpc5Context,
1077 endianness_from=_dce_rpc_endianness,
1078 count_from=lambda pkt: pkt.n_context_elem,
1079 ),
1080 ]
1083bind_layers(DceRpc5, DceRpc5Bind, ptype=11)
1085# sec 12.6.4.4
1088class DceRpc5BindAck(_DceRpcPayload):
1089 name = "DCE/RPC v5 - Bind Ack"
1090 fields_desc = [
1091 _EField(ShortField("max_xmit_frag", 5840)),
1092 _EField(ShortField("max_recv_frag", 8192)),
1093 _EField(IntField("assoc_group_id", 0)),
1094 PadField(
1095 EPacketField("sec_addr", None, DceRpc5PortAny),
1096 align=4,
1097 ),
1098 # p_result_list_t
1099 _EField(FieldLenField("n_results", None, count_of="results", fmt="B")),
1100 StrFixedLenField("reserved", 0, length=3),
1101 EPacketListField(
1102 "results",
1103 [],
1104 DceRpc5Result,
1105 endianness_from=_dce_rpc_endianness,
1106 count_from=lambda pkt: pkt.n_results,
1107 ),
1108 ]
1111bind_layers(DceRpc5, DceRpc5BindAck, ptype=12)
1113# sec 12.6.4.5
1116class DceRpc5Version(EPacket):
1117 name = "version_t"
1118 fields_desc = [
1119 ByteField("major", 0),
1120 ByteField("minor", 0),
1121 ]
1124class DceRpc5BindNak(_DceRpcPayload):
1125 name = "DCE/RPC v5 - Bind Nak"
1126 fields_desc = [
1127 _EField(
1128 ShortEnumField("provider_reject_reason", 0, _DCE_RPC_REJECTION_REASONS)
1129 ),
1130 # p_rt_versions_supported_t
1131 _EField(FieldLenField("n_protocols", None, count_of="protocols", fmt="B")),
1132 EPacketListField(
1133 "protocols",
1134 [],
1135 DceRpc5Version,
1136 count_from=lambda pkt: pkt.n_protocols,
1137 endianness_from=_dce_rpc_endianness,
1138 ),
1139 # [MS-RPCE] sect 2.2.2.9
1140 ConditionalField(
1141 ReversePadField(
1142 _EField(
1143 UUIDEnumField(
1144 "signature",
1145 None,
1146 {
1147 UUID(
1148 "90740320-fad0-11d3-82d7-009027b130ab"
1149 ): "Extended Error",
1150 },
1151 )
1152 ),
1153 align=8,
1154 ),
1155 lambda pkt: pkt.fields.get("signature", None)
1156 or (
1157 pkt.underlayer
1158 and pkt.underlayer.frag_len >= 24 + pkt.n_protocols * 2 + 16
1159 ),
1160 ),
1161 ]
1164bind_layers(DceRpc5, DceRpc5BindNak, ptype=13)
1167# sec 12.6.4.1
1170class DceRpc5AlterContext(_DceRpcPayload):
1171 name = "DCE/RPC v5 - AlterContext"
1172 fields_desc = DceRpc5Bind.fields_desc
1175bind_layers(DceRpc5, DceRpc5AlterContext, ptype=14)
1178# sec 12.6.4.2
1181class DceRpc5AlterContextResp(_DceRpcPayload):
1182 name = "DCE/RPC v5 - AlterContextResp"
1183 fields_desc = DceRpc5BindAck.fields_desc
1186bind_layers(DceRpc5, DceRpc5AlterContextResp, ptype=15)
1188# [MS-RPCE] sect 2.2.2.10 - rpc_auth_3
1191class DceRpc5Auth3(Packet):
1192 name = "DCE/RPC v5 - Auth3"
1193 fields_desc = [StrFixedLenField("pad", b"", length=4)]
1196bind_layers(DceRpc5, DceRpc5Auth3, ptype=16)
1198# sec 12.6.4.7
1201class DceRpc5Fault(_DceRpcPayload):
1202 name = "DCE/RPC v5 - Fault"
1203 fields_desc = [
1204 _EField(IntField("alloc_hint", 0)),
1205 _EField(ShortField("cont_id", 0)),
1206 ByteField("cancel_count", 0),
1207 FlagsField("reserved", 0, -8, {0x1: "RPC extended error"}),
1208 _EField(LEIntEnumField("status", 0, _DCE_RPC_ERROR_CODES)),
1209 IntField("reserved2", 0),
1210 ]
1213bind_layers(DceRpc5, DceRpc5Fault, ptype=3)
1216# sec 12.6.4.9
1219class DceRpc5Request(_DceRpcPayload):
1220 name = "DCE/RPC v5 - Request"
1221 fields_desc = [
1222 _EField(IntField("alloc_hint", 0)),
1223 _EField(ShortField("cont_id", 0)),
1224 _EField(ShortField("opnum", 0)),
1225 ConditionalField(
1226 PadField(
1227 _EField(UUIDField("object", None)),
1228 align=8,
1229 ),
1230 lambda pkt: pkt.underlayer and pkt.underlayer.pfc_flags.PFC_OBJECT_UUID,
1231 ),
1232 ]
1235bind_layers(DceRpc5, DceRpc5Request, ptype=0)
1237# sec 12.6.4.10
1240class DceRpc5Response(_DceRpcPayload):
1241 name = "DCE/RPC v5 - Response"
1242 fields_desc = [
1243 _EField(IntField("alloc_hint", 0)),
1244 _EField(ShortField("cont_id", 0)),
1245 ByteField("cancel_count", 0),
1246 ByteField("reserved", 0),
1247 ]
1250bind_layers(DceRpc5, DceRpc5Response, ptype=2)
1252# --- API
1254DceRpcOp = collections.namedtuple("DceRpcOp", ["request", "response"])
1255DCE_RPC_INTERFACES = {}
1258class DceRpcInterface:
1259 def __init__(self, name, uuid, version_tuple, if_version, opnums):
1260 self.name = name
1261 self.uuid = uuid
1262 self.major_version, self.minor_version = version_tuple
1263 self.if_version = if_version
1264 self.opnums = opnums
1266 def __repr__(self):
1267 return "<DCE/RPC Interface %s v%s.%s>" % (
1268 self.name,
1269 self.major_version,
1270 self.minor_version,
1271 )
1274def register_dcerpc_interface(name, uuid, version, opnums):
1275 """
1276 Register a DCE/RPC interface
1277 """
1278 version_tuple = tuple(map(int, version.split(".")))
1279 assert len(version_tuple) == 2, "Version should be in format 'X.X' !"
1280 if_version = (version_tuple[1] << 16) + version_tuple[0]
1281 if (uuid, if_version) in DCE_RPC_INTERFACES:
1282 # Interface is already registered.
1283 interface = DCE_RPC_INTERFACES[(uuid, if_version)]
1284 if interface.name == name:
1285 if set(opnums) - set(interface.opnums):
1286 # Interface is an extension of a previous interface
1287 interface.opnums.update(opnums)
1288 else:
1289 log_runtime.warning(
1290 "This interface is already registered: %s. Skip" % interface
1291 )
1292 return
1293 else:
1294 raise ValueError(
1295 "An interface with the same UUID is already registered: %s" % interface
1296 )
1297 else:
1298 # New interface
1299 DCE_RPC_INTERFACES_NAMES[uuid] = name
1300 DCE_RPC_INTERFACES_NAMES_rev[name.lower()] = uuid
1301 DCE_RPC_INTERFACES[(uuid, if_version)] = DceRpcInterface(
1302 name,
1303 uuid,
1304 version_tuple,
1305 if_version,
1306 opnums,
1307 )
1308 # bind for build
1309 for opnum, operations in opnums.items():
1310 bind_top_down(DceRpc5Request, operations.request, opnum=opnum)
1313def find_dcerpc_interface(name) -> DceRpcInterface:
1314 """
1315 Find an interface object through the name in the IDL
1316 """
1317 try:
1318 return next(x for x in DCE_RPC_INTERFACES.values() if x.name == name)
1319 except StopIteration:
1320 raise AttributeError("Unknown interface !")
1323COM_INTERFACES = {}
1326class ComInterface:
1327 if_version = 0
1329 def __init__(self, name, uuid, opnums):
1330 self.name = name
1331 self.uuid = uuid
1332 self.opnums = opnums
1334 def __repr__(self):
1335 return "<COM Interface %s>" % (self.name,)
1338def register_com_interface(name, uuid, opnums):
1339 """
1340 Register a COM interface
1341 """
1342 COM_INTERFACES[uuid] = ComInterface(
1343 name,
1344 uuid,
1345 opnums,
1346 )
1347 # bind for build
1348 for opnum, operations in opnums.items():
1349 bind_top_down(DceRpc5Request, operations.request, opnum=opnum)
1352def find_com_interface(name) -> ComInterface:
1353 """
1354 Find an interface object through the name in the IDL
1355 """
1356 try:
1357 return next(x for x in COM_INTERFACES.values() if x.name == name)
1358 except StopIteration:
1359 raise AttributeError("Unknown interface !")
1362# --- NDR fields - [C706] chap 14
1365def _set_ctx_on(f, obj):
1366 if isinstance(f, _NDRPacket):
1367 f.ndr64 = obj.ndr64
1368 f.ndrendian = obj.ndrendian
1369 if isinstance(f, list):
1370 for x in f:
1371 if isinstance(x, _NDRPacket):
1372 x.ndr64 = obj.ndr64
1373 x.ndrendian = obj.ndrendian
1376def _e(ndrendian):
1377 return {"big": ">", "little": "<"}[ndrendian]
1380class _NDRPacket(Packet):
1381 __slots__ = ["ndr64", "ndrendian", "deferred_pointers", "request_packet"]
1383 def __init__(self, *args, **kwargs):
1384 self.ndr64 = kwargs.pop("ndr64", conf.ndr64)
1385 self.ndrendian = kwargs.pop("ndrendian", "little")
1386 # request_packet is used in the session, so that a response packet
1387 # can resolve union arms if the case parameter is in the request.
1388 self.request_packet = kwargs.pop("request_packet", None)
1389 self.deferred_pointers = []
1390 super(_NDRPacket, self).__init__(*args, **kwargs)
1392 def do_dissect(self, s):
1393 _up = self.parent or self.underlayer
1394 if _up and isinstance(_up, _NDRPacket):
1395 self.ndr64 = _up.ndr64
1396 self.ndrendian = _up.ndrendian
1397 else:
1398 # See comment above NDRConstructedType
1399 return NDRConstructedType([]).read_deferred_pointers(
1400 self, super(_NDRPacket, self).do_dissect(s)
1401 )
1402 return super(_NDRPacket, self).do_dissect(s)
1404 def post_dissect(self, s):
1405 if self.deferred_pointers:
1406 # Can't trust the cache if there were deferred pointers
1407 self.raw_packet_cache = None
1408 return s
1410 def do_build(self):
1411 _up = self.parent or self.underlayer
1412 for f in self.fields.values():
1413 _set_ctx_on(f, self)
1414 if not _up or not isinstance(_up, _NDRPacket):
1415 # See comment above NDRConstructedType
1416 return NDRConstructedType([]).add_deferred_pointers(
1417 self, super(_NDRPacket, self).do_build()
1418 )
1419 return super(_NDRPacket, self).do_build()
1421 def default_payload_class(self, pkt):
1422 return conf.padding_layer
1424 def clone_with(self, *args, **kwargs):
1425 pkt = super(_NDRPacket, self).clone_with(*args, **kwargs)
1426 # We need to copy deferred_pointers to not break pointer deferral
1427 # on build.
1428 pkt.deferred_pointers = self.deferred_pointers
1429 pkt.ndr64 = self.ndr64
1430 pkt.ndrendian = self.ndrendian
1431 return pkt
1433 def copy(self):
1434 pkt = super(_NDRPacket, self).copy()
1435 pkt.deferred_pointers = self.deferred_pointers
1436 pkt.ndr64 = self.ndr64
1437 pkt.ndrendian = self.ndrendian
1438 return pkt
1440 def show2(self, dump=False, indent=3, lvl="", label_lvl=""):
1441 return self.__class__(
1442 bytes(self), ndr64=self.ndr64, ndrendian=self.ndrendian
1443 ).show(dump, indent, lvl, label_lvl)
1445 def getfield_and_val(self, attr):
1446 try:
1447 return Packet.getfield_and_val(self, attr)
1448 except ValueError:
1449 if self.request_packet:
1450 # Try to resolve the field from the request on failure
1451 try:
1452 return self.request_packet.getfield_and_val(attr)
1453 except AttributeError:
1454 pass
1455 raise
1457 def valueof(self, request):
1458 """
1459 Util to get the value of a NDRField, ignoring arrays, pointers, etc.
1460 """
1461 val = self
1462 for ndr_field in request.split("."):
1463 fld, fval = val.getfield_and_val(ndr_field)
1464 val = fld.valueof(val, fval)
1465 return val
1468class _NDRAlign:
1469 def padlen(self, flen, pkt):
1470 return -flen % self._align[pkt.ndr64]
1472 def original_length(self, pkt):
1473 # Find the length of the NDR frag to be able to pad properly
1474 while pkt:
1475 par = pkt.parent or pkt.underlayer
1476 if par and isinstance(par, _NDRPacket):
1477 pkt = par
1478 else:
1479 break
1480 return len(pkt.original)
1483class NDRAlign(_NDRAlign, ReversePadField):
1484 """
1485 ReversePadField modified to fit NDR.
1487 - If no align size is specified, use the one from the inner field
1488 - Size is calculated from the beginning of the NDR stream
1489 """
1491 def __init__(self, fld, align, padwith=None):
1492 super(NDRAlign, self).__init__(fld, align=align, padwith=padwith)
1495class _VirtualField(Field):
1496 # Hold a value but doesn't show up when building/dissecting
1497 def addfield(self, pkt, s, x):
1498 return s
1500 def getfield(self, pkt, s):
1501 return s, None
1504class _NDRPacketMetaclass(Packet_metaclass):
1505 def __new__(cls, name, bases, dct):
1506 newcls = super(_NDRPacketMetaclass, cls).__new__(cls, name, bases, dct)
1507 conformants = dct.get("DEPORTED_CONFORMANTS", [])
1508 if conformants:
1509 amount = len(conformants)
1510 if amount == 1:
1511 newcls.fields_desc.insert(
1512 0,
1513 _VirtualField("max_count", None),
1514 )
1515 else:
1516 newcls.fields_desc.insert(
1517 0,
1518 FieldListField(
1519 "max_counts",
1520 [],
1521 _VirtualField("", 0),
1522 count_from=lambda _: amount,
1523 ),
1524 )
1525 return newcls # type: ignore
1528class NDRPacket(_NDRPacket, metaclass=_NDRPacketMetaclass):
1529 """
1530 A NDR Packet. Handles pointer size & endianness
1531 """
1533 __slots__ = ["_align"]
1535 # NDR64 pad structures
1536 # [MS-RPCE] 2.2.5.3.4.1
1537 ALIGNMENT = (1, 1)
1538 # [C706] sect 14.3.7 - Conformants max_count can be added to the beginning
1539 DEPORTED_CONFORMANTS = []
1542# Primitive types
1545class _NDRValueOf:
1546 def valueof(self, pkt, x):
1547 return x
1550class _NDRLenField(_NDRValueOf, Field):
1551 """
1552 Field similar to FieldLenField that takes size_of and adjust as arguments,
1553 and take the value of a size on build.
1554 """
1556 __slots__ = ["size_of", "adjust"]
1558 def __init__(self, *args, **kwargs):
1559 self.size_of = kwargs.pop("size_of", None)
1560 self.adjust = kwargs.pop("adjust", lambda _, x: x)
1561 super(_NDRLenField, self).__init__(*args, **kwargs)
1563 def i2m(self, pkt, x):
1564 if x is None and pkt is not None and self.size_of is not None:
1565 fld, fval = pkt.getfield_and_val(self.size_of)
1566 f = fld.i2len(pkt, fval)
1567 x = self.adjust(pkt, f)
1568 elif x is None:
1569 x = 0
1570 return x
1573class NDRByteField(_NDRLenField, ByteField):
1574 pass
1577class NDRSignedByteField(_NDRLenField, SignedByteField):
1578 pass
1581class _NDRField(_NDRLenField):
1582 FMT = ""
1583 ALIGN = (0, 0)
1585 def getfield(self, pkt, s):
1586 return NDRAlign(
1587 Field("", 0, fmt=_e(pkt.ndrendian) + self.FMT), align=self.ALIGN
1588 ).getfield(pkt, s)
1590 def addfield(self, pkt, s, val):
1591 return NDRAlign(
1592 Field("", 0, fmt=_e(pkt.ndrendian) + self.FMT), align=self.ALIGN
1593 ).addfield(pkt, s, self.i2m(pkt, val))
1596class NDRShortField(_NDRField):
1597 FMT = "H"
1598 ALIGN = (2, 2)
1601class NDRSignedShortField(_NDRField):
1602 FMT = "h"
1603 ALIGN = (2, 2)
1606class NDRIntField(_NDRField):
1607 FMT = "I"
1608 ALIGN = (4, 4)
1611class NDRSignedIntField(_NDRField):
1612 FMT = "i"
1613 ALIGN = (4, 4)
1616class NDRLongField(_NDRField):
1617 FMT = "Q"
1618 ALIGN = (8, 8)
1621class NDRSignedLongField(_NDRField):
1622 FMT = "q"
1623 ALIGN = (8, 8)
1626class NDRIEEEFloatField(_NDRField):
1627 FMT = "f"
1628 ALIGN = (4, 4)
1631class NDRIEEEDoubleField(_NDRField):
1632 FMT = "d"
1633 ALIGN = (8, 8)
1636# Enum types
1639class _NDREnumField(_NDRValueOf, EnumField):
1640 # [MS-RPCE] sect 2.2.5.2 - Enums are 4 octets in NDR64
1641 FMTS = ["H", "I"]
1643 def getfield(self, pkt, s):
1644 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1645 return NDRAlign(Field("", 0, fmt=fmt), align=(2, 4)).getfield(pkt, s)
1647 def addfield(self, pkt, s, val):
1648 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1649 return NDRAlign(Field("", 0, fmt=fmt), align=(2, 4)).addfield(
1650 pkt, s, self.i2m(pkt, val)
1651 )
1654class NDRInt3264EnumField(NDRAlign):
1655 def __init__(self, *args, **kwargs):
1656 super(NDRInt3264EnumField, self).__init__(
1657 _NDREnumField(*args, **kwargs), align=(2, 4)
1658 )
1661class NDRIntEnumField(_NDRValueOf, NDRAlign):
1662 # v1_enum are always 4-octets, even in NDR32
1663 def __init__(self, *args, **kwargs):
1664 super(NDRIntEnumField, self).__init__(
1665 LEIntEnumField(*args, **kwargs), align=(4, 4)
1666 )
1669# Special types
1672class NDRInt3264Field(_NDRLenField):
1673 FMTS = ["I", "Q"]
1675 def getfield(self, pkt, s):
1676 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1677 return NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(pkt, s)
1679 def addfield(self, pkt, s, val):
1680 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1681 return NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(
1682 pkt, s, self.i2m(pkt, val)
1683 )
1686class NDRSignedInt3264Field(NDRInt3264Field):
1687 FMTS = ["i", "q"]
1690# Pointer types
1693class NDRPointer(_NDRPacket):
1694 fields_desc = [
1695 MultipleTypeField(
1696 [(XLELongField("referent_id", 1), lambda pkt: pkt and pkt.ndr64)],
1697 XLEIntField("referent_id", 1),
1698 ),
1699 PacketField("value", None, conf.raw_layer),
1700 ]
1703class NDRFullPointerField(_FieldContainer):
1704 """
1705 A NDR Full/Unique pointer field encapsulation.
1707 :param EMBEDDED: This pointer is embedded. This means that it's representation
1708 will not appear after the pointer (pointer deferral applies).
1709 See [C706] 14.3.12.3 - Algorithm for Deferral of Referents
1710 """
1712 EMBEDDED = False
1713 EMBEDDED_REF = False
1715 def __init__(self, fld, ref=False, fmt="I"):
1716 self.fld = fld
1717 self.ref = ref
1718 self.default = None
1720 def getfield(self, pkt, s):
1721 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
1722 remain, referent_id = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(
1723 pkt, s
1724 )
1726 # No value
1727 if referent_id == 0 and not self.EMBEDDED_REF:
1728 return remain, None
1730 # With value
1731 if self.EMBEDDED:
1732 # deferred
1733 ptr = NDRPointer(
1734 ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, referent_id=referent_id
1735 )
1736 pkt.deferred_pointers.append((ptr, partial(self.fld.getfield, pkt)))
1737 return remain, ptr
1739 remain, val = self.fld.getfield(pkt, remain)
1740 return remain, NDRPointer(
1741 ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, referent_id=referent_id, value=val
1742 )
1744 def addfield(self, pkt, s, val):
1745 if val is not None and not isinstance(val, NDRPointer):
1746 raise ValueError(
1747 "Expected NDRPointer in %s. You are using it wrong!" % self.name
1748 )
1749 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
1750 fld = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8))
1752 # No value
1753 if val is None and not self.EMBEDDED_REF:
1754 return fld.addfield(pkt, s, 0)
1756 # With value
1757 _set_ctx_on(val.value, pkt)
1758 s = fld.addfield(pkt, s, val.referent_id)
1759 if self.EMBEDDED:
1760 # deferred
1761 pkt.deferred_pointers.append(
1762 ((lambda s: self.fld.addfield(pkt, s, val.value)), val)
1763 )
1764 return s
1766 return self.fld.addfield(pkt, s, val.value)
1768 def any2i(self, pkt, x):
1769 # User-friendly helper
1770 if x is not None and not isinstance(x, NDRPointer):
1771 return NDRPointer(
1772 referent_id=0x20000,
1773 value=self.fld.any2i(pkt, x),
1774 )
1775 return x
1777 # Can't use i2repr = Field.i2repr and so on on PY2 :/
1778 def i2repr(self, pkt, val):
1779 return repr(val)
1781 def i2h(self, pkt, x):
1782 return x
1784 def h2i(self, pkt, x):
1785 return x
1787 def i2len(self, pkt, x):
1788 if x is None:
1789 return 0
1790 return self.fld.i2len(pkt, x.value)
1792 def valueof(self, pkt, x):
1793 if x is None:
1794 return x
1795 return self.fld.valueof(pkt, x.value)
1798class NDRFullEmbPointerField(NDRFullPointerField):
1799 """
1800 A NDR Embedded Full pointer.
1802 Same as NDRFullPointerField with EMBEDDED = True.
1803 """
1805 EMBEDDED = True
1808class NDRRefEmbPointerField(NDRFullPointerField):
1809 """
1810 A NDR Embedded Reference pointer.
1812 Same as NDRFullPointerField with EMBEDDED = True and EMBEDDED_REF = True.
1813 """
1815 EMBEDDED = True
1816 EMBEDDED_REF = True
1819# Constructed types
1822# Note: this is utterly complex and will drive you crazy
1824# If you have a NDRPacket that contains a deferred pointer on the top level
1825# (only happens in non DCE/RPC structures, such as in MS-PAC, where you have an NDR
1826# structure encapsulated in a non-NDR structure), there will be left-over deferred
1827# pointers when exiting dissection/build (deferred pointers are only computed when
1828# reaching a field that extends NDRConstructedType, which is normal: if you follow
1829# the DCE/RPC spec, pointers are never deferred in root structures)
1830# Therefore there is a special case forcing the build/dissection of any leftover
1831# pointers in NDRPacket, if Scapy detects that they won't be handled by any parent.
1833# Implementation notes: I chose to set 'handles_deferred' inside the FIELD, rather
1834# than inside the PACKET. This is faster to compute because whether a constructed type
1835# should handle deferral or not is computed only once when loading, therefore Scapy
1836# knows in advance whether to handle deferred pointers or not. But it is technically
1837# incorrect: with this approach, a structure (packet) cannot be used in 2 code paths
1838# that have different pointer managements. I mean by that that if there was a
1839# structure that was directly embedded in a RPC request without a pointer but also
1840# embedded with a pointer in another RPC request, it would break.
1841# Fortunately this isn't the case: structures are never reused for 2 purposes.
1842# (or at least I never seen that... <i hope this works>)
1845class NDRConstructedType(object):
1846 def __init__(self, fields):
1847 self.handles_deferred = False
1848 self.ndr_fields = fields
1849 self.rec_check_deferral()
1851 def rec_check_deferral(self):
1852 # We iterate through the fields within this constructed type.
1853 # If we have a pointer, mark this field as handling deferrance
1854 # and make all sub-constructed types not.
1855 for f in self.ndr_fields:
1856 if isinstance(f, NDRFullPointerField) and f.EMBEDDED:
1857 self.handles_deferred = True
1858 if isinstance(f, NDRConstructedType):
1859 f.rec_check_deferral()
1860 if f.handles_deferred:
1861 self.handles_deferred = True
1862 f.handles_deferred = False
1864 def getfield(self, pkt, s):
1865 s, fval = super(NDRConstructedType, self).getfield(pkt, s)
1866 if isinstance(fval, _NDRPacket):
1867 # If a sub-packet we just dissected has deferred pointers,
1868 # pass it to parent packet to propagate.
1869 pkt.deferred_pointers.extend(fval.deferred_pointers)
1870 del fval.deferred_pointers[:]
1871 if self.handles_deferred:
1872 # This field handles deferral !
1873 s = self.read_deferred_pointers(pkt, s)
1874 return s, fval
1876 def read_deferred_pointers(self, pkt, s):
1877 # Now read content of the pointers that were deferred
1878 q = collections.deque()
1879 q.extend(pkt.deferred_pointers)
1880 del pkt.deferred_pointers[:]
1881 while q:
1882 # Recursively resolve pointers that were deferred
1883 ptr, getfld = q.popleft()
1884 s, val = getfld(s)
1885 ptr.value = val
1886 if isinstance(val, _NDRPacket):
1887 # Pointer resolves to a packet.. that may have deferred pointers?
1888 q.extend(val.deferred_pointers)
1889 del val.deferred_pointers[:]
1890 return s
1892 def addfield(self, pkt, s, val):
1893 try:
1894 s = super(NDRConstructedType, self).addfield(pkt, s, val)
1895 except Exception as ex:
1896 try:
1897 ex.args = (
1898 "While building field '%s': " % self.name + ex.args[0],
1899 ) + ex.args[1:]
1900 except (AttributeError, IndexError):
1901 pass
1902 raise ex
1903 if isinstance(val, _NDRPacket):
1904 # If a sub-packet we just dissected has deferred pointers,
1905 # pass it to parent packet to propagate.
1906 pkt.deferred_pointers.extend(val.deferred_pointers)
1907 del val.deferred_pointers[:]
1908 if self.handles_deferred:
1909 # This field handles deferral !
1910 s = self.add_deferred_pointers(pkt, s)
1911 return s
1913 def add_deferred_pointers(self, pkt, s):
1914 # Now add content of pointers that were deferred
1915 q = collections.deque()
1916 q.extend(pkt.deferred_pointers)
1917 del pkt.deferred_pointers[:]
1918 while q:
1919 addfld, fval = q.popleft()
1920 s = addfld(s)
1921 if isinstance(fval, NDRPointer) and isinstance(fval.value, _NDRPacket):
1922 q.extend(fval.value.deferred_pointers)
1923 del fval.value.deferred_pointers[:]
1924 return s
1927class _NDRPacketField(_NDRValueOf, PacketField):
1928 def m2i(self, pkt, m):
1929 return self.cls(m, ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, _parent=pkt)
1932class _NDRPacketPadField(PadField):
1933 # [MS-RPCE] 2.2.5.3.4.1 Structure with Trailing Gap
1934 # Structures have extra alignment/padding in NDR64.
1935 def padlen(self, flen, pkt):
1936 if pkt.ndr64:
1937 return -flen % self._align[1]
1938 else:
1939 return 0
1942class NDRPacketField(NDRConstructedType, NDRAlign):
1943 def __init__(self, name, default, pkt_cls, **kwargs):
1944 self.DEPORTED_CONFORMANTS = pkt_cls.DEPORTED_CONFORMANTS
1945 self.fld = _NDRPacketField(name, default, pkt_cls=pkt_cls, **kwargs)
1947 # The inner _NDRPacketPadField handles NDR64's trailing gap in
1948 # the case where there a no inner conformants (see [MS-RPCE] 2.2.5.3.4.1)
1949 if self.DEPORTED_CONFORMANTS:
1950 innerfld = self.fld
1951 else:
1952 innerfld = _NDRPacketPadField(self.fld, align=pkt_cls.ALIGNMENT)
1954 # C706 14.3.2 Alignment of Constructed Types is handled by the
1955 # NDRAlign below.
1956 NDRAlign.__init__(
1957 self,
1958 innerfld,
1959 align=pkt_cls.ALIGNMENT,
1960 )
1961 NDRConstructedType.__init__(self, pkt_cls.fields_desc)
1963 def getfield(self, pkt, x):
1964 # Handle deformed conformants max_count here
1965 if self.DEPORTED_CONFORMANTS:
1966 # C706 14.3.2: "In other words, the size information precedes the
1967 # structure and is aligned independently of the structure alignment."
1968 fld = NDRInt3264Field("", 0)
1969 max_counts = []
1970 for _ in self.DEPORTED_CONFORMANTS:
1971 x, max_count = fld.getfield(pkt, x)
1972 max_counts.append(max_count)
1973 res, val = super(NDRPacketField, self).getfield(pkt, x)
1974 if len(max_counts) == 1:
1975 val.max_count = max_counts[0]
1976 else:
1977 val.max_counts = max_counts
1978 return res, val
1979 return super(NDRPacketField, self).getfield(pkt, x)
1981 def addfield(self, pkt, s, x):
1982 # Handle deformed conformants max_count here
1983 if self.DEPORTED_CONFORMANTS:
1984 mcfld = NDRInt3264Field("", 0)
1985 if len(self.DEPORTED_CONFORMANTS) == 1:
1986 max_counts = [x.max_count]
1987 else:
1988 max_counts = x.max_counts
1989 for fldname, max_count in zip(self.DEPORTED_CONFORMANTS, max_counts):
1990 if max_count is None:
1991 fld, val = x.getfield_and_val(fldname)
1992 max_count = fld.i2len(x, val)
1993 s = mcfld.addfield(pkt, s, max_count)
1994 return super(NDRPacketField, self).addfield(pkt, s, x)
1995 return super(NDRPacketField, self).addfield(pkt, s, x)
1998# Array types
2001class _NDRPacketListField(NDRConstructedType, PacketListField):
2002 """
2003 A PacketListField for NDR that can optionally pack the packets into NDRPointers
2004 """
2006 islist = 1
2007 holds_packets = 1
2009 __slots__ = ["ptr_lvl", "fld"]
2011 def __init__(self, name, default, pkt_cls, **kwargs):
2012 self.ptr_lvl = kwargs.pop("ptr_lvl", False)
2013 if self.ptr_lvl:
2014 # TODO: support more than 1 level ?
2015 self.fld = NDRFullEmbPointerField(NDRPacketField("", None, pkt_cls))
2016 else:
2017 self.fld = NDRPacketField("", None, pkt_cls)
2018 PacketListField.__init__(self, name, default, pkt_cls=pkt_cls, **kwargs)
2019 NDRConstructedType.__init__(self, [self.fld])
2021 def m2i(self, pkt, s):
2022 remain, val = self.fld.getfield(pkt, s)
2023 if val is None:
2024 val = NDRNone()
2025 # A mistake here would be to use / instead of add_payload. It adds a copy
2026 # which breaks pointer defferal. Same applies elsewhere
2027 val.add_payload(conf.padding_layer(remain))
2028 return val
2030 def any2i(self, pkt, x):
2031 # User-friendly helper
2032 if isinstance(x, list):
2033 x = [self.fld.any2i(pkt, y) for y in x]
2034 return super(_NDRPacketListField, self).any2i(pkt, x)
2036 def i2m(self, pkt, val):
2037 return self.fld.addfield(pkt, b"", val)
2039 def i2len(self, pkt, x):
2040 return len(x)
2042 def valueof(self, pkt, x):
2043 return [
2044 self.fld.valueof(pkt, y if not isinstance(y, NDRNone) else None) for y in x
2045 ]
2048class NDRFieldListField(NDRConstructedType, FieldListField):
2049 """
2050 A FieldListField for NDR
2051 """
2053 islist = 1
2055 def __init__(self, *args, **kwargs):
2056 if "length_is" in kwargs:
2057 kwargs["count_from"] = kwargs.pop("length_is")
2058 elif "size_is" in kwargs:
2059 kwargs["count_from"] = kwargs.pop("size_is")
2060 FieldListField.__init__(self, *args, **kwargs)
2061 NDRConstructedType.__init__(self, [self.field])
2063 def i2len(self, pkt, x):
2064 return len(x)
2066 def valueof(self, pkt, x):
2067 return [self.field.valueof(pkt, y) for y in x]
2070class NDRVaryingArray(_NDRPacket):
2071 fields_desc = [
2072 MultipleTypeField(
2073 [(LELongField("offset", 0), lambda pkt: pkt and pkt.ndr64)],
2074 LEIntField("offset", 0),
2075 ),
2076 MultipleTypeField(
2077 [
2078 (
2079 LELongField("actual_count", None),
2080 lambda pkt: pkt and pkt.ndr64,
2081 )
2082 ],
2083 LEIntField("actual_count", None),
2084 ),
2085 PacketField("value", None, conf.raw_layer),
2086 ]
2089class _NDRVarField:
2090 """
2091 NDR Varying Array / String field
2092 """
2094 LENGTH_FROM = False
2095 COUNT_FROM = False
2097 def __init__(self, *args, **kwargs):
2098 # We build the length_is function by taking into account both the
2099 # actual_count (from the varying field) and a potentially provided
2100 # length_is field.
2101 if "length_is" in kwargs:
2102 _length_is = kwargs.pop("length_is")
2103 length_is = lambda pkt: (_length_is(pkt.underlayer) or pkt.actual_count)
2104 else:
2105 length_is = lambda pkt: pkt.actual_count
2106 # Pass it to the sub-field (actually subclass)
2107 if self.LENGTH_FROM:
2108 kwargs["length_from"] = length_is
2109 elif self.COUNT_FROM:
2110 kwargs["count_from"] = length_is
2111 # TODO: For now, we do nothing with max_is
2112 if "max_is" in kwargs:
2113 kwargs.pop("max_is")
2114 super(_NDRVarField, self).__init__(*args, **kwargs)
2116 def getfield(self, pkt, s):
2117 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2118 remain, offset = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(pkt, s)
2119 remain, actual_count = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(
2120 pkt, remain
2121 )
2122 final = NDRVaryingArray(
2123 ndr64=pkt.ndr64,
2124 ndrendian=pkt.ndrendian,
2125 offset=offset,
2126 actual_count=actual_count,
2127 _underlayer=pkt,
2128 )
2129 remain, val = super(_NDRVarField, self).getfield(final, remain)
2130 final.value = super(_NDRVarField, self).i2h(pkt, val)
2131 return remain, final
2133 def addfield(self, pkt, s, val):
2134 if not isinstance(val, NDRVaryingArray):
2135 raise ValueError(
2136 "Expected NDRVaryingArray in %s. You are using it wrong!" % self.name
2137 )
2138 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2139 _set_ctx_on(val.value, pkt)
2140 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(pkt, s, val.offset)
2141 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(
2142 pkt,
2143 s,
2144 val.actual_count is None
2145 and super(_NDRVarField, self).i2len(pkt, val.value)
2146 or val.actual_count,
2147 )
2148 return super(_NDRVarField, self).addfield(
2149 pkt, s, super(_NDRVarField, self).h2i(pkt, val.value)
2150 )
2152 def i2len(self, pkt, x):
2153 return super(_NDRVarField, self).i2len(pkt, x.value)
2155 def any2i(self, pkt, x):
2156 # User-friendly helper
2157 if not isinstance(x, NDRVaryingArray):
2158 return NDRVaryingArray(
2159 value=super(_NDRVarField, self).any2i(pkt, x),
2160 )
2161 return x
2163 # Can't use i2repr = Field.i2repr and so on on PY2 :/
2164 def i2repr(self, pkt, val):
2165 return repr(val)
2167 def i2h(self, pkt, x):
2168 return x
2170 def h2i(self, pkt, x):
2171 return x
2173 def valueof(self, pkt, x):
2174 return super(_NDRVarField, self).valueof(pkt, x.value)
2177class NDRConformantArray(_NDRPacket):
2178 fields_desc = [
2179 MultipleTypeField(
2180 [(LELongField("max_count", None), lambda pkt: pkt and pkt.ndr64)],
2181 LEIntField("max_count", None),
2182 ),
2183 MultipleTypeField(
2184 [
2185 (
2186 PacketListField(
2187 "value",
2188 [],
2189 conf.raw_layer,
2190 count_from=lambda pkt: pkt.max_count,
2191 ),
2192 (
2193 lambda pkt: pkt.fields.get("value", None)
2194 and isinstance(pkt.fields["value"][0], Packet),
2195 lambda _, val: val and isinstance(val[0], Packet),
2196 ),
2197 )
2198 ],
2199 FieldListField(
2200 "value", [], LEIntField("", 0), count_from=lambda pkt: pkt.max_count
2201 ),
2202 ),
2203 ]
2206class NDRConformantString(_NDRPacket):
2207 fields_desc = [
2208 MultipleTypeField(
2209 [(LELongField("max_count", None), lambda pkt: pkt and pkt.ndr64)],
2210 LEIntField("max_count", None),
2211 ),
2212 StrField("value", ""),
2213 ]
2216class _NDRConfField:
2217 """
2218 NDR Conformant Array / String field
2219 """
2221 CONFORMANT_STRING = False
2222 LENGTH_FROM = False
2223 COUNT_FROM = False
2225 def __init__(self, *args, **kwargs):
2226 # when conformant_in_struct is True, we remove the level of abstraction
2227 # provided by NDRConformantString / NDRConformantArray because max_count
2228 # is a proper field in the parent packet.
2229 self.conformant_in_struct = kwargs.pop("conformant_in_struct", False)
2230 # size_is/max_is end up here, and is what defines a conformant field.
2231 if "size_is" in kwargs:
2232 size_is = kwargs.pop("size_is")
2233 if self.LENGTH_FROM:
2234 kwargs["length_from"] = size_is
2235 elif self.COUNT_FROM:
2236 kwargs["count_from"] = size_is
2237 # TODO: For now, we do nothing with max_is
2238 if "max_is" in kwargs:
2239 kwargs.pop("max_is")
2240 super(_NDRConfField, self).__init__(*args, **kwargs)
2242 def getfield(self, pkt, s):
2243 # [C706] - 14.3.7 Structures Containing Arrays
2244 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2245 if self.conformant_in_struct:
2246 # [MS-RPCE] 2.2.5.3.4.2 Structure Containing a Conformant Array
2247 # Padding is here: just before the Conformant content
2248 return NDRAlign(
2249 super(_NDRConfField, self),
2250 align=pkt.ALIGNMENT,
2251 ).getfield(pkt, s)
2253 # The max count is aligned as a primitive type
2254 remain, max_count = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(
2255 pkt, s
2256 )
2257 remain, val = super(_NDRConfField, self).getfield(pkt, remain)
2258 return remain, (
2259 NDRConformantString if self.CONFORMANT_STRING else NDRConformantArray
2260 )(ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, max_count=max_count, value=val)
2262 def addfield(self, pkt, s, val):
2263 if self.conformant_in_struct:
2264 # [MS-RPCE] 2.2.5.3.4.2 Structure Containing a Conformant Array
2265 # Padding is here: just before the Conformant content
2266 return NDRAlign(super(_NDRConfField, self), align=pkt.ALIGNMENT).addfield(
2267 pkt, s, val
2268 )
2270 if self.CONFORMANT_STRING and not isinstance(val, NDRConformantString):
2271 raise ValueError(
2272 "Expected NDRConformantString in %s. You are using it wrong!"
2273 % self.name
2274 )
2275 elif not self.CONFORMANT_STRING and not isinstance(val, NDRConformantArray):
2276 raise ValueError(
2277 "Expected NDRConformantArray in %s. You are using it wrong!" % self.name
2278 )
2279 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2280 _set_ctx_on(val.value, pkt)
2281 if val.value and isinstance(val.value[0], NDRVaryingArray):
2282 value = val.value[0]
2283 else:
2284 value = val.value
2285 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(
2286 pkt,
2287 s,
2288 val.max_count is None
2289 and super(_NDRConfField, self).i2len(pkt, value)
2290 or val.max_count,
2291 )
2292 return super(_NDRConfField, self).addfield(pkt, s, value)
2294 def _subval(self, x):
2295 if self.conformant_in_struct:
2296 value = x
2297 elif (
2298 not self.CONFORMANT_STRING
2299 and x.value
2300 and isinstance(x.value[0], NDRVaryingArray)
2301 ):
2302 value = x.value[0]
2303 else:
2304 value = x.value
2305 return value
2307 def i2len(self, pkt, x):
2308 return super(_NDRConfField, self).i2len(pkt, self._subval(x))
2310 def any2i(self, pkt, x):
2311 # User-friendly helper
2312 if self.conformant_in_struct:
2313 return super(_NDRConfField, self).any2i(pkt, x)
2314 if self.CONFORMANT_STRING and not isinstance(x, NDRConformantString):
2315 return NDRConformantString(
2316 value=super(_NDRConfField, self).any2i(pkt, x),
2317 )
2318 elif not isinstance(x, NDRConformantArray):
2319 return NDRConformantArray(
2320 value=super(_NDRConfField, self).any2i(pkt, x),
2321 )
2322 return x
2324 # Can't use i2repr = Field.i2repr and so on on PY2 :/
2325 def i2repr(self, pkt, val):
2326 return repr(val)
2328 def i2h(self, pkt, x):
2329 return x
2331 def h2i(self, pkt, x):
2332 return x
2334 def valueof(self, pkt, x):
2335 return super(_NDRConfField, self).valueof(pkt, self._subval(x))
2338class NDRVarPacketListField(_NDRVarField, _NDRPacketListField):
2339 """
2340 NDR Varying PacketListField. Unused
2341 """
2343 COUNT_FROM = True
2346class NDRConfPacketListField(_NDRConfField, _NDRPacketListField):
2347 """
2348 NDR Conformant PacketListField
2349 """
2351 COUNT_FROM = True
2354class NDRConfVarPacketListField(_NDRConfField, _NDRVarField, _NDRPacketListField):
2355 """
2356 NDR Conformant Varying PacketListField
2357 """
2359 COUNT_FROM = True
2362class NDRConfFieldListField(_NDRConfField, NDRFieldListField):
2363 """
2364 NDR Conformant FieldListField
2365 """
2367 COUNT_FROM = True
2370class NDRConfVarFieldListField(_NDRConfField, _NDRVarField, NDRFieldListField):
2371 """
2372 NDR Conformant Varying FieldListField
2373 """
2375 COUNT_FROM = True
2378# NDR String fields
2381class _NDRUtf16(Field):
2382 def h2i(self, pkt, x):
2383 encoding = {"big": "utf-16be", "little": "utf-16le"}[pkt.ndrendian]
2384 return plain_str(x).encode(encoding)
2386 def i2h(self, pkt, x):
2387 encoding = {"big": "utf-16be", "little": "utf-16le"}[pkt.ndrendian]
2388 return bytes_encode(x).decode(encoding, errors="replace")
2391class NDRConfStrLenField(_NDRConfField, _NDRValueOf, StrLenField):
2392 """
2393 NDR Conformant StrLenField.
2395 This is not a "string" per NDR, but an a conformant byte array
2396 (e.g. tower_octet_string). For ease of use, we implicitly convert
2397 it in specific cases.
2398 """
2400 CONFORMANT_STRING = True
2401 LENGTH_FROM = True
2404class NDRConfStrLenFieldUtf16(_NDRConfField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16):
2405 """
2406 NDR Conformant StrLenFieldUtf16.
2408 See NDRConfStrLenField for comment.
2409 """
2411 CONFORMANT_STRING = True
2412 ON_WIRE_SIZE_UTF16 = False
2413 LENGTH_FROM = True
2416class NDRVarStrNullField(_NDRVarField, _NDRValueOf, StrNullField):
2417 """
2418 NDR Varying StrNullField
2419 """
2421 NULLFIELD = True
2424class NDRVarStrNullFieldUtf16(_NDRVarField, _NDRValueOf, StrNullFieldUtf16, _NDRUtf16):
2425 """
2426 NDR Varying StrNullFieldUtf16
2427 """
2429 NULLFIELD = True
2432class NDRVarStrLenField(_NDRVarField, StrLenField):
2433 """
2434 NDR Varying StrLenField
2435 """
2437 LENGTH_FROM = True
2440class NDRVarStrLenFieldUtf16(_NDRVarField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16):
2441 """
2442 NDR Varying StrLenFieldUtf16
2443 """
2445 ON_WIRE_SIZE_UTF16 = False
2446 LENGTH_FROM = True
2449class NDRConfVarStrLenField(_NDRConfField, _NDRVarField, _NDRValueOf, StrLenField):
2450 """
2451 NDR Conformant Varying StrLenField
2452 """
2454 LENGTH_FROM = True
2457class NDRConfVarStrLenFieldUtf16(
2458 _NDRConfField, _NDRVarField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16
2459):
2460 """
2461 NDR Conformant Varying StrLenFieldUtf16
2462 """
2464 ON_WIRE_SIZE_UTF16 = False
2465 LENGTH_FROM = True
2468class NDRConfVarStrNullField(_NDRConfField, _NDRVarField, _NDRValueOf, StrNullField):
2469 """
2470 NDR Conformant Varying StrNullField
2471 """
2473 NULLFIELD = True
2476class NDRConfVarStrNullFieldUtf16(
2477 _NDRConfField, _NDRVarField, _NDRValueOf, StrNullFieldUtf16, _NDRUtf16
2478):
2479 """
2480 NDR Conformant Varying StrNullFieldUtf16
2481 """
2483 ON_WIRE_SIZE_UTF16 = False
2484 NULLFIELD = True
2487# Union type
2490class NDRUnion(_NDRPacket):
2491 fields_desc = [
2492 IntField("tag", 0),
2493 PacketField("value", None, conf.raw_layer),
2494 ]
2497class _NDRUnionField(MultipleTypeField):
2498 __slots__ = ["switch_fmt", "align"]
2500 def __init__(self, flds, dflt, align, switch_fmt):
2501 self.switch_fmt = switch_fmt
2502 self.align = align
2503 super(_NDRUnionField, self).__init__(flds, dflt)
2505 def getfield(self, pkt, s):
2506 fmt = _e(pkt.ndrendian) + self.switch_fmt[pkt.ndr64]
2507 remain, tag = NDRAlign(Field("", 0, fmt=fmt), align=self.align).getfield(pkt, s)
2508 fld, _ = super(_NDRUnionField, self)._find_fld_pkt_val(pkt, NDRUnion(tag=tag))
2509 remain, val = fld.getfield(pkt, remain)
2510 return remain, NDRUnion(
2511 tag=tag, value=val, ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, _parent=pkt
2512 )
2514 def addfield(self, pkt, s, val):
2515 fmt = _e(pkt.ndrendian) + self.switch_fmt[pkt.ndr64]
2516 if not isinstance(val, NDRUnion):
2517 raise ValueError(
2518 "Expected NDRUnion in %s. You are using it wrong!" % self.name
2519 )
2520 _set_ctx_on(val.value, pkt)
2521 # First, align the whole tag+union against the align param
2522 s = NDRAlign(Field("", 0, fmt=fmt), align=self.align).addfield(pkt, s, val.tag)
2523 # Then, compute the subfield with its own alignment
2524 return super(_NDRUnionField, self).addfield(pkt, s, val)
2526 def _find_fld_pkt_val(self, pkt, val):
2527 fld, val = super(_NDRUnionField, self)._find_fld_pkt_val(pkt, val)
2528 return fld, val.value
2530 # Can't use i2repr = Field.i2repr and so on on PY2 :/
2531 def i2repr(self, pkt, val):
2532 return repr(val)
2534 def i2h(self, pkt, x):
2535 return x
2537 def h2i(self, pkt, x):
2538 return x
2540 def valueof(self, pkt, x):
2541 fld, val = self._find_fld_pkt_val(pkt, x)
2542 return fld.valueof(pkt, x.value)
2545class NDRUnionField(NDRConstructedType, _NDRUnionField):
2546 def __init__(self, flds, dflt, align, switch_fmt):
2547 _NDRUnionField.__init__(self, flds, dflt, align=align, switch_fmt=switch_fmt)
2548 NDRConstructedType.__init__(self, [x[0] for x in flds] + [dflt])
2550 def any2i(self, pkt, x):
2551 # User-friendly helper
2552 if x:
2553 if not isinstance(x, NDRUnion):
2554 raise ValueError("Invalid value for %s; should be NDRUnion" % self.name)
2555 else:
2556 x.value = _NDRUnionField.any2i(self, pkt, x)
2557 return x
2560# Misc
2563class _ProxyArray:
2564 # Hack for recursive fields DEPORTED_CONFORMANTS field
2565 __slots__ = ["getfld"]
2567 def __init__(self, getfld):
2568 self.getfld = getfld
2570 def __len__(self):
2571 try:
2572 return len(self.getfld())
2573 except AttributeError:
2574 return 0
2576 def __iter__(self):
2577 try:
2578 return iter(self.getfld())
2579 except AttributeError:
2580 return iter([])
2583class _ProxyTuple:
2584 # Hack for recursive fields ALIGNMENT field
2585 __slots__ = ["getfld"]
2587 def __init__(self, getfld):
2588 self.getfld = getfld
2590 def __getitem__(self, name):
2591 try:
2592 return self.getfld()[name]
2593 except AttributeError:
2594 raise KeyError
2597def NDRRecursiveClass(clsname):
2598 """
2599 Return a special class that is used for pointer recursion
2600 """
2601 # Get module where this is called
2602 frame = inspect.currentframe().f_back
2603 mod = frame.f_globals["__loader__"].name
2604 getcls = lambda: getattr(importlib.import_module(mod), clsname)
2606 class _REC(NDRPacket):
2607 ALIGNMENT = _ProxyTuple(lambda: getattr(getcls(), "ALIGNMENT"))
2608 DEPORTED_CONFORMANTS = _ProxyArray(
2609 lambda: getattr(getcls(), "DEPORTED_CONFORMANTS")
2610 )
2612 @classmethod
2613 def dispatch_hook(cls, _pkt=None, *args, **kargs):
2614 return getcls()
2616 return _REC
2619# The very few NDR-specific structures
2622class NDRContextHandle(NDRPacket):
2623 ALIGNMENT = (4, 4)
2624 fields_desc = [
2625 LEIntField("attributes", 0),
2626 StrFixedLenField("uuid", b"", length=16),
2627 ]
2629 def guess_payload_class(self, payload):
2630 return conf.padding_layer
2633class NDRNone(NDRPacket):
2634 # This is only used in NDRPacketListField to act as a "None" pointer, and is
2635 # a workaround because the field doesn't support None as a value in the list.
2636 name = "None"
2637 ALIGNMENT = (4, 8)
2638 fields_desc = [
2639 NDRInt3264Field("ptr", 0),
2640 ]
2643# --- Type Serialization Version 1 - [MSRPCE] sect 2.2.6
2646def _get_ndrtype1_endian(pkt):
2647 if pkt.underlayer is None:
2648 return "<"
2649 return {0x00: ">", 0x10: "<"}.get(pkt.underlayer.Endianness, "<")
2652class NDRSerialization1Header(Packet):
2653 fields_desc = [
2654 ByteField("Version", 1),
2655 ByteEnumField("Endianness", 0x10, {0x00: "big", 0x10: "little"}),
2656 LEShortField("CommonHeaderLength", 8),
2657 XLEIntField("Filler", 0xCCCCCCCC),
2658 ]
2660 # Add a bit of goo so that valueof() goes through the header
2662 def _ndrlayer(self):
2663 cur = self
2664 while cur and not isinstance(cur, _NDRPacket) and cur.payload:
2665 cur = cur.payload
2666 if isinstance(cur, NDRPointer):
2667 cur = cur.value
2668 return cur
2670 def getfield_and_val(self, attr):
2671 try:
2672 return Packet.getfield_and_val(self, attr)
2673 except ValueError:
2674 return self._ndrlayer().getfield_and_val(attr)
2676 def valueof(self, name):
2677 return self._ndrlayer().valueof(name)
2680class NDRSerialization1PrivateHeader(Packet):
2681 fields_desc = [
2682 EField(
2683 LEIntField("ObjectBufferLength", 0), endianness_from=_get_ndrtype1_endian
2684 ),
2685 XLEIntField("Filler", 0),
2686 ]
2689def ndr_deserialize1(b, cls, ptr_pack=False):
2690 """
2691 Deserialize Type Serialization Version 1
2692 [MS-RPCE] sect 2.2.6
2694 :param ptr_pack: pack in a pointer to the structure.
2695 """
2696 if issubclass(cls, NDRPacket):
2697 # We use an intermediary class because it uses NDRPacketField which handles
2698 # deported conformant fields
2699 if ptr_pack:
2700 hdrlen = 20
2702 class _cls(NDRPacket):
2703 fields_desc = [NDRFullPointerField(NDRPacketField("pkt", None, cls))]
2705 else:
2706 hdrlen = 16
2708 class _cls(NDRPacket):
2709 fields_desc = [NDRPacketField("pkt", None, cls)]
2711 hdr = NDRSerialization1Header(b[:8]) / NDRSerialization1PrivateHeader(b[8:16])
2712 endian = {0x00: "big", 0x10: "little"}[hdr.Endianness]
2713 padlen = (-hdr.ObjectBufferLength) % _TYPE1_S_PAD
2714 # padlen should be 0 (pad included in length), but some implementations
2715 # implement apparently misread the spec
2716 return (
2717 hdr
2718 / _cls(
2719 b[16 : hdrlen + hdr.ObjectBufferLength],
2720 ndr64=False, # Only NDR32 is supported in Type 1
2721 ndrendian=endian,
2722 ).pkt
2723 / conf.padding_layer(b[hdrlen + padlen + hdr.ObjectBufferLength :])
2724 )
2725 return NDRSerialization1Header(b[:8]) / cls(b[8:])
2728def ndr_serialize1(pkt, ptr_pack=False):
2729 """
2730 Serialize Type Serialization Version 1
2731 [MS-RPCE] sect 2.2.6
2733 :param ptr_pack: pack in a pointer to the structure.
2734 """
2735 pkt = pkt.copy()
2736 endian = getattr(pkt, "ndrendian", "little")
2737 if not isinstance(pkt, NDRSerialization1Header):
2738 if not isinstance(pkt, NDRPacket):
2739 return bytes(NDRSerialization1Header(Endianness=endian) / pkt)
2740 if isinstance(pkt, NDRPointer):
2741 cls = pkt.value.__class__
2742 else:
2743 cls = pkt.__class__
2744 val = pkt
2745 pkt_len = len(pkt)
2746 # ObjectBufferLength:
2747 # > It MUST include the padding length and exclude the header itself
2748 pkt = NDRSerialization1Header(
2749 Endianness=endian
2750 ) / NDRSerialization1PrivateHeader(
2751 ObjectBufferLength=pkt_len + (-pkt_len) % _TYPE1_S_PAD
2752 )
2753 else:
2754 cls = pkt.value.__class__
2755 val = pkt.payload.payload
2756 pkt.payload.remove_payload()
2758 # See above about why we need an intermediary class
2759 if ptr_pack:
2761 class _cls(NDRPacket):
2762 fields_desc = [NDRFullPointerField(NDRPacketField("pkt", None, cls))]
2764 else:
2766 class _cls(NDRPacket):
2767 fields_desc = [NDRPacketField("pkt", None, cls)]
2769 ret = bytes(pkt / _cls(pkt=val, ndr64=False, ndrendian=endian))
2770 return ret + (-len(ret) % _TYPE1_S_PAD) * b"\x00"
2773class _NDRSerializeType1:
2774 def __init__(self, *args, **kwargs):
2775 self.ptr_pack = kwargs.pop("ptr_pack", False)
2776 super(_NDRSerializeType1, self).__init__(*args, **kwargs)
2778 def i2m(self, pkt, val):
2779 return ndr_serialize1(val, ptr_pack=self.ptr_pack)
2781 def m2i(self, pkt, s):
2782 return ndr_deserialize1(s, self.cls, ptr_pack=self.ptr_pack)
2784 def i2len(self, pkt, val):
2785 return len(self.i2m(pkt, val))
2788class NDRSerializeType1PacketField(_NDRSerializeType1, PacketField):
2789 __slots__ = ["ptr_pack"]
2792class NDRSerializeType1PacketLenField(_NDRSerializeType1, PacketLenField):
2793 __slots__ = ["ptr_pack"]
2796class NDRSerializeType1PacketListField(_NDRSerializeType1, PacketListField):
2797 __slots__ = ["ptr_pack"]
2799 def i2len(self, pkt, val):
2800 return sum(len(self.i2m(pkt, p)) for p in val)
2803# --- DCE/RPC session
2806class DceRpcSession(DefaultSession):
2807 """
2808 A DCE/RPC session within a TCP socket.
2809 """
2811 def __init__(self, *args, **kwargs):
2812 self.rpc_bind_interface: Union[DceRpcInterface, ComInterface] = None
2813 self.rpc_bind_is_com: bool = False
2814 self.ndr64 = False
2815 self.ndrendian = "little"
2816 self.support_header_signing = kwargs.pop("support_header_signing", True)
2817 self.header_sign = conf.dcerpc_force_header_signing
2818 self.ssp = kwargs.pop("ssp", None)
2819 self.sspcontext = kwargs.pop("sspcontext", None)
2820 self.auth_level = kwargs.pop("auth_level", None)
2821 self.sent_cont_ids = []
2822 self.cont_id = 0 # Currently selected context
2823 self.auth_context_id = 0 # Currently selected authentication context
2824 self.map_callid_opnum = {}
2825 self.frags = collections.defaultdict(lambda: b"")
2826 self.sniffsspcontexts = {} # Unfinished contexts for passive
2827 if conf.dcerpc_session_enable and conf.winssps_passive:
2828 for ssp in conf.winssps_passive:
2829 self.sniffsspcontexts[ssp] = None
2830 super(DceRpcSession, self).__init__(*args, **kwargs)
2832 def _up_pkt(self, pkt):
2833 """
2834 Common function to handle the DCE/RPC session: what interfaces are bind,
2835 opnums, etc.
2836 """
2837 opnum = None
2838 opts = {}
2839 if DceRpc5Bind in pkt or DceRpc5AlterContext in pkt:
2840 # bind => get which RPC interface
2841 self.sent_cont_ids = [x.cont_id for x in pkt.context_elem]
2842 for ctx in pkt.context_elem:
2843 if_uuid = ctx.abstract_syntax.if_uuid
2844 if_version = ctx.abstract_syntax.if_version
2845 try:
2846 self.rpc_bind_interface = DCE_RPC_INTERFACES[(if_uuid, if_version)]
2847 self.rpc_bind_is_com = False
2848 except KeyError:
2849 try:
2850 self.rpc_bind_interface = COM_INTERFACES[if_uuid]
2851 self.rpc_bind_is_com = True
2852 except KeyError:
2853 self.rpc_bind_interface = None
2854 log_runtime.warning(
2855 "Unknown RPC interface %s. Try loading the IDL" % if_uuid
2856 )
2857 elif DceRpc5BindAck in pkt or DceRpc5AlterContextResp in pkt:
2858 # bind ack => is it NDR64
2859 for i, res in enumerate(pkt.results):
2860 if res.result == 0: # Accepted
2861 # Context
2862 try:
2863 self.cont_id = self.sent_cont_ids[i]
2864 except IndexError:
2865 self.cont_id = 0
2866 finally:
2867 self.sent_cont_ids = []
2869 # Endianness
2870 self.ndrendian = {0: "big", 1: "little"}[pkt[DceRpc5].endian]
2872 # Transfer syntax
2873 if res.transfer_syntax.sprintf("%if_uuid%") == "NDR64":
2874 self.ndr64 = True
2875 elif DceRpc5Request in pkt:
2876 # request => match opnum with callID
2877 opnum = pkt.opnum
2878 if self.rpc_bind_is_com:
2879 self.map_callid_opnum[pkt.call_id] = (
2880 opnum,
2881 pkt[DceRpc5Request].payload.payload,
2882 )
2883 else:
2884 self.map_callid_opnum[pkt.call_id] = opnum, pkt[DceRpc5Request].payload
2885 elif DceRpc5Response in pkt:
2886 # response => get opnum from table
2887 try:
2888 opnum, opts["request_packet"] = self.map_callid_opnum[pkt.call_id]
2889 del self.map_callid_opnum[pkt.call_id]
2890 except KeyError:
2891 log_runtime.info("Unknown call_id %s in DCE/RPC session" % pkt.call_id)
2892 # Bind / Alter request/response specific
2893 if (
2894 DceRpc5Bind in pkt
2895 or DceRpc5AlterContext in pkt
2896 or DceRpc5BindAck in pkt
2897 or DceRpc5AlterContextResp in pkt
2898 ):
2899 # Detect if "Header Signing" is in use
2900 if pkt.pfc_flags & 0x04: # PFC_SUPPORT_HEADER_SIGN
2901 self.header_sign = True
2902 return opnum, opts
2904 # [C706] sect 12.6.2 - Fragmentation and Reassembly
2905 # Since the connection-oriented transport guarantees sequentiality, the receiver
2906 # will always receive the fragments in order.
2908 def _defragment(self, pkt, body=None):
2909 """
2910 Function to defragment DCE/RPC packets.
2911 """
2912 uid = pkt.call_id
2913 if pkt.pfc_flags.PFC_FIRST_FRAG and pkt.pfc_flags.PFC_LAST_FRAG:
2914 # Not fragmented
2915 return body
2916 if pkt.pfc_flags.PFC_FIRST_FRAG or uid in self.frags:
2917 # Packet is fragmented
2918 if body is None:
2919 body = pkt[DceRpc5].payload.payload.original
2920 self.frags[uid] += body
2921 if pkt.pfc_flags.PFC_LAST_FRAG:
2922 return self.frags[uid]
2923 else:
2924 # Not fragmented
2925 return body
2927 # C706 sect 12.5.2.15 - PDU Body Length
2928 # "The maximum PDU body size is 65528 bytes."
2929 MAX_PDU_BODY_SIZE = 4176
2931 def _fragment(self, pkt, body):
2932 """
2933 Function to fragment DCE/RPC packets.
2934 """
2935 if len(body) > self.MAX_PDU_BODY_SIZE:
2936 # Clear any PFC_*_FRAG flag
2937 pkt.pfc_flags &= 0xFC
2939 # Iterate through fragments
2940 cur = None
2941 while body:
2942 # Create a fragment
2943 pkt_frag = pkt.copy()
2945 if cur is None:
2946 # It's the first one
2947 pkt_frag.pfc_flags += "PFC_FIRST_FRAG"
2949 # Split
2950 cur, body = (
2951 body[: self.MAX_PDU_BODY_SIZE],
2952 body[self.MAX_PDU_BODY_SIZE :],
2953 )
2955 if not body:
2956 # It's the last one
2957 pkt_frag.pfc_flags += "PFC_LAST_FRAG"
2958 yield pkt_frag, cur
2959 else:
2960 yield pkt, body
2962 # [MS-RPCE] sect 3.3.1.5.2.2
2964 # The PDU header, PDU body, and sec_trailer MUST be passed in the input message, in
2965 # this order, to GSS_WrapEx, GSS_UnwrapEx, GSS_GetMICEx, and GSS_VerifyMICEx. For
2966 # integrity protection the sign flag for that PDU segment MUST be set to TRUE, else
2967 # it MUST be set to FALSE. For confidentiality protection, the conf_req_flag for
2968 # that PDU segment MUST be set to TRUE, else it MUST be set to FALSE.
2970 # If the authentication level is RPC_C_AUTHN_LEVEL_PKT_PRIVACY, the PDU body will
2971 # be encrypted.
2972 # The PDU body from the output message of GSS_UnwrapEx represents the plain text
2973 # version of the PDU body. The PDU header and sec_trailer output from the output
2974 # message SHOULD be ignored.
2975 # Similarly the signature output SHOULD be ignored.
2977 def in_pkt(self, pkt):
2978 # Check for encrypted payloads
2979 body = None
2980 if conf.raw_layer in pkt.payload:
2981 body = bytes(pkt.payload[conf.raw_layer])
2982 # If we are doing passive sniffing
2983 if conf.dcerpc_session_enable and conf.winssps_passive:
2984 # We have Windows SSPs, and no current context
2985 if pkt.auth_verifier and pkt.auth_verifier.is_ssp():
2986 # This is a bind/alter/auth3 req/resp
2987 for ssp in self.sniffsspcontexts:
2988 self.sniffsspcontexts[ssp], status = ssp.GSS_Passive(
2989 self.sniffsspcontexts[ssp],
2990 pkt.auth_verifier.auth_value,
2991 req_flags=GSS_S_FLAGS.GSS_S_ALLOW_MISSING_BINDINGS
2992 | GSS_C_FLAGS.GSS_C_DCE_STYLE,
2993 )
2994 if status == GSS_S_COMPLETE:
2995 self.auth_level = DCE_C_AUTHN_LEVEL(
2996 int(pkt.auth_verifier.auth_level)
2997 )
2998 self.ssp = ssp
2999 self.sspcontext = self.sniffsspcontexts[ssp]
3000 self.sniffsspcontexts[ssp] = None
3001 elif (
3002 self.sspcontext
3003 and pkt.auth_verifier
3004 and pkt.auth_verifier.is_protected()
3005 and body
3006 ):
3007 # This is a request/response
3008 if self.sspcontext.passive:
3009 self.ssp.GSS_Passive_set_Direction(
3010 self.sspcontext,
3011 IsAcceptor=DceRpc5Response in pkt,
3012 )
3013 if pkt.auth_verifier and pkt.auth_verifier.is_protected() and body:
3014 if self.sspcontext is None:
3015 return pkt
3016 if self.auth_level in (
3017 RPC_C_AUTHN_LEVEL.PKT_INTEGRITY,
3018 RPC_C_AUTHN_LEVEL.PKT_PRIVACY,
3019 ):
3020 # note: 'vt_trailer' is included in the pdu body
3021 # [MS-RPCE] sect 2.2.2.13
3022 # "The data structures MUST only appear in a request PDU, and they
3023 # SHOULD be placed in the PDU immediately after the stub data but
3024 # before the authentication padding octets. Therefore, for security
3025 # purposes, the verification trailer is considered part of the PDU
3026 # body."
3027 if pkt.vt_trailer:
3028 body += bytes(pkt.vt_trailer)
3029 # Account for padding when computing checksum/encryption
3030 if pkt.auth_padding:
3031 body += pkt.auth_padding
3033 # Build pdu_header and sec_trailer
3034 pdu_header = pkt.copy()
3035 sec_trailer = pdu_header.auth_verifier
3036 # sec_trailer: include the sec_trailer but not the Authentication token
3037 authval_len = len(sec_trailer.auth_value)
3038 # Discard everything out of the header
3039 pdu_header.auth_padding = None
3040 pdu_header.auth_verifier = None
3041 pdu_header.payload.payload = NoPayload()
3042 pdu_header.vt_trailer = None
3044 # [MS-RPCE] sect 2.2.2.12
3045 if self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY:
3046 _msgs = self.ssp.GSS_UnwrapEx(
3047 self.sspcontext,
3048 [
3049 # "PDU header"
3050 SSP.WRAP_MSG(
3051 conf_req_flag=False,
3052 sign=self.header_sign,
3053 data=bytes(pdu_header),
3054 ),
3055 # "PDU body"
3056 SSP.WRAP_MSG(
3057 conf_req_flag=True,
3058 sign=True,
3059 data=body,
3060 ),
3061 # "sec_trailer"
3062 SSP.WRAP_MSG(
3063 conf_req_flag=False,
3064 sign=self.header_sign,
3065 data=bytes(sec_trailer)[:-authval_len],
3066 ),
3067 ],
3068 pkt.auth_verifier.auth_value,
3069 )
3070 body = _msgs[1].data # PDU body
3071 elif self.auth_level == RPC_C_AUTHN_LEVEL.PKT_INTEGRITY:
3072 self.ssp.GSS_VerifyMICEx(
3073 self.sspcontext,
3074 [
3075 # "PDU header"
3076 SSP.MIC_MSG(
3077 sign=self.header_sign,
3078 data=bytes(pdu_header),
3079 ),
3080 # "PDU body"
3081 SSP.MIC_MSG(
3082 sign=True,
3083 data=body,
3084 ),
3085 # "sec_trailer"
3086 SSP.MIC_MSG(
3087 sign=self.header_sign,
3088 data=bytes(sec_trailer)[:-authval_len],
3089 ),
3090 ],
3091 pkt.auth_verifier.auth_value,
3092 )
3093 # Put padding back into the header
3094 if pkt.auth_padding:
3095 padlen = len(pkt.auth_padding)
3096 body, pkt.auth_padding = body[:-padlen], body[-padlen:]
3097 # Put back vt_trailer into the header, if present.
3098 if _SECTRAILER_MAGIC in body:
3099 body, pkt.vt_trailer = pkt.get_field("vt_trailer").getfield(
3100 pkt, body
3101 )
3102 # If it's a request / response, could be fragmented
3103 if isinstance(pkt.payload, (DceRpc5Request, DceRpc5Response)) and body:
3104 body = self._defragment(pkt, body)
3105 if not body:
3106 return
3107 # Get opnum and options
3108 opnum, opts = self._up_pkt(pkt)
3109 # Try to parse the payload
3110 if opnum is not None and self.rpc_bind_interface:
3111 # use opnum to parse the payload
3112 is_response = DceRpc5Response in pkt
3113 try:
3114 cls = self.rpc_bind_interface.opnums[opnum][is_response]
3115 except KeyError:
3116 log_runtime.warning(
3117 "Unknown opnum %s for interface %s"
3118 % (opnum, self.rpc_bind_interface)
3119 )
3120 pkt.payload[conf.raw_layer].load = body
3121 return pkt
3122 if body:
3123 orpc = None
3124 if self.rpc_bind_is_com:
3125 # If interface is a COM interface, start off by dissecting the
3126 # ORPCTHIS / ORPCTHAT argument
3127 from scapy.layers.msrpce.raw.ms_dcom import ORPCTHAT, ORPCTHIS
3129 # [MS-DCOM] sect 2.2.13
3130 # "ORPCTHIS and ORPCTHAT structures MUST be marshaled using
3131 # the NDR (32) Transfer Syntax"
3132 if is_response:
3133 orpc = ORPCTHAT(body, ndr64=False)
3134 else:
3135 orpc = ORPCTHIS(body, ndr64=False)
3136 body = orpc.load
3137 orpc.remove_payload()
3138 # Dissect payload using class
3139 try:
3140 payload = cls(
3141 body, ndr64=self.ndr64, ndrendian=self.ndrendian, **opts
3142 )
3143 except Exception:
3144 if conf.debug_dissector:
3145 log_runtime.error("%s dissector failed", cls.__name__)
3146 if cls is not None:
3147 raise
3148 payload = conf.raw_layer(body, _internal=1)
3149 pkt.payload[conf.raw_layer].underlayer.remove_payload()
3150 if conf.padding_layer in payload:
3151 # Most likely, dissection failed.
3152 log_runtime.warning(
3153 "Padding detected when dissecting %s. Looks wrong." % cls
3154 )
3155 pad = payload[conf.padding_layer]
3156 pad.underlayer.payload = conf.raw_layer(load=pad.load)
3157 if orpc is not None:
3158 pkt /= orpc
3159 pkt /= payload
3160 # If a request was encrypted, we need to re-register it once re-parsed.
3161 if not is_response and self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY:
3162 self._up_pkt(pkt)
3163 elif not cls.fields_desc:
3164 # Request class has no payload
3165 pkt /= cls(ndr64=self.ndr64, ndrendian=self.ndrendian, **opts)
3166 elif body:
3167 pkt.payload[conf.raw_layer].load = body
3168 return pkt
3170 def out_pkt(self, pkt):
3171 assert DceRpc5 in pkt
3172 # Register opnum and options
3173 self._up_pkt(pkt)
3175 # If it's a request / response, we can frag it
3176 if isinstance(pkt.payload, (DceRpc5Request, DceRpc5Response)):
3177 # The list of packet responses
3178 pkts = []
3179 # Take the body payload, and eventually split it
3180 body = bytes(pkt.payload.payload)
3182 for pkt, body in self._fragment(pkt, body):
3183 if pkt.auth_verifier is not None:
3184 # Verifier already set
3185 pkts.append(pkt)
3186 continue
3188 # Sign / Encrypt
3189 if self.sspcontext:
3190 signature = None
3191 if self.auth_level in (
3192 RPC_C_AUTHN_LEVEL.PKT_INTEGRITY,
3193 RPC_C_AUTHN_LEVEL.PKT_PRIVACY,
3194 ):
3195 # Remember that vt_trailer is included in the PDU
3196 if pkt.vt_trailer:
3197 body += bytes(pkt.vt_trailer)
3198 # Account for padding when computing checksum/encryption
3199 if pkt.auth_padding is None:
3200 padlen = (-len(body)) % _COMMON_AUTH_PAD # authdata padding
3201 pkt.auth_padding = b"\x00" * padlen
3202 else:
3203 padlen = len(pkt.auth_padding)
3204 # Remember that padding IS SIGNED & ENCRYPTED
3205 body += pkt.auth_padding
3206 # Add the auth_verifier
3207 pkt.auth_verifier = CommonAuthVerifier(
3208 auth_type=self.ssp.auth_type,
3209 auth_level=self.auth_level,
3210 auth_context_id=self.auth_context_id,
3211 auth_pad_length=padlen,
3212 # Note: auth_value should have the correct length because
3213 # when using PFC_SUPPORT_HEADER_SIGN, auth_len
3214 # (and frag_len) is included in the token.. but this
3215 # creates a dependency loop as you'd need to know the token
3216 # length to compute the token. Windows solves this by
3217 # setting the 'Maximum Signature Length' (or something
3218 # similar) beforehand, instead of the real length.
3219 # See `gensec_sig_size` in samba.
3220 auth_value=b"\x00"
3221 * self.ssp.MaximumSignatureLength(self.sspcontext),
3222 )
3223 # Build pdu_header and sec_trailer
3224 pdu_header = pkt.copy()
3225 pdu_header.auth_len = len(pdu_header.auth_verifier) - 8
3226 pdu_header.frag_len = len(pdu_header)
3227 sec_trailer = pdu_header.auth_verifier
3228 # sec_trailer: include the sec_trailer but not the
3229 # Authentication token
3230 authval_len = len(sec_trailer.auth_value)
3231 # sec_trailer.auth_value = None
3232 # Discard everything out of the header
3233 pdu_header.auth_padding = None
3234 pdu_header.auth_verifier = None
3235 pdu_header.payload.payload = NoPayload()
3236 pdu_header.vt_trailer = None
3237 signature = None
3238 # [MS-RPCE] sect 2.2.2.12
3239 if self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY:
3240 _msgs, signature = self.ssp.GSS_WrapEx(
3241 self.sspcontext,
3242 [
3243 # "PDU header"
3244 SSP.WRAP_MSG(
3245 conf_req_flag=False,
3246 sign=self.header_sign,
3247 data=bytes(pdu_header),
3248 ),
3249 # "PDU body"
3250 SSP.WRAP_MSG(
3251 conf_req_flag=True,
3252 sign=True,
3253 data=body,
3254 ),
3255 # "sec_trailer"
3256 SSP.WRAP_MSG(
3257 conf_req_flag=False,
3258 sign=self.header_sign,
3259 data=bytes(sec_trailer)[:-authval_len],
3260 ),
3261 ],
3262 )
3263 s = _msgs[1].data # PDU body
3264 elif self.auth_level == RPC_C_AUTHN_LEVEL.PKT_INTEGRITY:
3265 signature = self.ssp.GSS_GetMICEx(
3266 self.sspcontext,
3267 [
3268 # "PDU header"
3269 SSP.MIC_MSG(
3270 sign=self.header_sign,
3271 data=bytes(pdu_header),
3272 ),
3273 # "PDU body"
3274 SSP.MIC_MSG(
3275 sign=True,
3276 data=body,
3277 ),
3278 # "sec_trailer"
3279 SSP.MIC_MSG(
3280 sign=self.header_sign,
3281 data=bytes(sec_trailer)[:-authval_len],
3282 ),
3283 ],
3284 pkt.auth_verifier.auth_value,
3285 )
3286 s = body
3287 else:
3288 raise ValueError("Impossible")
3289 # Put padding back in the header
3290 if padlen:
3291 s, pkt.auth_padding = s[:-padlen], s[-padlen:]
3292 # Put back vt_trailer into the header
3293 if pkt.vt_trailer:
3294 vtlen = len(pkt.vt_trailer)
3295 s, pkt.vt_trailer = s[:-vtlen], s[-vtlen:]
3296 else:
3297 s = body
3299 # now inject the encrypted payload into the packet
3300 pkt.payload.payload = conf.raw_layer(load=s)
3301 # and the auth_value
3302 if signature:
3303 pkt.auth_verifier.auth_value = signature
3304 else:
3305 pkt.auth_verifier = None
3306 # Add to the list
3307 pkts.append(pkt)
3308 return pkts
3309 else:
3310 return [pkt]
3312 def process(self, pkt: Packet) -> Optional[Packet]:
3313 """
3314 Used when DceRpcSession is used for passive sniffing.
3315 """
3316 pkt = super(DceRpcSession, self).process(pkt)
3317 if pkt is not None and DceRpc5 in pkt:
3318 rpkt = self.in_pkt(pkt)
3319 if rpkt is None:
3320 # We are passively dissecting a fragmented packet. Return
3321 # just the header showing that it was indeed, fragmented.
3322 pkt[DceRpc5].payload.remove_payload()
3323 return pkt
3324 return rpkt
3325 return pkt
3328class DceRpcSocket(StreamSocket):
3329 """
3330 A Wrapper around StreamSocket that uses a DceRpcSession
3331 """
3333 def __init__(self, *args, **kwargs):
3334 self.transport = kwargs.pop("transport", None)
3335 self.session = DceRpcSession(
3336 ssp=kwargs.pop("ssp", None),
3337 auth_level=kwargs.pop("auth_level", None),
3338 support_header_signing=kwargs.pop("support_header_signing", True),
3339 )
3340 super(DceRpcSocket, self).__init__(*args, **kwargs)
3342 def send(self, x, **kwargs):
3343 for pkt in self.session.out_pkt(x):
3344 if self.transport == DCERPC_Transport.NCACN_NP:
3345 # In this case DceRpcSocket wraps a SMB_RPC_SOCKET, call it directly.
3346 self.ins.send(pkt, **kwargs)
3347 else:
3348 super(DceRpcSocket, self).send(pkt, **kwargs)
3350 def recv(self, x=None):
3351 pkt = super(DceRpcSocket, self).recv(x)
3352 if pkt is not None:
3353 return self.session.in_pkt(pkt)
3356# --- TODO cleanup below
3358# Heuristically way to find the payload class
3359#
3360# To add a possible payload to a DCE/RPC packet, one must first create the
3361# packet class, then instead of binding layers using bind_layers, he must
3362# call DceRpcPayload.register_possible_payload() with the payload class as
3363# parameter.
3364#
3365# To be able to decide if the payload class is capable of handling the rest of
3366# the dissection, the classmethod can_handle() should be implemented in the
3367# payload class. This method is given the rest of the string to dissect as
3368# first argument, and the DceRpc packet instance as second argument. Based on
3369# this information, the method must return True if the class is capable of
3370# handling the dissection, False otherwise
3373class DceRpc4Payload(Packet):
3374 """Dummy class which use the dispatch_hook to find the payload class"""
3376 _payload_class = []
3378 @classmethod
3379 def dispatch_hook(cls, _pkt, _underlayer=None, *args, **kargs):
3380 """dispatch_hook to choose among different registered payloads"""
3381 for klass in cls._payload_class:
3382 if hasattr(klass, "can_handle") and klass.can_handle(_pkt, _underlayer):
3383 return klass
3384 log_runtime.warning("DCE/RPC payload class not found or undefined (using Raw)")
3385 return Raw
3387 @classmethod
3388 def register_possible_payload(cls, pay):
3389 """Method to call from possible DCE/RPC endpoint to register it as
3390 possible payload"""
3391 cls._payload_class.append(pay)
3394bind_layers(DceRpc4, DceRpc4Payload)