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
5
6# scapy.contrib.description = DCE/RPC
7# scapy.contrib.status = loads
8
9"""
10DCE/RPC
11Distributed Computing Environment / Remote Procedure Calls
12
13Based on [C706] - aka DCE/RPC 1.1
14https://pubs.opengroup.org/onlinepubs/9629399/toc.pdf
15
16And on [MS-RPCE]
17https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rpce/290c38b1-92fe-4229-91e6-4fc376610c15
18
19.. note::
20 Please read the documentation over
21 `DCE/RPC <https://scapy.readthedocs.io/en/latest/layers/dcerpc.html>`_
22"""
23
24import collections
25import importlib
26import inspect
27import struct
28
29from enum import IntEnum
30from functools import partial
31from uuid import UUID
32
33from scapy.base_classes import Packet_metaclass
34
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
97
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
111
112from scapy.contrib.rtps.common_types import (
113 EField,
114 EPacket,
115 EPacketField,
116 EPacketListField,
117)
118
119# Typing imports
120from typing import (
121 Optional,
122 Union,
123)
124
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
131
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 = {}
183
184
185class DCERPC_Transport(IntEnum):
186 """
187 Protocols identifiers currently supported by Scapy
188 """
189
190 NCACN_IP_TCP = 0x07
191 NCACN_NP = 0x0F
192 # TODO: add more.. if people use them?
193
194
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}
223
224
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 "!"
235
236
237class _EField(EField):
238 def __init__(self, fld):
239 super(_EField, self).__init__(fld, endianness_from=_dce_rpc_endianness)
240
241
242class DceRpc(Packet):
243 """DCE/RPC packet"""
244
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
254
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)
260
261
262bind_bottom_up(TCP, DceRpc, sport=135)
263bind_layers(TCP, DceRpc, dport=135)
264
265
266class _DceRpcPayload(Packet):
267 @property
268 def endianness(self):
269 if not self.underlayer:
270 return "!"
271 return _dce_rpc_endianness(self.underlayer)
272
273
274# sect 12.5
275
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]
282
283
284class DceRpc4(DceRpc):
285 """
286 DCE/RPC v4 'connection-less' packet
287 """
288
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 )
317
318
319# Exceptionally, we define those 3 here.
320
321
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 ]
369
370
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 ]
410
411
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
415
416
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
439
440
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
449
450
451DCE_C_AUTHN_LEVEL = RPC_C_AUTHN_LEVEL # C706 name
452
453
454# C706 sect 13.2.6.1
455
456
457class CommonAuthVerifier(Packet):
458 name = "Common Authentication Verifier"
459 fields_desc = [
460 ByteEnumField(
461 "auth_type",
462 0,
463 RPC_C_AUTHN,
464 ),
465 ByteEnumField("auth_level", 0, RPC_C_AUTHN_LEVEL),
466 ByteField("auth_pad_length", None),
467 ByteField("auth_reserved", 0),
468 XLEIntField("auth_context_id", 0),
469 MultipleTypeField(
470 [
471 # SPNEGO
472 (
473 PacketLenField(
474 "auth_value",
475 GSSAPI_BLOB(),
476 GSSAPI_BLOB,
477 length_from=lambda pkt: pkt.parent.auth_len,
478 ),
479 lambda pkt: pkt.auth_type == 0x09 and pkt.parent and
480 # Bind/Alter
481 pkt.parent.ptype in [11, 12, 13, 14, 15, 16],
482 ),
483 (
484 PacketLenField(
485 "auth_value",
486 GSSAPI_BLOB_SIGNATURE(),
487 GSSAPI_BLOB_SIGNATURE,
488 length_from=lambda pkt: pkt.parent.auth_len,
489 ),
490 lambda pkt: pkt.auth_type == 0x09
491 and pkt.parent
492 and (
493 # Other
494 not pkt.parent
495 or pkt.parent.ptype not in [11, 12, 13, 14, 15, 16]
496 ),
497 ),
498 # Kerberos
499 (
500 PacketLenField(
501 "auth_value",
502 Kerberos(),
503 Kerberos,
504 length_from=lambda pkt: pkt.parent.auth_len,
505 ),
506 lambda pkt: pkt.auth_type == 0x10 and pkt.parent and
507 # Bind/Alter
508 pkt.parent.ptype in [11, 12, 13, 14, 15, 16],
509 ),
510 (
511 PacketLenField(
512 "auth_value",
513 KRB_InnerToken(),
514 KRB_InnerToken,
515 length_from=lambda pkt: pkt.parent.auth_len,
516 ),
517 lambda pkt: pkt.auth_type == 0x10
518 and pkt.parent
519 and (
520 # Other
521 not pkt.parent
522 or pkt.parent.ptype not in [11, 12, 13, 14, 15, 16]
523 ),
524 ),
525 # NTLM
526 (
527 PacketLenField(
528 "auth_value",
529 NTLM_Header(),
530 NTLM_Header,
531 length_from=lambda pkt: pkt.parent.auth_len,
532 ),
533 lambda pkt: pkt.auth_type in [0x0A, 0xFF] and pkt.parent and
534 # Bind/Alter
535 pkt.parent.ptype in [11, 12, 13, 14, 15, 16],
536 ),
537 (
538 PacketLenField(
539 "auth_value",
540 NTLMSSP_MESSAGE_SIGNATURE(),
541 NTLMSSP_MESSAGE_SIGNATURE,
542 length_from=lambda pkt: pkt.parent.auth_len,
543 ),
544 lambda pkt: pkt.auth_type in [0x0A, 0xFF]
545 and pkt.parent
546 and (
547 # Other
548 not pkt.parent
549 or pkt.parent.ptype not in [11, 12, 13, 14, 15, 16]
550 ),
551 ),
552 # NetLogon
553 (
554 PacketLenField(
555 "auth_value",
556 NL_AUTH_MESSAGE(),
557 NL_AUTH_MESSAGE,
558 length_from=lambda pkt: pkt.parent.auth_len,
559 ),
560 lambda pkt: pkt.auth_type == 0x44 and pkt.parent and
561 # Bind/Alter
562 pkt.parent.ptype in [11, 12, 13, 14, 15],
563 ),
564 (
565 PacketLenField(
566 "auth_value",
567 NL_AUTH_SIGNATURE(),
568 NL_AUTH_SIGNATURE,
569 length_from=lambda pkt: pkt.parent.auth_len,
570 ),
571 lambda pkt: pkt.auth_type == 0x44
572 and (
573 # Other
574 not pkt.parent
575 or pkt.parent.ptype not in [11, 12, 13, 14, 15]
576 ),
577 ),
578 ],
579 PacketLenField(
580 "auth_value",
581 None,
582 conf.raw_layer,
583 length_from=lambda pkt: pkt.parent and pkt.parent.auth_len or 0,
584 ),
585 ),
586 ]
587
588 def is_protected(self):
589 if not self.auth_value:
590 return False
591 if self.parent and self.parent.ptype in [11, 12, 13, 14, 15, 16]:
592 return False
593 return True
594
595 def is_ssp(self):
596 if not self.auth_value:
597 return False
598 if self.parent and self.parent.ptype not in [11, 12, 13, 14, 15, 16]:
599 return False
600 return True
601
602 def default_payload_class(self, pkt):
603 return conf.padding_layer
604
605
606# [MS-RPCE] sect 2.2.2.13 - Verification Trailer
607_SECTRAILER_MAGIC = b"\x8a\xe3\x13\x71\x02\xf4\x36\x71"
608
609
610class DceRpcSecVTCommand(Packet):
611 name = "Verification trailer command"
612 fields_desc = [
613 BitField("SEC_VT_MUST_PROCESS_COMMAND", 0, 1, tot_size=-2),
614 BitField("SEC_VT_COMMAND_END", 0, 1),
615 BitEnumField(
616 "Command",
617 0,
618 -14,
619 {
620 0x0001: "SEC_VT_COMMAND_BITMASK_1",
621 0x0002: "SEC_VT_COMMAND_PCONTEXT",
622 0x0003: "SEC_VT_COMMAND_HEADER2",
623 },
624 end_tot_size=-2,
625 ),
626 LenField("Length", None, fmt="<H"),
627 ]
628
629
630# [MS-RPCE] sect 2.2.2.13.2
631
632
633class DceRpcSecVTBitmask(Packet):
634 name = "rpc_sec_vt_bitmask"
635 fields_desc = [
636 LEIntField("bits", 1),
637 ]
638
639 def default_payload_class(self, pkt):
640 return conf.padding_layer
641
642
643bind_layers(DceRpcSecVTCommand, DceRpcSecVTBitmask, Command=0x0001)
644
645
646# [MS-RPCE] sect 2.2.2.13.4
647
648
649class DceRpcSecVTPcontext(Packet):
650 name = "rpc_sec_vt_pcontext"
651 fields_desc = [
652 UUIDEnumField(
653 "InterfaceId",
654 None,
655 (
656 DCE_RPC_INTERFACES_NAMES.get,
657 lambda x: DCE_RPC_INTERFACES_NAMES_rev.get(x.lower()),
658 ),
659 uuid_fmt=UUIDField.FORMAT_LE,
660 ),
661 LEIntField("Version", 0),
662 UUIDEnumField(
663 "TransferSyntax",
664 None,
665 DCE_RPC_TRANSFER_SYNTAXES,
666 uuid_fmt=UUIDField.FORMAT_LE,
667 ),
668 LEIntField("TransferVersion", 0),
669 ]
670
671 def default_payload_class(self, pkt):
672 return conf.padding_layer
673
674
675bind_layers(DceRpcSecVTCommand, DceRpcSecVTPcontext, Command=0x0002)
676
677
678# [MS-RPCE] sect 2.2.2.13.3
679
680
681class DceRpcSecVTHeader2(Packet):
682 name = "rpc_sec_vt_header2"
683 fields_desc = [
684 ByteField("PTYPE", 0),
685 ByteField("Reserved1", 0),
686 LEShortField("Reserved2", 0),
687 LEIntField("drep", 0),
688 LEIntField("call_id", 0),
689 LEShortField("p_cont_id", 0),
690 LEShortField("opnum", 0),
691 ]
692
693 def default_payload_class(self, pkt):
694 return conf.padding_layer
695
696
697bind_layers(DceRpcSecVTCommand, DceRpcSecVTHeader2, Command=0x0003)
698
699
700class DceRpcSecVT(Packet):
701 name = "Verification trailer"
702 fields_desc = [
703 XStrFixedLenField("rpc_sec_verification_trailer", _SECTRAILER_MAGIC, length=8),
704 PacketListField("commands", [], DceRpcSecVTCommand),
705 ]
706
707
708class _VerifTrailerField(PacketField):
709 def getfield(
710 self,
711 pkt,
712 s,
713 ):
714 if _SECTRAILER_MAGIC in s:
715 # a bit ugly
716 ind = s.index(_SECTRAILER_MAGIC)
717 sectrailer_bytes, remain = bytes(s[:-ind]), bytes(s[-ind:])
718 vt_trailer = self.m2i(pkt, sectrailer_bytes)
719 if not isinstance(vt_trailer.payload, NoPayload):
720 # bad parse
721 return s, None
722 return remain, vt_trailer
723 return s, None
724
725
726# sect 12.6.3
727
728
729_DCE_RPC_5_FLAGS = {
730 0x01: "PFC_FIRST_FRAG",
731 0x02: "PFC_LAST_FRAG",
732 0x04: "PFC_PENDING_CANCEL",
733 0x08: "PFC_RESERVED_1",
734 0x10: "PFC_CONC_MPX",
735 0x20: "PFC_DID_NOT_EXECUTE",
736 0x40: "PFC_MAYBE",
737 0x80: "PFC_OBJECT_UUID",
738}
739
740# [MS-RPCE] sect 2.2.2.3
741
742_DCE_RPC_5_FLAGS_2 = _DCE_RPC_5_FLAGS.copy()
743_DCE_RPC_5_FLAGS_2[0x04] = "PFC_SUPPORT_HEADER_SIGN"
744
745
746_DCE_RPC_ERROR_CODES = {
747 # Win32
748 0x776: "OR_INVALID_OXID",
749 0x777: "OR_INVALID_OID",
750 0x778: "OR_INVALID_SET",
751 # Appendix N
752 0x1C010001: "nca_s_comm_failure",
753 0x1C010002: "nca_s_op_rng_error",
754 0x1C010003: "nca_s_unk_if",
755 0x1C010006: "nca_s_wrong_boot_time",
756 0x1C010009: "nca_s_you_crashed",
757 0x1C01000B: "nca_s_proto_error",
758 0x1C010013: "nca_s_out_args_too_big",
759 0x1C010014: "nca_s_server_too_busy",
760 0x1C010015: "nca_s_fault_string_too_long",
761 0x1C010017: "nca_s_unsupported_type",
762 0x1C000001: "nca_s_fault_int_div_by_zero",
763 0x1C000002: "nca_s_fault_addr_error",
764 0x1C000003: "nca_s_fault_fp_div_zero",
765 0x1C000004: "nca_s_fault_fp_underflow",
766 0x1C000005: "nca_s_fault_fp_overflow",
767 0x1C000006: "nca_s_fault_invalid_tag",
768 0x1C000007: "nca_s_fault_invalid_bound",
769 0x1C000008: "nca_s_rpc_version_mismatch",
770 0x1C000009: "nca_s_unspec_reject",
771 0x1C00000A: "nca_s_bad_actid",
772 0x1C00000B: "nca_s_who_are_you_failed",
773 0x1C00000C: "nca_s_manager_not_entered",
774 0x1C00000D: "nca_s_fault_cancel",
775 0x1C00000E: "nca_s_fault_ill_inst",
776 0x1C00000F: "nca_s_fault_fp_error",
777 0x1C000010: "nca_s_fault_int_overflow",
778 0x1C000012: "nca_s_fault_unspec",
779 0x1C000013: "nca_s_fault_remote_comm_failure",
780 0x1C000014: "nca_s_fault_pipe_empty",
781 0x1C000015: "nca_s_fault_pipe_closed",
782 0x1C000016: "nca_s_fault_pipe_order",
783 0x1C000017: "nca_s_fault_pipe_discipline",
784 0x1C000018: "nca_s_fault_pipe_comm_error",
785 0x1C000019: "nca_s_fault_pipe_memory",
786 0x1C00001A: "nca_s_fault_context_mismatch",
787 0x1C00001B: "nca_s_fault_remote_no_memory",
788 0x1C00001C: "nca_s_invalid_pres_context_id",
789 0x1C00001D: "nca_s_unsupported_authn_level",
790 0x1C00001F: "nca_s_invalid_checksum",
791 0x1C000020: "nca_s_invalid_crc",
792 0x1C000021: "nca_s_fault_user_defined",
793 0x1C000022: "nca_s_fault_tx_open_failed",
794 0x1C000023: "nca_s_fault_codeset_conv_error",
795 0x1C000024: "nca_s_fault_object_not_found",
796 0x1C000025: "nca_s_fault_no_client_stub",
797 # [MS-ERREF]
798 0x000006D3: "RPC_S_UNKNOWN_AUTHN_SERVICE",
799 0x000006D8: "EPT_S_CANT_PERFORM_OP",
800 0x000006F7: "RPC_X_BAD_STUB_DATA",
801 0x00000719: "RPC_S_NO_INTERFACES",
802 0x0000071A: "RPC_S_CALL_CANCELLED",
803 0x0000071B: "RPC_S_BINDING_INCOMPLETE",
804 0x0000071C: "RPC_S_COMM_FAILURE",
805 0x0000071D: "RPC_S_UNSUPPORTED_AUTHN_LEVEL",
806 0x0000071E: "RPC_S_NO_PRINC_NAME",
807 0x0000071F: "RPC_S_NOT_RPC_ERROR",
808 0x00000720: "RPC_S_UUID_LOCAL_ONLY",
809 0x00000721: "RPC_S_SEC_PKG_ERROR",
810 0x00000722: "RPC_S_NOT_CANCELLED",
811 0x0000076A: "RPC_S_GROUP_MEMBER_NOT_FOUND",
812 0x0000076C: "RPC_S_INVALID_OBJECT",
813 0x80004002: "E_NOINTERFACE",
814 0x80010107: "RPC_E_INVALIDMETHOD",
815 0x80010108: "RPC_E_DISCONNECTED",
816 0x80010109: "RPC_E_RETRY",
817 0x80040153: "REGDB_E_INVALIDVALUE",
818 0x80040154: "REGDB_E_CLASSNOTREG",
819 0x80040155: "REGDB_E_IIDNOTREG",
820 0x800706F7: "COM_X_BAD_STUB_DATA",
821}
822
823_DCE_RPC_REJECTION_REASONS = {
824 0: "REASON_NOT_SPECIFIED",
825 1: "TEMPORARY_CONGESTION",
826 2: "LOCAL_LIMIT_EXCEEDED",
827 3: "CALLED_PADDR_UNKNOWN",
828 4: "PROTOCOL_VERSION_NOT_SUPPORTED",
829 5: "DEFAULT_CONTEXT_NOT_SUPPORTED",
830 6: "USER_DATA_NOT_READABLE",
831 7: "NO_PSAP_AVAILABLE",
832 8: "AUTHENTICATION_TYPE_NOT_RECOGNIZED",
833 9: "INVALID_CHECKSUM",
834}
835
836
837class DceRpc5(DceRpc):
838 """
839 DCE/RPC v5 'connection-oriented' packet
840 """
841
842 name = "DCE/RPC v5"
843 fields_desc = (
844 [
845 ByteEnumField(
846 "rpc_vers", 5, {4: "4 (connection-less)", 5: "5 (connection-oriented)"}
847 ),
848 ByteField("rpc_vers_minor", 0),
849 ByteEnumField("ptype", 0, DCE_RPC_TYPE),
850 MultipleTypeField(
851 # [MS-RPCE] sect 2.2.2.3
852 [
853 (
854 FlagsField("pfc_flags", 0x3, 8, _DCE_RPC_5_FLAGS_2),
855 lambda pkt: pkt.ptype in [11, 12, 13, 14, 15, 16],
856 )
857 ],
858 FlagsField("pfc_flags", 0x3, 8, _DCE_RPC_5_FLAGS),
859 ),
860 ]
861 + _drep
862 + [
863 ByteField("reserved2", 0),
864 _EField(ShortField("frag_len", None)),
865 _EField(
866 FieldLenField(
867 "auth_len",
868 None,
869 fmt="H",
870 length_of="auth_verifier",
871 adjust=lambda _, x: 0 if not x else (x - 8),
872 )
873 ),
874 _EField(IntField("call_id", None)),
875 # Now let's proceed with trailer fields, i.e. at the end of the PACKET
876 # (below all payloads, etc.). Have a look at Figure 3 in sect 2.2.2.13
877 # of [MS-RPCE] but note the following:
878 # - auth_verifier includes sec_trailer + the authentication token
879 # - auth_padding is the authentication padding
880 # - vt_trailer is the verification trailer
881 ConditionalField(
882 TrailerField(
883 PacketLenField(
884 "auth_verifier",
885 None,
886 CommonAuthVerifier,
887 length_from=lambda pkt: pkt.auth_len + 8,
888 )
889 ),
890 lambda pkt: pkt.auth_len != 0,
891 ),
892 ConditionalField(
893 TrailerField(
894 StrLenField(
895 "auth_padding",
896 None,
897 length_from=lambda pkt: pkt.auth_verifier.auth_pad_length,
898 )
899 ),
900 lambda pkt: pkt.auth_len != 0,
901 ),
902 TrailerField(
903 _VerifTrailerField("vt_trailer", None, DceRpcSecVT),
904 ),
905 ]
906 )
907
908 def do_dissect(self, s):
909 # Overload do_dissect to only include the current layer in dissection.
910 # This allows to support TrailerFields, even in the case where multiple DceRpc5
911 # packets are concatenated
912 frag_len = self.get_field("frag_len").getfield(self, s[8:10])[1]
913 s, remain = s[:frag_len], s[frag_len:]
914 return super(DceRpc5, self).do_dissect(s) + remain
915
916 def extract_padding(self, s):
917 # Now, take any data that doesn't fit in the current fragment and make it
918 # padding. The caller is responsible for looking for eventual padding and
919 # creating the next fragment, etc.
920 pay_len = self.frag_len - len(self.original) + len(s)
921 return s[:pay_len], s[pay_len:]
922
923 def post_build(self, pkt, pay):
924 if (
925 self.auth_verifier
926 and self.auth_padding is None
927 and self.auth_verifier.auth_pad_length is None
928 ):
929 # Compute auth_len and add padding
930 auth_len = self.get_field("auth_len").getfield(self, pkt[10:12])[1] + 8
931 auth_verifier, pay = pay[-auth_len:], pay[:-auth_len]
932 pdu_len = len(pay)
933 if self.payload:
934 pdu_len -= len(self.payload.self_build())
935 padlen = (-pdu_len) % _COMMON_AUTH_PAD
936 auth_verifier = (
937 auth_verifier[:2] + struct.pack("B", padlen) + auth_verifier[3:]
938 )
939 pay = pay + (padlen * b"\x00") + auth_verifier
940 if self.frag_len is None:
941 # Compute frag_len
942 length = len(pkt) + len(pay)
943 pkt = (
944 pkt[:8]
945 + self.get_field("frag_len").addfield(self, b"", length)
946 + pkt[10:]
947 )
948 return pkt + pay
949
950 def answers(self, pkt):
951 return isinstance(pkt, DceRpc5) and pkt[DceRpc5].call_id == self.call_id
952
953 @classmethod
954 def tcp_reassemble(cls, data, _, session):
955 if data[0:1] != b"\x05":
956 return
957 endian = struct.unpack("!B", data[4:5])[0] >> 4
958 if endian not in [0, 1]:
959 return
960 length = struct.unpack(("<" if endian else ">") + "H", data[8:10])[0]
961 if len(data) >= length:
962 if conf.dcerpc_session_enable:
963 # If DCE/RPC sessions are enabled, use them !
964 if "dcerpcsess" not in session:
965 session["dcerpcsess"] = dcerpcsess = DceRpcSession()
966 else:
967 dcerpcsess = session["dcerpcsess"]
968 return dcerpcsess.process(DceRpc5(data))
969 return DceRpc5(data)
970
971
972# sec 12.6.3.1
973
974
975class DceRpc5AbstractSyntax(EPacket):
976 name = "Presentation Syntax (p_syntax_id_t)"
977 fields_desc = [
978 _EField(
979 UUIDEnumField(
980 "if_uuid",
981 None,
982 (
983 # Those are dynamic
984 DCE_RPC_INTERFACES_NAMES.get,
985 lambda x: DCE_RPC_INTERFACES_NAMES_rev.get(x.lower()),
986 ),
987 )
988 ),
989 _EField(IntField("if_version", 3)),
990 ]
991
992
993class DceRpc5TransferSyntax(EPacket):
994 name = "Presentation Transfer Syntax (p_syntax_id_t)"
995 fields_desc = [
996 _EField(
997 UUIDEnumField(
998 "if_uuid",
999 None,
1000 DCE_RPC_TRANSFER_SYNTAXES,
1001 )
1002 ),
1003 _EField(IntField("if_version", 3)),
1004 ]
1005
1006
1007class DceRpc5Context(EPacket):
1008 name = "Presentation Context (p_cont_elem_t)"
1009 fields_desc = [
1010 _EField(ShortField("cont_id", 0)),
1011 FieldLenField("n_transfer_syn", None, count_of="transfer_syntaxes", fmt="B"),
1012 ByteField("reserved", 0),
1013 EPacketField("abstract_syntax", None, DceRpc5AbstractSyntax),
1014 EPacketListField(
1015 "transfer_syntaxes",
1016 None,
1017 DceRpc5TransferSyntax,
1018 count_from=lambda pkt: pkt.n_transfer_syn,
1019 endianness_from=_dce_rpc_endianness,
1020 ),
1021 ]
1022
1023
1024class DceRpc5Result(EPacket):
1025 name = "Context negotiation Result"
1026 fields_desc = [
1027 _EField(
1028 ShortEnumField(
1029 "result", 0, ["acceptance", "user_rejection", "provider_rejection"]
1030 )
1031 ),
1032 _EField(
1033 ShortEnumField(
1034 "reason",
1035 0,
1036 _DCE_RPC_REJECTION_REASONS,
1037 )
1038 ),
1039 EPacketField("transfer_syntax", None, DceRpc5TransferSyntax),
1040 ]
1041
1042
1043class DceRpc5PortAny(EPacket):
1044 name = "Port Any (port_any_t)"
1045 fields_desc = [
1046 _EField(FieldLenField("length", None, length_of="port_spec", fmt="H")),
1047 _EField(StrLenField("port_spec", b"", length_from=lambda pkt: pkt.length)),
1048 ]
1049
1050
1051# sec 12.6.4.3
1052
1053
1054class DceRpc5Bind(_DceRpcPayload):
1055 name = "DCE/RPC v5 - Bind"
1056 fields_desc = [
1057 _EField(ShortField("max_xmit_frag", 5840)),
1058 _EField(ShortField("max_recv_frag", 8192)),
1059 _EField(IntField("assoc_group_id", 0)),
1060 # p_cont_list_t
1061 _EField(
1062 FieldLenField("n_context_elem", None, count_of="context_elem", fmt="B")
1063 ),
1064 StrFixedLenField("reserved", 0, length=3),
1065 EPacketListField(
1066 "context_elem",
1067 [],
1068 DceRpc5Context,
1069 endianness_from=_dce_rpc_endianness,
1070 count_from=lambda pkt: pkt.n_context_elem,
1071 ),
1072 ]
1073
1074
1075bind_layers(DceRpc5, DceRpc5Bind, ptype=11)
1076
1077# sec 12.6.4.4
1078
1079
1080class DceRpc5BindAck(_DceRpcPayload):
1081 name = "DCE/RPC v5 - Bind Ack"
1082 fields_desc = [
1083 _EField(ShortField("max_xmit_frag", 5840)),
1084 _EField(ShortField("max_recv_frag", 8192)),
1085 _EField(IntField("assoc_group_id", 0)),
1086 PadField(
1087 EPacketField("sec_addr", None, DceRpc5PortAny),
1088 align=4,
1089 ),
1090 # p_result_list_t
1091 _EField(FieldLenField("n_results", None, count_of="results", fmt="B")),
1092 StrFixedLenField("reserved", 0, length=3),
1093 EPacketListField(
1094 "results",
1095 [],
1096 DceRpc5Result,
1097 endianness_from=_dce_rpc_endianness,
1098 count_from=lambda pkt: pkt.n_results,
1099 ),
1100 ]
1101
1102
1103bind_layers(DceRpc5, DceRpc5BindAck, ptype=12)
1104
1105# sec 12.6.4.5
1106
1107
1108class DceRpc5Version(EPacket):
1109 name = "version_t"
1110 fields_desc = [
1111 ByteField("major", 0),
1112 ByteField("minor", 0),
1113 ]
1114
1115
1116class DceRpc5BindNak(_DceRpcPayload):
1117 name = "DCE/RPC v5 - Bind Nak"
1118 fields_desc = [
1119 _EField(
1120 ShortEnumField("provider_reject_reason", 0, _DCE_RPC_REJECTION_REASONS)
1121 ),
1122 # p_rt_versions_supported_t
1123 _EField(FieldLenField("n_protocols", None, count_of="protocols", fmt="B")),
1124 EPacketListField(
1125 "protocols",
1126 [],
1127 DceRpc5Version,
1128 count_from=lambda pkt: pkt.n_protocols,
1129 endianness_from=_dce_rpc_endianness,
1130 ),
1131 # [MS-RPCE] sect 2.2.2.9
1132 ConditionalField(
1133 ReversePadField(
1134 _EField(
1135 UUIDEnumField(
1136 "signature",
1137 None,
1138 {
1139 UUID(
1140 "90740320-fad0-11d3-82d7-009027b130ab"
1141 ): "Extended Error",
1142 },
1143 )
1144 ),
1145 align=8,
1146 ),
1147 lambda pkt: pkt.fields.get("signature", None)
1148 or (
1149 pkt.underlayer
1150 and pkt.underlayer.frag_len >= 24 + pkt.n_protocols * 2 + 16
1151 ),
1152 ),
1153 ]
1154
1155
1156bind_layers(DceRpc5, DceRpc5BindNak, ptype=13)
1157
1158
1159# sec 12.6.4.1
1160
1161
1162class DceRpc5AlterContext(_DceRpcPayload):
1163 name = "DCE/RPC v5 - AlterContext"
1164 fields_desc = DceRpc5Bind.fields_desc
1165
1166
1167bind_layers(DceRpc5, DceRpc5AlterContext, ptype=14)
1168
1169
1170# sec 12.6.4.2
1171
1172
1173class DceRpc5AlterContextResp(_DceRpcPayload):
1174 name = "DCE/RPC v5 - AlterContextResp"
1175 fields_desc = DceRpc5BindAck.fields_desc
1176
1177
1178bind_layers(DceRpc5, DceRpc5AlterContextResp, ptype=15)
1179
1180# [MS-RPCE] sect 2.2.2.10 - rpc_auth_3
1181
1182
1183class DceRpc5Auth3(Packet):
1184 name = "DCE/RPC v5 - Auth3"
1185 fields_desc = [StrFixedLenField("pad", b"", length=4)]
1186
1187
1188bind_layers(DceRpc5, DceRpc5Auth3, ptype=16)
1189
1190# sec 12.6.4.7
1191
1192
1193class DceRpc5Fault(_DceRpcPayload):
1194 name = "DCE/RPC v5 - Fault"
1195 fields_desc = [
1196 _EField(IntField("alloc_hint", 0)),
1197 _EField(ShortField("cont_id", 0)),
1198 ByteField("cancel_count", 0),
1199 FlagsField("reserved", 0, -8, {0x1: "RPC extended error"}),
1200 _EField(LEIntEnumField("status", 0, _DCE_RPC_ERROR_CODES)),
1201 IntField("reserved2", 0),
1202 ]
1203
1204
1205bind_layers(DceRpc5, DceRpc5Fault, ptype=3)
1206
1207
1208# sec 12.6.4.9
1209
1210
1211class DceRpc5Request(_DceRpcPayload):
1212 name = "DCE/RPC v5 - Request"
1213 fields_desc = [
1214 _EField(IntField("alloc_hint", 0)),
1215 _EField(ShortField("cont_id", 0)),
1216 _EField(ShortField("opnum", 0)),
1217 ConditionalField(
1218 PadField(
1219 _EField(UUIDField("object", None)),
1220 align=8,
1221 ),
1222 lambda pkt: pkt.underlayer and pkt.underlayer.pfc_flags.PFC_OBJECT_UUID,
1223 ),
1224 ]
1225
1226
1227bind_layers(DceRpc5, DceRpc5Request, ptype=0)
1228
1229# sec 12.6.4.10
1230
1231
1232class DceRpc5Response(_DceRpcPayload):
1233 name = "DCE/RPC v5 - Response"
1234 fields_desc = [
1235 _EField(IntField("alloc_hint", 0)),
1236 _EField(ShortField("cont_id", 0)),
1237 ByteField("cancel_count", 0),
1238 ByteField("reserved", 0),
1239 ]
1240
1241
1242bind_layers(DceRpc5, DceRpc5Response, ptype=2)
1243
1244# --- API
1245
1246DceRpcOp = collections.namedtuple("DceRpcOp", ["request", "response"])
1247DCE_RPC_INTERFACES = {}
1248
1249
1250class DceRpcInterface:
1251 def __init__(self, name, uuid, version_tuple, if_version, opnums):
1252 self.name = name
1253 self.uuid = uuid
1254 self.major_version, self.minor_version = version_tuple
1255 self.if_version = if_version
1256 self.opnums = opnums
1257
1258 def __repr__(self):
1259 return "<DCE/RPC Interface %s v%s.%s>" % (
1260 self.name,
1261 self.major_version,
1262 self.minor_version,
1263 )
1264
1265
1266def register_dcerpc_interface(name, uuid, version, opnums):
1267 """
1268 Register a DCE/RPC interface
1269 """
1270 version_tuple = tuple(map(int, version.split(".")))
1271 assert len(version_tuple) == 2, "Version should be in format 'X.X' !"
1272 if_version = (version_tuple[1] << 16) + version_tuple[0]
1273 if (uuid, if_version) in DCE_RPC_INTERFACES:
1274 # Interface is already registered.
1275 interface = DCE_RPC_INTERFACES[(uuid, if_version)]
1276 if interface.name == name:
1277 if set(opnums) - set(interface.opnums):
1278 # Interface is an extension of a previous interface
1279 interface.opnums.update(opnums)
1280 else:
1281 log_runtime.warning(
1282 "This interface is already registered: %s. Skip" % interface
1283 )
1284 return
1285 else:
1286 raise ValueError(
1287 "An interface with the same UUID is already registered: %s" % interface
1288 )
1289 else:
1290 # New interface
1291 DCE_RPC_INTERFACES_NAMES[uuid] = name
1292 DCE_RPC_INTERFACES_NAMES_rev[name.lower()] = uuid
1293 DCE_RPC_INTERFACES[(uuid, if_version)] = DceRpcInterface(
1294 name,
1295 uuid,
1296 version_tuple,
1297 if_version,
1298 opnums,
1299 )
1300 # bind for build
1301 for opnum, operations in opnums.items():
1302 bind_top_down(DceRpc5Request, operations.request, opnum=opnum)
1303
1304
1305def find_dcerpc_interface(name) -> DceRpcInterface:
1306 """
1307 Find an interface object through the name in the IDL
1308 """
1309 try:
1310 return next(x for x in DCE_RPC_INTERFACES.values() if x.name == name)
1311 except StopIteration:
1312 raise AttributeError("Unknown interface !")
1313
1314
1315COM_INTERFACES = {}
1316
1317
1318class ComInterface:
1319 if_version = 0
1320
1321 def __init__(self, name, uuid, opnums):
1322 self.name = name
1323 self.uuid = uuid
1324 self.opnums = opnums
1325
1326 def __repr__(self):
1327 return "<COM Interface %s>" % (self.name,)
1328
1329
1330def register_com_interface(name, uuid, opnums):
1331 """
1332 Register a COM interface
1333 """
1334 COM_INTERFACES[uuid] = ComInterface(
1335 name,
1336 uuid,
1337 opnums,
1338 )
1339 # bind for build
1340 for opnum, operations in opnums.items():
1341 bind_top_down(DceRpc5Request, operations.request, opnum=opnum)
1342
1343
1344def find_com_interface(name) -> ComInterface:
1345 """
1346 Find an interface object through the name in the IDL
1347 """
1348 try:
1349 return next(x for x in COM_INTERFACES.values() if x.name == name)
1350 except StopIteration:
1351 raise AttributeError("Unknown interface !")
1352
1353
1354# --- NDR fields - [C706] chap 14
1355
1356
1357def _set_ctx_on(f, obj):
1358 if isinstance(f, _NDRPacket):
1359 f.ndr64 = obj.ndr64
1360 f.ndrendian = obj.ndrendian
1361 if isinstance(f, list):
1362 for x in f:
1363 if isinstance(x, _NDRPacket):
1364 x.ndr64 = obj.ndr64
1365 x.ndrendian = obj.ndrendian
1366
1367
1368def _e(ndrendian):
1369 return {"big": ">", "little": "<"}[ndrendian]
1370
1371
1372class _NDRPacket(Packet):
1373 __slots__ = ["ndr64", "ndrendian", "deferred_pointers", "request_packet"]
1374
1375 def __init__(self, *args, **kwargs):
1376 self.ndr64 = kwargs.pop("ndr64", conf.ndr64)
1377 self.ndrendian = kwargs.pop("ndrendian", "little")
1378 # request_packet is used in the session, so that a response packet
1379 # can resolve union arms if the case parameter is in the request.
1380 self.request_packet = kwargs.pop("request_packet", None)
1381 self.deferred_pointers = []
1382 super(_NDRPacket, self).__init__(*args, **kwargs)
1383
1384 def do_dissect(self, s):
1385 _up = self.parent or self.underlayer
1386 if _up and isinstance(_up, _NDRPacket):
1387 self.ndr64 = _up.ndr64
1388 self.ndrendian = _up.ndrendian
1389 else:
1390 # See comment above NDRConstructedType
1391 return NDRConstructedType([]).read_deferred_pointers(
1392 self, super(_NDRPacket, self).do_dissect(s)
1393 )
1394 return super(_NDRPacket, self).do_dissect(s)
1395
1396 def post_dissect(self, s):
1397 if self.deferred_pointers:
1398 # Can't trust the cache if there were deferred pointers
1399 self.raw_packet_cache = None
1400 return s
1401
1402 def do_build(self):
1403 _up = self.parent or self.underlayer
1404 for f in self.fields.values():
1405 _set_ctx_on(f, self)
1406 if not _up or not isinstance(_up, _NDRPacket):
1407 # See comment above NDRConstructedType
1408 return NDRConstructedType([]).add_deferred_pointers(
1409 self, super(_NDRPacket, self).do_build()
1410 )
1411 return super(_NDRPacket, self).do_build()
1412
1413 def default_payload_class(self, pkt):
1414 return conf.padding_layer
1415
1416 def clone_with(self, *args, **kwargs):
1417 pkt = super(_NDRPacket, self).clone_with(*args, **kwargs)
1418 # We need to copy deferred_pointers to not break pointer deferral
1419 # on build.
1420 pkt.deferred_pointers = self.deferred_pointers
1421 pkt.ndr64 = self.ndr64
1422 pkt.ndrendian = self.ndrendian
1423 return pkt
1424
1425 def copy(self):
1426 pkt = super(_NDRPacket, self).copy()
1427 pkt.deferred_pointers = self.deferred_pointers
1428 pkt.ndr64 = self.ndr64
1429 pkt.ndrendian = self.ndrendian
1430 return pkt
1431
1432 def show2(self, dump=False, indent=3, lvl="", label_lvl=""):
1433 return self.__class__(
1434 bytes(self), ndr64=self.ndr64, ndrendian=self.ndrendian
1435 ).show(dump, indent, lvl, label_lvl)
1436
1437 def getfield_and_val(self, attr):
1438 try:
1439 return Packet.getfield_and_val(self, attr)
1440 except ValueError:
1441 if self.request_packet:
1442 # Try to resolve the field from the request on failure
1443 try:
1444 return self.request_packet.getfield_and_val(attr)
1445 except AttributeError:
1446 pass
1447 raise
1448
1449 def valueof(self, request):
1450 """
1451 Util to get the value of a NDRField, ignoring arrays, pointers, etc.
1452 """
1453 val = self
1454 for ndr_field in request.split("."):
1455 fld, fval = val.getfield_and_val(ndr_field)
1456 val = fld.valueof(val, fval)
1457 return val
1458
1459
1460class _NDRAlign:
1461 def padlen(self, flen, pkt):
1462 return -flen % self._align[pkt.ndr64]
1463
1464 def original_length(self, pkt):
1465 # Find the length of the NDR frag to be able to pad properly
1466 while pkt:
1467 par = pkt.parent or pkt.underlayer
1468 if par and isinstance(par, _NDRPacket):
1469 pkt = par
1470 else:
1471 break
1472 return len(pkt.original)
1473
1474
1475class NDRAlign(_NDRAlign, ReversePadField):
1476 """
1477 ReversePadField modified to fit NDR.
1478
1479 - If no align size is specified, use the one from the inner field
1480 - Size is calculated from the beginning of the NDR stream
1481 """
1482
1483 def __init__(self, fld, align, padwith=None):
1484 super(NDRAlign, self).__init__(fld, align=align, padwith=padwith)
1485
1486
1487class _VirtualField(Field):
1488 # Hold a value but doesn't show up when building/dissecting
1489 def addfield(self, pkt, s, x):
1490 return s
1491
1492 def getfield(self, pkt, s):
1493 return s, None
1494
1495
1496class _NDRPacketMetaclass(Packet_metaclass):
1497 def __new__(cls, name, bases, dct):
1498 newcls = super(_NDRPacketMetaclass, cls).__new__(cls, name, bases, dct)
1499 conformants = dct.get("DEPORTED_CONFORMANTS", [])
1500 if conformants:
1501 amount = len(conformants)
1502 if amount == 1:
1503 newcls.fields_desc.insert(
1504 0,
1505 _VirtualField("max_count", None),
1506 )
1507 else:
1508 newcls.fields_desc.insert(
1509 0,
1510 FieldListField(
1511 "max_counts",
1512 [],
1513 _VirtualField("", 0),
1514 count_from=lambda _: amount,
1515 ),
1516 )
1517 return newcls # type: ignore
1518
1519
1520class NDRPacket(_NDRPacket, metaclass=_NDRPacketMetaclass):
1521 """
1522 A NDR Packet. Handles pointer size & endianness
1523 """
1524
1525 __slots__ = ["_align"]
1526
1527 # NDR64 pad structures
1528 # [MS-RPCE] 2.2.5.3.4.1
1529 ALIGNMENT = (1, 1)
1530 # [C706] sect 14.3.7 - Conformants max_count can be added to the beginning
1531 DEPORTED_CONFORMANTS = []
1532
1533
1534# Primitive types
1535
1536
1537class _NDRValueOf:
1538 def valueof(self, pkt, x):
1539 return x
1540
1541
1542class _NDRLenField(_NDRValueOf, Field):
1543 """
1544 Field similar to FieldLenField that takes size_of and adjust as arguments,
1545 and take the value of a size on build.
1546 """
1547
1548 __slots__ = ["size_of", "adjust"]
1549
1550 def __init__(self, *args, **kwargs):
1551 self.size_of = kwargs.pop("size_of", None)
1552 self.adjust = kwargs.pop("adjust", lambda _, x: x)
1553 super(_NDRLenField, self).__init__(*args, **kwargs)
1554
1555 def i2m(self, pkt, x):
1556 if x is None and pkt is not None and self.size_of is not None:
1557 fld, fval = pkt.getfield_and_val(self.size_of)
1558 f = fld.i2len(pkt, fval)
1559 x = self.adjust(pkt, f)
1560 elif x is None:
1561 x = 0
1562 return x
1563
1564
1565class NDRByteField(_NDRLenField, ByteField):
1566 pass
1567
1568
1569class NDRSignedByteField(_NDRLenField, SignedByteField):
1570 pass
1571
1572
1573class _NDRField(_NDRLenField):
1574 FMT = ""
1575 ALIGN = (0, 0)
1576
1577 def getfield(self, pkt, s):
1578 return NDRAlign(
1579 Field("", 0, fmt=_e(pkt.ndrendian) + self.FMT), align=self.ALIGN
1580 ).getfield(pkt, s)
1581
1582 def addfield(self, pkt, s, val):
1583 return NDRAlign(
1584 Field("", 0, fmt=_e(pkt.ndrendian) + self.FMT), align=self.ALIGN
1585 ).addfield(pkt, s, self.i2m(pkt, val))
1586
1587
1588class NDRShortField(_NDRField):
1589 FMT = "H"
1590 ALIGN = (2, 2)
1591
1592
1593class NDRSignedShortField(_NDRField):
1594 FMT = "h"
1595 ALIGN = (2, 2)
1596
1597
1598class NDRIntField(_NDRField):
1599 FMT = "I"
1600 ALIGN = (4, 4)
1601
1602
1603class NDRSignedIntField(_NDRField):
1604 FMT = "i"
1605 ALIGN = (4, 4)
1606
1607
1608class NDRLongField(_NDRField):
1609 FMT = "Q"
1610 ALIGN = (8, 8)
1611
1612
1613class NDRSignedLongField(_NDRField):
1614 FMT = "q"
1615 ALIGN = (8, 8)
1616
1617
1618class NDRIEEEFloatField(_NDRField):
1619 FMT = "f"
1620 ALIGN = (4, 4)
1621
1622
1623class NDRIEEEDoubleField(_NDRField):
1624 FMT = "d"
1625 ALIGN = (8, 8)
1626
1627
1628# Enum types
1629
1630
1631class _NDREnumField(_NDRValueOf, EnumField):
1632 # [MS-RPCE] sect 2.2.5.2 - Enums are 4 octets in NDR64
1633 FMTS = ["H", "I"]
1634
1635 def getfield(self, pkt, s):
1636 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1637 return NDRAlign(Field("", 0, fmt=fmt), align=(2, 4)).getfield(pkt, s)
1638
1639 def addfield(self, pkt, s, val):
1640 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1641 return NDRAlign(Field("", 0, fmt=fmt), align=(2, 4)).addfield(
1642 pkt, s, self.i2m(pkt, val)
1643 )
1644
1645
1646class NDRInt3264EnumField(NDRAlign):
1647 def __init__(self, *args, **kwargs):
1648 super(NDRInt3264EnumField, self).__init__(
1649 _NDREnumField(*args, **kwargs), align=(2, 4)
1650 )
1651
1652
1653class NDRIntEnumField(_NDRValueOf, NDRAlign):
1654 # v1_enum are always 4-octets, even in NDR32
1655 def __init__(self, *args, **kwargs):
1656 super(NDRIntEnumField, self).__init__(
1657 LEIntEnumField(*args, **kwargs), align=(4, 4)
1658 )
1659
1660
1661# Special types
1662
1663
1664class NDRInt3264Field(_NDRLenField):
1665 FMTS = ["I", "Q"]
1666
1667 def getfield(self, pkt, s):
1668 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1669 return NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(pkt, s)
1670
1671 def addfield(self, pkt, s, val):
1672 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1673 return NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(
1674 pkt, s, self.i2m(pkt, val)
1675 )
1676
1677
1678class NDRSignedInt3264Field(NDRInt3264Field):
1679 FMTS = ["i", "q"]
1680
1681
1682# Pointer types
1683
1684
1685class NDRPointer(_NDRPacket):
1686 fields_desc = [
1687 MultipleTypeField(
1688 [(XLELongField("referent_id", 1), lambda pkt: pkt and pkt.ndr64)],
1689 XLEIntField("referent_id", 1),
1690 ),
1691 PacketField("value", None, conf.raw_layer),
1692 ]
1693
1694
1695class NDRFullPointerField(_FieldContainer):
1696 """
1697 A NDR Full/Unique pointer field encapsulation.
1698
1699 :param EMBEDDED: This pointer is embedded. This means that it's representation
1700 will not appear after the pointer (pointer deferral applies).
1701 See [C706] 14.3.12.3 - Algorithm for Deferral of Referents
1702 """
1703
1704 EMBEDDED = False
1705 EMBEDDED_REF = False
1706
1707 def __init__(self, fld, ref=False, fmt="I"):
1708 self.fld = fld
1709 self.ref = ref
1710 self.default = None
1711
1712 def getfield(self, pkt, s):
1713 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
1714 remain, referent_id = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(
1715 pkt, s
1716 )
1717
1718 # No value
1719 if referent_id == 0 and not self.EMBEDDED_REF:
1720 return remain, None
1721
1722 # With value
1723 if self.EMBEDDED:
1724 # deferred
1725 ptr = NDRPointer(
1726 ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, referent_id=referent_id
1727 )
1728 pkt.deferred_pointers.append((ptr, partial(self.fld.getfield, pkt)))
1729 return remain, ptr
1730
1731 remain, val = self.fld.getfield(pkt, remain)
1732 return remain, NDRPointer(
1733 ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, referent_id=referent_id, value=val
1734 )
1735
1736 def addfield(self, pkt, s, val):
1737 if val is not None and not isinstance(val, NDRPointer):
1738 raise ValueError(
1739 "Expected NDRPointer in %s. You are using it wrong!" % self.name
1740 )
1741 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
1742 fld = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8))
1743
1744 # No value
1745 if val is None and not self.EMBEDDED_REF:
1746 return fld.addfield(pkt, s, 0)
1747
1748 # With value
1749 _set_ctx_on(val.value, pkt)
1750 s = fld.addfield(pkt, s, val.referent_id)
1751 if self.EMBEDDED:
1752 # deferred
1753 pkt.deferred_pointers.append(
1754 ((lambda s: self.fld.addfield(pkt, s, val.value)), val)
1755 )
1756 return s
1757
1758 return self.fld.addfield(pkt, s, val.value)
1759
1760 def any2i(self, pkt, x):
1761 # User-friendly helper
1762 if x is not None and not isinstance(x, NDRPointer):
1763 return NDRPointer(
1764 referent_id=0x20000,
1765 value=self.fld.any2i(pkt, x),
1766 )
1767 return x
1768
1769 # Can't use i2repr = Field.i2repr and so on on PY2 :/
1770 def i2repr(self, pkt, val):
1771 return repr(val)
1772
1773 def i2h(self, pkt, x):
1774 return x
1775
1776 def h2i(self, pkt, x):
1777 return x
1778
1779 def i2len(self, pkt, x):
1780 if x is None:
1781 return 0
1782 return self.fld.i2len(pkt, x.value)
1783
1784 def valueof(self, pkt, x):
1785 if x is None:
1786 return x
1787 return self.fld.valueof(pkt, x.value)
1788
1789
1790class NDRFullEmbPointerField(NDRFullPointerField):
1791 """
1792 A NDR Embedded Full pointer.
1793
1794 Same as NDRFullPointerField with EMBEDDED = True.
1795 """
1796
1797 EMBEDDED = True
1798
1799
1800class NDRRefEmbPointerField(NDRFullPointerField):
1801 """
1802 A NDR Embedded Reference pointer.
1803
1804 Same as NDRFullPointerField with EMBEDDED = True and EMBEDDED_REF = True.
1805 """
1806
1807 EMBEDDED = True
1808 EMBEDDED_REF = True
1809
1810
1811# Constructed types
1812
1813
1814# Note: this is utterly complex and will drive you crazy
1815
1816# If you have a NDRPacket that contains a deferred pointer on the top level
1817# (only happens in non DCE/RPC structures, such as in MS-PAC, where you have an NDR
1818# structure encapsulated in a non-NDR structure), there will be left-over deferred
1819# pointers when exiting dissection/build (deferred pointers are only computed when
1820# reaching a field that extends NDRConstructedType, which is normal: if you follow
1821# the DCE/RPC spec, pointers are never deferred in root structures)
1822# Therefore there is a special case forcing the build/dissection of any leftover
1823# pointers in NDRPacket, if Scapy detects that they won't be handled by any parent.
1824
1825# Implementation notes: I chose to set 'handles_deferred' inside the FIELD, rather
1826# than inside the PACKET. This is faster to compute because whether a constructed type
1827# should handle deferral or not is computed only once when loading, therefore Scapy
1828# knows in advance whether to handle deferred pointers or not. But it is technically
1829# incorrect: with this approach, a structure (packet) cannot be used in 2 code paths
1830# that have different pointer managements. I mean by that that if there was a
1831# structure that was directly embedded in a RPC request without a pointer but also
1832# embedded with a pointer in another RPC request, it would break.
1833# Fortunately this isn't the case: structures are never reused for 2 purposes.
1834# (or at least I never seen that... <i hope this works>)
1835
1836
1837class NDRConstructedType(object):
1838 def __init__(self, fields):
1839 self.handles_deferred = False
1840 self.ndr_fields = fields
1841 self.rec_check_deferral()
1842
1843 def rec_check_deferral(self):
1844 # We iterate through the fields within this constructed type.
1845 # If we have a pointer, mark this field as handling deferrance
1846 # and make all sub-constructed types not.
1847 for f in self.ndr_fields:
1848 if isinstance(f, NDRFullPointerField) and f.EMBEDDED:
1849 self.handles_deferred = True
1850 if isinstance(f, NDRConstructedType):
1851 f.rec_check_deferral()
1852 if f.handles_deferred:
1853 self.handles_deferred = True
1854 f.handles_deferred = False
1855
1856 def getfield(self, pkt, s):
1857 s, fval = super(NDRConstructedType, self).getfield(pkt, s)
1858 if isinstance(fval, _NDRPacket):
1859 # If a sub-packet we just dissected has deferred pointers,
1860 # pass it to parent packet to propagate.
1861 pkt.deferred_pointers.extend(fval.deferred_pointers)
1862 del fval.deferred_pointers[:]
1863 if self.handles_deferred:
1864 # This field handles deferral !
1865 s = self.read_deferred_pointers(pkt, s)
1866 return s, fval
1867
1868 def read_deferred_pointers(self, pkt, s):
1869 # Now read content of the pointers that were deferred
1870 q = collections.deque()
1871 q.extend(pkt.deferred_pointers)
1872 del pkt.deferred_pointers[:]
1873 while q:
1874 # Recursively resolve pointers that were deferred
1875 ptr, getfld = q.popleft()
1876 s, val = getfld(s)
1877 ptr.value = val
1878 if isinstance(val, _NDRPacket):
1879 # Pointer resolves to a packet.. that may have deferred pointers?
1880 q.extend(val.deferred_pointers)
1881 del val.deferred_pointers[:]
1882 return s
1883
1884 def addfield(self, pkt, s, val):
1885 try:
1886 s = super(NDRConstructedType, self).addfield(pkt, s, val)
1887 except Exception as ex:
1888 try:
1889 ex.args = (
1890 "While building field '%s': " % self.name + ex.args[0],
1891 ) + ex.args[1:]
1892 except (AttributeError, IndexError):
1893 pass
1894 raise ex
1895 if isinstance(val, _NDRPacket):
1896 # If a sub-packet we just dissected has deferred pointers,
1897 # pass it to parent packet to propagate.
1898 pkt.deferred_pointers.extend(val.deferred_pointers)
1899 del val.deferred_pointers[:]
1900 if self.handles_deferred:
1901 # This field handles deferral !
1902 s = self.add_deferred_pointers(pkt, s)
1903 return s
1904
1905 def add_deferred_pointers(self, pkt, s):
1906 # Now add content of pointers that were deferred
1907 q = collections.deque()
1908 q.extend(pkt.deferred_pointers)
1909 del pkt.deferred_pointers[:]
1910 while q:
1911 addfld, fval = q.popleft()
1912 s = addfld(s)
1913 if isinstance(fval, NDRPointer) and isinstance(fval.value, _NDRPacket):
1914 q.extend(fval.value.deferred_pointers)
1915 del fval.value.deferred_pointers[:]
1916 return s
1917
1918
1919class _NDRPacketField(_NDRValueOf, PacketField):
1920 def m2i(self, pkt, m):
1921 return self.cls(m, ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, _parent=pkt)
1922
1923
1924class _NDRPacketPadField(PadField):
1925 # [MS-RPCE] 2.2.5.3.4.1 Structure with Trailing Gap
1926 # Structures have extra alignment/padding in NDR64.
1927 def padlen(self, flen, pkt):
1928 if pkt.ndr64:
1929 return -flen % self._align[1]
1930 else:
1931 return 0
1932
1933
1934class NDRPacketField(NDRConstructedType, NDRAlign):
1935 def __init__(self, name, default, pkt_cls, **kwargs):
1936 self.DEPORTED_CONFORMANTS = pkt_cls.DEPORTED_CONFORMANTS
1937 self.fld = _NDRPacketField(name, default, pkt_cls=pkt_cls, **kwargs)
1938 NDRAlign.__init__(
1939 self,
1940 _NDRPacketPadField(self.fld, align=pkt_cls.ALIGNMENT),
1941 align=pkt_cls.ALIGNMENT,
1942 )
1943 NDRConstructedType.__init__(self, pkt_cls.fields_desc)
1944
1945 def getfield(self, pkt, x):
1946 # Handle deformed conformants max_count here
1947 if self.DEPORTED_CONFORMANTS:
1948 # C706 14.3.2: "In other words, the size information precedes the
1949 # structure and is aligned independently of the structure alignment."
1950 fld = NDRInt3264Field("", 0)
1951 max_counts = []
1952 for _ in self.DEPORTED_CONFORMANTS:
1953 x, max_count = fld.getfield(pkt, x)
1954 max_counts.append(max_count)
1955 res, val = super(NDRPacketField, self).getfield(pkt, x)
1956 if len(max_counts) == 1:
1957 val.max_count = max_counts[0]
1958 else:
1959 val.max_counts = max_counts
1960 return res, val
1961 return super(NDRPacketField, self).getfield(pkt, x)
1962
1963 def addfield(self, pkt, s, x):
1964 # Handle deformed conformants max_count here
1965 if self.DEPORTED_CONFORMANTS:
1966 mcfld = NDRInt3264Field("", 0)
1967 if len(self.DEPORTED_CONFORMANTS) == 1:
1968 max_counts = [x.max_count]
1969 else:
1970 max_counts = x.max_counts
1971 for fldname, max_count in zip(self.DEPORTED_CONFORMANTS, max_counts):
1972 if max_count is None:
1973 fld, val = x.getfield_and_val(fldname)
1974 max_count = fld.i2len(x, val)
1975 s = mcfld.addfield(pkt, s, max_count)
1976 return super(NDRPacketField, self).addfield(pkt, s, x)
1977 return super(NDRPacketField, self).addfield(pkt, s, x)
1978
1979
1980# Array types
1981
1982
1983class _NDRPacketListField(NDRConstructedType, PacketListField):
1984 """
1985 A PacketListField for NDR that can optionally pack the packets into NDRPointers
1986 """
1987
1988 islist = 1
1989 holds_packets = 1
1990
1991 __slots__ = ["ptr_pack", "fld"]
1992
1993 def __init__(self, name, default, pkt_cls, **kwargs):
1994 self.ptr_pack = kwargs.pop("ptr_pack", False)
1995 if self.ptr_pack:
1996 self.fld = NDRFullEmbPointerField(NDRPacketField("", None, pkt_cls))
1997 else:
1998 self.fld = NDRPacketField("", None, pkt_cls)
1999 PacketListField.__init__(self, name, default, pkt_cls=pkt_cls, **kwargs)
2000 NDRConstructedType.__init__(self, [self.fld])
2001
2002 def m2i(self, pkt, s):
2003 remain, val = self.fld.getfield(pkt, s)
2004 if val is None:
2005 val = NDRNone()
2006 # A mistake here would be to use / instead of add_payload. It adds a copy
2007 # which breaks pointer defferal. Same applies elsewhere
2008 val.add_payload(conf.padding_layer(remain))
2009 return val
2010
2011 def any2i(self, pkt, x):
2012 # User-friendly helper
2013 if isinstance(x, list):
2014 x = [self.fld.any2i(pkt, y) for y in x]
2015 return super(_NDRPacketListField, self).any2i(pkt, x)
2016
2017 def i2m(self, pkt, val):
2018 return self.fld.addfield(pkt, b"", val)
2019
2020 def i2len(self, pkt, x):
2021 return len(x)
2022
2023 def valueof(self, pkt, x):
2024 return [
2025 self.fld.valueof(pkt, y if not isinstance(y, NDRNone) else None) for y in x
2026 ]
2027
2028
2029class NDRFieldListField(NDRConstructedType, FieldListField):
2030 """
2031 A FieldListField for NDR
2032 """
2033
2034 islist = 1
2035
2036 def __init__(self, *args, **kwargs):
2037 kwargs.pop("ptr_pack", None) # TODO: unimplemented
2038 if "length_is" in kwargs:
2039 kwargs["count_from"] = kwargs.pop("length_is")
2040 elif "size_is" in kwargs:
2041 kwargs["count_from"] = kwargs.pop("size_is")
2042 FieldListField.__init__(self, *args, **kwargs)
2043 NDRConstructedType.__init__(self, [self.field])
2044
2045 def i2len(self, pkt, x):
2046 return len(x)
2047
2048 def valueof(self, pkt, x):
2049 return [self.field.valueof(pkt, y) for y in x]
2050
2051
2052class NDRVaryingArray(_NDRPacket):
2053 fields_desc = [
2054 MultipleTypeField(
2055 [(LELongField("offset", 0), lambda pkt: pkt and pkt.ndr64)],
2056 LEIntField("offset", 0),
2057 ),
2058 MultipleTypeField(
2059 [
2060 (
2061 LELongField("actual_count", None),
2062 lambda pkt: pkt and pkt.ndr64,
2063 )
2064 ],
2065 LEIntField("actual_count", None),
2066 ),
2067 PacketField("value", None, conf.raw_layer),
2068 ]
2069
2070
2071class _NDRVarField:
2072 """
2073 NDR Varying Array / String field
2074 """
2075
2076 LENGTH_FROM = False
2077 COUNT_FROM = False
2078
2079 def __init__(self, *args, **kwargs):
2080 # We build the length_is function by taking into account both the
2081 # actual_count (from the varying field) and a potentially provided
2082 # length_is field.
2083 if "length_is" in kwargs:
2084 _length_is = kwargs.pop("length_is")
2085 length_is = lambda pkt: (_length_is(pkt.underlayer) or pkt.actual_count)
2086 else:
2087 length_is = lambda pkt: pkt.actual_count
2088 # Pass it to the sub-field (actually subclass)
2089 if self.LENGTH_FROM:
2090 kwargs["length_from"] = length_is
2091 elif self.COUNT_FROM:
2092 kwargs["count_from"] = length_is
2093 super(_NDRVarField, self).__init__(*args, **kwargs)
2094
2095 def getfield(self, pkt, s):
2096 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2097 remain, offset = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(pkt, s)
2098 remain, actual_count = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(
2099 pkt, remain
2100 )
2101 final = NDRVaryingArray(
2102 ndr64=pkt.ndr64,
2103 ndrendian=pkt.ndrendian,
2104 offset=offset,
2105 actual_count=actual_count,
2106 _underlayer=pkt,
2107 )
2108 remain, val = super(_NDRVarField, self).getfield(final, remain)
2109 final.value = super(_NDRVarField, self).i2h(pkt, val)
2110 return remain, final
2111
2112 def addfield(self, pkt, s, val):
2113 if not isinstance(val, NDRVaryingArray):
2114 raise ValueError(
2115 "Expected NDRVaryingArray in %s. You are using it wrong!" % self.name
2116 )
2117 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2118 _set_ctx_on(val.value, pkt)
2119 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(pkt, s, val.offset)
2120 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(
2121 pkt,
2122 s,
2123 val.actual_count is None
2124 and super(_NDRVarField, self).i2len(pkt, val.value)
2125 or val.actual_count,
2126 )
2127 return super(_NDRVarField, self).addfield(
2128 pkt, s, super(_NDRVarField, self).h2i(pkt, val.value)
2129 )
2130
2131 def i2len(self, pkt, x):
2132 return super(_NDRVarField, self).i2len(pkt, x.value)
2133
2134 def any2i(self, pkt, x):
2135 # User-friendly helper
2136 if not isinstance(x, NDRVaryingArray):
2137 return NDRVaryingArray(
2138 value=super(_NDRVarField, self).any2i(pkt, x),
2139 )
2140 return x
2141
2142 # Can't use i2repr = Field.i2repr and so on on PY2 :/
2143 def i2repr(self, pkt, val):
2144 return repr(val)
2145
2146 def i2h(self, pkt, x):
2147 return x
2148
2149 def h2i(self, pkt, x):
2150 return x
2151
2152 def valueof(self, pkt, x):
2153 return super(_NDRVarField, self).valueof(pkt, x.value)
2154
2155
2156class NDRConformantArray(_NDRPacket):
2157 fields_desc = [
2158 MultipleTypeField(
2159 [(LELongField("max_count", None), lambda pkt: pkt and pkt.ndr64)],
2160 LEIntField("max_count", None),
2161 ),
2162 MultipleTypeField(
2163 [
2164 (
2165 PacketListField(
2166 "value",
2167 [],
2168 conf.raw_layer,
2169 count_from=lambda pkt: pkt.max_count,
2170 ),
2171 (
2172 lambda pkt: pkt.fields.get("value", None)
2173 and isinstance(pkt.fields["value"][0], Packet),
2174 lambda _, val: val and isinstance(val[0], Packet),
2175 ),
2176 )
2177 ],
2178 FieldListField(
2179 "value", [], LEIntField("", 0), count_from=lambda pkt: pkt.max_count
2180 ),
2181 ),
2182 ]
2183
2184
2185class NDRConformantString(_NDRPacket):
2186 fields_desc = [
2187 MultipleTypeField(
2188 [(LELongField("max_count", None), lambda pkt: pkt and pkt.ndr64)],
2189 LEIntField("max_count", None),
2190 ),
2191 StrField("value", ""),
2192 ]
2193
2194
2195class _NDRConfField:
2196 """
2197 NDR Conformant Array / String field
2198 """
2199
2200 CONFORMANT_STRING = False
2201 LENGTH_FROM = False
2202 COUNT_FROM = False
2203
2204 def __init__(self, *args, **kwargs):
2205 # when conformant_in_struct is True, we remove the level of abstraction
2206 # provided by NDRConformantString / NDRConformantArray because max_count
2207 # is a proper field in the parent packet.
2208 self.conformant_in_struct = kwargs.pop("conformant_in_struct", False)
2209 # size_is/max_is end up here, and is what defines a conformant field.
2210 if "size_is" in kwargs:
2211 size_is = kwargs.pop("size_is")
2212 if self.LENGTH_FROM:
2213 kwargs["length_from"] = size_is
2214 elif self.COUNT_FROM:
2215 kwargs["count_from"] = size_is
2216 super(_NDRConfField, self).__init__(*args, **kwargs)
2217
2218 def getfield(self, pkt, s):
2219 # [C706] - 14.3.7 Structures Containing Arrays
2220 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2221 if self.conformant_in_struct:
2222 return super(_NDRConfField, self).getfield(pkt, s)
2223 remain, max_count = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(
2224 pkt, s
2225 )
2226 remain, val = super(_NDRConfField, self).getfield(pkt, remain)
2227 return remain, (
2228 NDRConformantString if self.CONFORMANT_STRING else NDRConformantArray
2229 )(ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, max_count=max_count, value=val)
2230
2231 def addfield(self, pkt, s, val):
2232 if self.conformant_in_struct:
2233 return super(_NDRConfField, self).addfield(pkt, s, val)
2234 if self.CONFORMANT_STRING and not isinstance(val, NDRConformantString):
2235 raise ValueError(
2236 "Expected NDRConformantString in %s. You are using it wrong!"
2237 % self.name
2238 )
2239 elif not self.CONFORMANT_STRING and not isinstance(val, NDRConformantArray):
2240 raise ValueError(
2241 "Expected NDRConformantArray in %s. You are using it wrong!" % self.name
2242 )
2243 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2244 _set_ctx_on(val.value, pkt)
2245 if val.value and isinstance(val.value[0], NDRVaryingArray):
2246 value = val.value[0]
2247 else:
2248 value = val.value
2249 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(
2250 pkt,
2251 s,
2252 val.max_count is None
2253 and super(_NDRConfField, self).i2len(pkt, value)
2254 or val.max_count,
2255 )
2256 return super(_NDRConfField, self).addfield(pkt, s, value)
2257
2258 def _subval(self, x):
2259 if self.conformant_in_struct:
2260 value = x
2261 elif (
2262 not self.CONFORMANT_STRING
2263 and x.value
2264 and isinstance(x.value[0], NDRVaryingArray)
2265 ):
2266 value = x.value[0]
2267 else:
2268 value = x.value
2269 return value
2270
2271 def i2len(self, pkt, x):
2272 return super(_NDRConfField, self).i2len(pkt, self._subval(x))
2273
2274 def any2i(self, pkt, x):
2275 # User-friendly helper
2276 if self.conformant_in_struct:
2277 return super(_NDRConfField, self).any2i(pkt, x)
2278 if self.CONFORMANT_STRING and not isinstance(x, NDRConformantString):
2279 return NDRConformantString(
2280 value=super(_NDRConfField, self).any2i(pkt, x),
2281 )
2282 elif not isinstance(x, NDRConformantArray):
2283 return NDRConformantArray(
2284 value=super(_NDRConfField, self).any2i(pkt, x),
2285 )
2286 return x
2287
2288 # Can't use i2repr = Field.i2repr and so on on PY2 :/
2289 def i2repr(self, pkt, val):
2290 return repr(val)
2291
2292 def i2h(self, pkt, x):
2293 return x
2294
2295 def h2i(self, pkt, x):
2296 return x
2297
2298 def valueof(self, pkt, x):
2299 return super(_NDRConfField, self).valueof(pkt, self._subval(x))
2300
2301
2302class NDRVarPacketListField(_NDRVarField, _NDRPacketListField):
2303 """
2304 NDR Varying PacketListField. Unused
2305 """
2306
2307 COUNT_FROM = True
2308
2309
2310class NDRConfPacketListField(_NDRConfField, _NDRPacketListField):
2311 """
2312 NDR Conformant PacketListField
2313 """
2314
2315 COUNT_FROM = True
2316
2317
2318class NDRConfVarPacketListField(_NDRConfField, _NDRVarField, _NDRPacketListField):
2319 """
2320 NDR Conformant Varying PacketListField
2321 """
2322
2323 COUNT_FROM = True
2324
2325
2326class NDRConfFieldListField(_NDRConfField, NDRFieldListField):
2327 """
2328 NDR Conformant FieldListField
2329 """
2330
2331 COUNT_FROM = True
2332
2333
2334class NDRConfVarFieldListField(_NDRConfField, _NDRVarField, NDRFieldListField):
2335 """
2336 NDR Conformant Varying FieldListField
2337 """
2338
2339 COUNT_FROM = True
2340
2341
2342# NDR String fields
2343
2344
2345class _NDRUtf16(Field):
2346 def h2i(self, pkt, x):
2347 encoding = {"big": "utf-16be", "little": "utf-16le"}[pkt.ndrendian]
2348 return plain_str(x).encode(encoding)
2349
2350 def i2h(self, pkt, x):
2351 encoding = {"big": "utf-16be", "little": "utf-16le"}[pkt.ndrendian]
2352 return bytes_encode(x).decode(encoding, errors="replace")
2353
2354
2355class NDRConfStrLenField(_NDRConfField, _NDRValueOf, StrLenField):
2356 """
2357 NDR Conformant StrLenField.
2358
2359 This is not a "string" per NDR, but an a conformant byte array
2360 (e.g. tower_octet_string). For ease of use, we implicitly convert
2361 it in specific cases.
2362 """
2363
2364 CONFORMANT_STRING = True
2365 LENGTH_FROM = True
2366
2367
2368class NDRConfStrLenFieldUtf16(_NDRConfField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16):
2369 """
2370 NDR Conformant StrLenFieldUtf16.
2371
2372 See NDRConfStrLenField for comment.
2373 """
2374
2375 CONFORMANT_STRING = True
2376 ON_WIRE_SIZE_UTF16 = False
2377 LENGTH_FROM = True
2378
2379
2380class NDRVarStrLenField(_NDRVarField, StrLenField):
2381 """
2382 NDR Varying StrLenField
2383 """
2384
2385 LENGTH_FROM = True
2386
2387
2388class NDRVarStrLenFieldUtf16(_NDRVarField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16):
2389 """
2390 NDR Varying StrLenFieldUtf16
2391 """
2392
2393 ON_WIRE_SIZE_UTF16 = False
2394 LENGTH_FROM = True
2395
2396
2397class NDRConfVarStrLenField(_NDRConfField, _NDRVarField, _NDRValueOf, StrLenField):
2398 """
2399 NDR Conformant Varying StrLenField
2400 """
2401
2402 LENGTH_FROM = True
2403
2404
2405class NDRConfVarStrLenFieldUtf16(
2406 _NDRConfField, _NDRVarField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16
2407):
2408 """
2409 NDR Conformant Varying StrLenFieldUtf16
2410 """
2411
2412 ON_WIRE_SIZE_UTF16 = False
2413 LENGTH_FROM = True
2414
2415
2416class NDRConfVarStrNullField(_NDRConfField, _NDRVarField, _NDRValueOf, StrNullField):
2417 """
2418 NDR Conformant Varying StrNullField
2419 """
2420
2421 NULLFIELD = True
2422
2423
2424class NDRConfVarStrNullFieldUtf16(
2425 _NDRConfField, _NDRVarField, _NDRValueOf, StrNullFieldUtf16, _NDRUtf16
2426):
2427 """
2428 NDR Conformant Varying StrNullFieldUtf16
2429 """
2430
2431 ON_WIRE_SIZE_UTF16 = False
2432 NULLFIELD = True
2433
2434
2435# Union type
2436
2437
2438class NDRUnion(_NDRPacket):
2439 fields_desc = [
2440 IntField("tag", 0),
2441 PacketField("value", None, conf.raw_layer),
2442 ]
2443
2444
2445class _NDRUnionField(MultipleTypeField):
2446 __slots__ = ["switch_fmt", "align"]
2447
2448 def __init__(self, flds, dflt, align, switch_fmt):
2449 self.switch_fmt = switch_fmt
2450 self.align = align
2451 super(_NDRUnionField, self).__init__(flds, dflt)
2452
2453 def getfield(self, pkt, s):
2454 fmt = _e(pkt.ndrendian) + self.switch_fmt[pkt.ndr64]
2455 remain, tag = NDRAlign(Field("", 0, fmt=fmt), align=self.align).getfield(pkt, s)
2456 fld, _ = super(_NDRUnionField, self)._find_fld_pkt_val(pkt, NDRUnion(tag=tag))
2457 remain, val = fld.getfield(pkt, remain)
2458 return remain, NDRUnion(
2459 tag=tag, value=val, ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, _parent=pkt
2460 )
2461
2462 def addfield(self, pkt, s, val):
2463 fmt = _e(pkt.ndrendian) + self.switch_fmt[pkt.ndr64]
2464 if not isinstance(val, NDRUnion):
2465 raise ValueError(
2466 "Expected NDRUnion in %s. You are using it wrong!" % self.name
2467 )
2468 _set_ctx_on(val.value, pkt)
2469 # First, align the whole tag+union against the align param
2470 s = NDRAlign(Field("", 0, fmt=fmt), align=self.align).addfield(pkt, s, val.tag)
2471 # Then, compute the subfield with its own alignment
2472 return super(_NDRUnionField, self).addfield(pkt, s, val)
2473
2474 def _find_fld_pkt_val(self, pkt, val):
2475 fld, val = super(_NDRUnionField, self)._find_fld_pkt_val(pkt, val)
2476 return fld, val.value
2477
2478 # Can't use i2repr = Field.i2repr and so on on PY2 :/
2479 def i2repr(self, pkt, val):
2480 return repr(val)
2481
2482 def i2h(self, pkt, x):
2483 return x
2484
2485 def h2i(self, pkt, x):
2486 return x
2487
2488 def valueof(self, pkt, x):
2489 fld, val = self._find_fld_pkt_val(pkt, x)
2490 return fld.valueof(pkt, x.value)
2491
2492
2493class NDRUnionField(NDRConstructedType, _NDRUnionField):
2494 def __init__(self, flds, dflt, align, switch_fmt):
2495 _NDRUnionField.__init__(self, flds, dflt, align=align, switch_fmt=switch_fmt)
2496 NDRConstructedType.__init__(self, [x[0] for x in flds] + [dflt])
2497
2498 def any2i(self, pkt, x):
2499 # User-friendly helper
2500 if x:
2501 if not isinstance(x, NDRUnion):
2502 raise ValueError("Invalid value for %s; should be NDRUnion" % self.name)
2503 else:
2504 x.value = _NDRUnionField.any2i(self, pkt, x)
2505 return x
2506
2507
2508# Misc
2509
2510
2511class _ProxyArray:
2512 # Hack for recursive fields DEPORTED_CONFORMANTS field
2513 __slots__ = ["getfld"]
2514
2515 def __init__(self, getfld):
2516 self.getfld = getfld
2517
2518 def __len__(self):
2519 try:
2520 return len(self.getfld())
2521 except AttributeError:
2522 return 0
2523
2524 def __iter__(self):
2525 try:
2526 return iter(self.getfld())
2527 except AttributeError:
2528 return iter([])
2529
2530
2531class _ProxyTuple:
2532 # Hack for recursive fields ALIGNMENT field
2533 __slots__ = ["getfld"]
2534
2535 def __init__(self, getfld):
2536 self.getfld = getfld
2537
2538 def __getitem__(self, name):
2539 try:
2540 return self.getfld()[name]
2541 except AttributeError:
2542 raise KeyError
2543
2544
2545def NDRRecursiveClass(clsname):
2546 """
2547 Return a special class that is used for pointer recursion
2548 """
2549 # Get module where this is called
2550 frame = inspect.currentframe().f_back
2551 mod = frame.f_globals["__loader__"].name
2552 getcls = lambda: getattr(importlib.import_module(mod), clsname)
2553
2554 class _REC(NDRPacket):
2555 ALIGNMENT = _ProxyTuple(lambda: getattr(getcls(), "ALIGNMENT"))
2556 DEPORTED_CONFORMANTS = _ProxyArray(
2557 lambda: getattr(getcls(), "DEPORTED_CONFORMANTS")
2558 )
2559
2560 @classmethod
2561 def dispatch_hook(cls, _pkt=None, *args, **kargs):
2562 return getcls()
2563
2564 return _REC
2565
2566
2567# The very few NDR-specific structures
2568
2569
2570class NDRContextHandle(NDRPacket):
2571 ALIGNMENT = (4, 4)
2572 fields_desc = [
2573 LEIntField("attributes", 0),
2574 StrFixedLenField("uuid", b"", length=16),
2575 ]
2576
2577 def guess_payload_class(self, payload):
2578 return conf.padding_layer
2579
2580
2581class NDRNone(NDRPacket):
2582 # This is only used in NDRPacketListField to act as a "None" pointer, and is
2583 # a workaround because the field doesn't support None as a value in the list.
2584 name = "None"
2585 ALIGNMENT = (4, 8)
2586 fields_desc = [
2587 NDRInt3264Field("ptr", 0),
2588 ]
2589
2590
2591# --- Type Serialization Version 1 - [MSRPCE] sect 2.2.6
2592
2593
2594def _get_ndrtype1_endian(pkt):
2595 if pkt.underlayer is None:
2596 return "<"
2597 return {0x00: ">", 0x10: "<"}.get(pkt.underlayer.Endianness, "<")
2598
2599
2600class NDRSerialization1Header(Packet):
2601 fields_desc = [
2602 ByteField("Version", 1),
2603 ByteEnumField("Endianness", 0x10, {0x00: "big", 0x10: "little"}),
2604 LEShortField("CommonHeaderLength", 8),
2605 XLEIntField("Filler", 0xCCCCCCCC),
2606 ]
2607
2608 # Add a bit of goo so that valueof() goes through the header
2609
2610 def _ndrlayer(self):
2611 cur = self
2612 while cur and not isinstance(cur, _NDRPacket) and cur.payload:
2613 cur = cur.payload
2614 if isinstance(cur, NDRPointer):
2615 cur = cur.value
2616 return cur
2617
2618 def getfield_and_val(self, attr):
2619 try:
2620 return Packet.getfield_and_val(self, attr)
2621 except ValueError:
2622 return self._ndrlayer().getfield_and_val(attr)
2623
2624 def valueof(self, name):
2625 return self._ndrlayer().valueof(name)
2626
2627
2628class NDRSerialization1PrivateHeader(Packet):
2629 fields_desc = [
2630 EField(
2631 LEIntField("ObjectBufferLength", 0), endianness_from=_get_ndrtype1_endian
2632 ),
2633 XLEIntField("Filler", 0),
2634 ]
2635
2636
2637def ndr_deserialize1(b, cls, ptr_pack=False):
2638 """
2639 Deserialize Type Serialization Version 1
2640 [MS-RPCE] sect 2.2.6
2641
2642 :param ptr_pack: pack in a pointer to the structure.
2643 """
2644 if issubclass(cls, NDRPacket):
2645 # We use an intermediary class because it uses NDRPacketField which handles
2646 # deported conformant fields
2647 if ptr_pack:
2648 hdrlen = 20
2649
2650 class _cls(NDRPacket):
2651 fields_desc = [NDRFullPointerField(NDRPacketField("pkt", None, cls))]
2652
2653 else:
2654 hdrlen = 16
2655
2656 class _cls(NDRPacket):
2657 fields_desc = [NDRPacketField("pkt", None, cls)]
2658
2659 hdr = NDRSerialization1Header(b[:8]) / NDRSerialization1PrivateHeader(b[8:16])
2660 endian = {0x00: "big", 0x10: "little"}[hdr.Endianness]
2661 padlen = (-hdr.ObjectBufferLength) % _TYPE1_S_PAD
2662 # padlen should be 0 (pad included in length), but some implementations
2663 # implement apparently misread the spec
2664 return (
2665 hdr
2666 / _cls(
2667 b[16 : hdrlen + hdr.ObjectBufferLength],
2668 ndr64=False, # Only NDR32 is supported in Type 1
2669 ndrendian=endian,
2670 ).pkt
2671 / conf.padding_layer(b[hdrlen + padlen + hdr.ObjectBufferLength :])
2672 )
2673 return NDRSerialization1Header(b[:8]) / cls(b[8:])
2674
2675
2676def ndr_serialize1(pkt, ptr_pack=False):
2677 """
2678 Serialize Type Serialization Version 1
2679 [MS-RPCE] sect 2.2.6
2680
2681 :param ptr_pack: pack in a pointer to the structure.
2682 """
2683 pkt = pkt.copy()
2684 endian = getattr(pkt, "ndrendian", "little")
2685 if not isinstance(pkt, NDRSerialization1Header):
2686 if not isinstance(pkt, NDRPacket):
2687 return bytes(NDRSerialization1Header(Endianness=endian) / pkt)
2688 if isinstance(pkt, NDRPointer):
2689 cls = pkt.value.__class__
2690 else:
2691 cls = pkt.__class__
2692 val = pkt
2693 pkt_len = len(pkt)
2694 # ObjectBufferLength:
2695 # > It MUST include the padding length and exclude the header itself
2696 pkt = NDRSerialization1Header(
2697 Endianness=endian
2698 ) / NDRSerialization1PrivateHeader(
2699 ObjectBufferLength=pkt_len + (-pkt_len) % _TYPE1_S_PAD
2700 )
2701 else:
2702 cls = pkt.value.__class__
2703 val = pkt.payload.payload
2704 pkt.payload.remove_payload()
2705
2706 # See above about why we need an intermediary class
2707 if ptr_pack:
2708
2709 class _cls(NDRPacket):
2710 fields_desc = [NDRFullPointerField(NDRPacketField("pkt", None, cls))]
2711
2712 else:
2713
2714 class _cls(NDRPacket):
2715 fields_desc = [NDRPacketField("pkt", None, cls)]
2716
2717 ret = bytes(pkt / _cls(pkt=val, ndr64=False, ndrendian=endian))
2718 return ret + (-len(ret) % _TYPE1_S_PAD) * b"\x00"
2719
2720
2721class _NDRSerializeType1:
2722 def __init__(self, *args, **kwargs):
2723 self.ptr_pack = kwargs.pop("ptr_pack", False)
2724 super(_NDRSerializeType1, self).__init__(*args, **kwargs)
2725
2726 def i2m(self, pkt, val):
2727 return ndr_serialize1(val, ptr_pack=self.ptr_pack)
2728
2729 def m2i(self, pkt, s):
2730 return ndr_deserialize1(s, self.cls, ptr_pack=self.ptr_pack)
2731
2732 def i2len(self, pkt, val):
2733 return len(self.i2m(pkt, val))
2734
2735
2736class NDRSerializeType1PacketField(_NDRSerializeType1, PacketField):
2737 __slots__ = ["ptr_pack"]
2738
2739
2740class NDRSerializeType1PacketLenField(_NDRSerializeType1, PacketLenField):
2741 __slots__ = ["ptr_pack"]
2742
2743
2744class NDRSerializeType1PacketListField(_NDRSerializeType1, PacketListField):
2745 __slots__ = ["ptr_pack"]
2746
2747 def i2len(self, pkt, val):
2748 return sum(len(self.i2m(pkt, p)) for p in val)
2749
2750
2751# --- DCE/RPC session
2752
2753
2754class DceRpcSession(DefaultSession):
2755 """
2756 A DCE/RPC session within a TCP socket.
2757 """
2758
2759 def __init__(self, *args, **kwargs):
2760 self.rpc_bind_interface: Union[DceRpcInterface, ComInterface] = None
2761 self.rpc_bind_is_com: bool = False
2762 self.ndr64 = False
2763 self.ndrendian = "little"
2764 self.support_header_signing = kwargs.pop("support_header_signing", True)
2765 self.header_sign = conf.dcerpc_force_header_signing
2766 self.ssp = kwargs.pop("ssp", None)
2767 self.sspcontext = kwargs.pop("sspcontext", None)
2768 self.auth_level = kwargs.pop("auth_level", None)
2769 self.auth_context_id = kwargs.pop("auth_context_id", 0)
2770 self.sent_cont_ids = []
2771 self.cont_id = 0 # Currently selected context
2772 self.map_callid_opnum = {}
2773 self.frags = collections.defaultdict(lambda: b"")
2774 self.sniffsspcontexts = {} # Unfinished contexts for passive
2775 if conf.dcerpc_session_enable and conf.winssps_passive:
2776 for ssp in conf.winssps_passive:
2777 self.sniffsspcontexts[ssp] = None
2778 super(DceRpcSession, self).__init__(*args, **kwargs)
2779
2780 def _up_pkt(self, pkt):
2781 """
2782 Common function to handle the DCE/RPC session: what interfaces are bind,
2783 opnums, etc.
2784 """
2785 opnum = None
2786 opts = {}
2787 if DceRpc5Bind in pkt or DceRpc5AlterContext in pkt:
2788 # bind => get which RPC interface
2789 self.sent_cont_ids = [x.cont_id for x in pkt.context_elem]
2790 for ctx in pkt.context_elem:
2791 if_uuid = ctx.abstract_syntax.if_uuid
2792 if_version = ctx.abstract_syntax.if_version
2793 try:
2794 self.rpc_bind_interface = DCE_RPC_INTERFACES[(if_uuid, if_version)]
2795 self.rpc_bind_is_com = False
2796 except KeyError:
2797 try:
2798 self.rpc_bind_interface = COM_INTERFACES[if_uuid]
2799 self.rpc_bind_is_com = True
2800 except KeyError:
2801 self.rpc_bind_interface = None
2802 log_runtime.warning(
2803 "Unknown RPC interface %s. Try loading the IDL" % if_uuid
2804 )
2805 elif DceRpc5BindAck in pkt or DceRpc5AlterContextResp in pkt:
2806 # bind ack => is it NDR64
2807 for i, res in enumerate(pkt.results):
2808 if res.result == 0: # Accepted
2809 # Context
2810 try:
2811 self.cont_id = self.sent_cont_ids[i]
2812 except IndexError:
2813 self.cont_id = 0
2814 finally:
2815 self.sent_cont_ids = []
2816
2817 # Endianness
2818 self.ndrendian = {0: "big", 1: "little"}[pkt[DceRpc5].endian]
2819
2820 # Transfer syntax
2821 if res.transfer_syntax.sprintf("%if_uuid%") == "NDR64":
2822 self.ndr64 = True
2823 elif DceRpc5Request in pkt:
2824 # request => match opnum with callID
2825 opnum = pkt.opnum
2826 if self.rpc_bind_is_com:
2827 self.map_callid_opnum[pkt.call_id] = (
2828 opnum,
2829 pkt[DceRpc5Request].payload.payload,
2830 )
2831 else:
2832 self.map_callid_opnum[pkt.call_id] = opnum, pkt[DceRpc5Request].payload
2833 elif DceRpc5Response in pkt:
2834 # response => get opnum from table
2835 try:
2836 opnum, opts["request_packet"] = self.map_callid_opnum[pkt.call_id]
2837 del self.map_callid_opnum[pkt.call_id]
2838 except KeyError:
2839 log_runtime.info("Unknown call_id %s in DCE/RPC session" % pkt.call_id)
2840 # Bind / Alter request/response specific
2841 if (
2842 DceRpc5Bind in pkt
2843 or DceRpc5AlterContext in pkt
2844 or DceRpc5BindAck in pkt
2845 or DceRpc5AlterContextResp in pkt
2846 ):
2847 # Detect if "Header Signing" is in use
2848 if pkt.pfc_flags & 0x04: # PFC_SUPPORT_HEADER_SIGN
2849 self.header_sign = True
2850 return opnum, opts
2851
2852 # [C706] sect 12.6.2 - Fragmentation and Reassembly
2853 # Since the connection-oriented transport guarantees sequentiality, the receiver
2854 # will always receive the fragments in order.
2855
2856 def _defragment(self, pkt, body=None):
2857 """
2858 Function to defragment DCE/RPC packets.
2859 """
2860 uid = pkt.call_id
2861 if pkt.pfc_flags.PFC_FIRST_FRAG and pkt.pfc_flags.PFC_LAST_FRAG:
2862 # Not fragmented
2863 return body
2864 if pkt.pfc_flags.PFC_FIRST_FRAG or uid in self.frags:
2865 # Packet is fragmented
2866 if body is None:
2867 body = pkt[DceRpc5].payload.payload.original
2868 self.frags[uid] += body
2869 if pkt.pfc_flags.PFC_LAST_FRAG:
2870 return self.frags[uid]
2871 else:
2872 # Not fragmented
2873 return body
2874
2875 # C706 sect 12.5.2.15 - PDU Body Length
2876 # "The maximum PDU body size is 65528 bytes."
2877 MAX_PDU_BODY_SIZE = 4176
2878
2879 def _fragment(self, pkt, body):
2880 """
2881 Function to fragment DCE/RPC packets.
2882 """
2883 if len(body) > self.MAX_PDU_BODY_SIZE:
2884 # Clear any PFC_*_FRAG flag
2885 pkt.pfc_flags &= 0xFC
2886
2887 # Iterate through fragments
2888 cur = None
2889 while body:
2890 # Create a fragment
2891 pkt_frag = pkt.copy()
2892
2893 if cur is None:
2894 # It's the first one
2895 pkt_frag.pfc_flags += "PFC_FIRST_FRAG"
2896
2897 # Split
2898 cur, body = (
2899 body[: self.MAX_PDU_BODY_SIZE],
2900 body[self.MAX_PDU_BODY_SIZE :],
2901 )
2902
2903 if not body:
2904 # It's the last one
2905 pkt_frag.pfc_flags += "PFC_LAST_FRAG"
2906 yield pkt_frag, cur
2907 else:
2908 yield pkt, body
2909
2910 # [MS-RPCE] sect 3.3.1.5.2.2
2911
2912 # The PDU header, PDU body, and sec_trailer MUST be passed in the input message, in
2913 # this order, to GSS_WrapEx, GSS_UnwrapEx, GSS_GetMICEx, and GSS_VerifyMICEx. For
2914 # integrity protection the sign flag for that PDU segment MUST be set to TRUE, else
2915 # it MUST be set to FALSE. For confidentiality protection, the conf_req_flag for
2916 # that PDU segment MUST be set to TRUE, else it MUST be set to FALSE.
2917
2918 # If the authentication level is RPC_C_AUTHN_LEVEL_PKT_PRIVACY, the PDU body will
2919 # be encrypted.
2920 # The PDU body from the output message of GSS_UnwrapEx represents the plain text
2921 # version of the PDU body. The PDU header and sec_trailer output from the output
2922 # message SHOULD be ignored.
2923 # Similarly the signature output SHOULD be ignored.
2924
2925 def in_pkt(self, pkt):
2926 # Check for encrypted payloads
2927 body = None
2928 if conf.raw_layer in pkt.payload:
2929 body = bytes(pkt.payload[conf.raw_layer])
2930 # If we are doing passive sniffing
2931 if conf.dcerpc_session_enable and conf.winssps_passive:
2932 # We have Windows SSPs, and no current context
2933 if pkt.auth_verifier and pkt.auth_verifier.is_ssp():
2934 # This is a bind/alter/auth3 req/resp
2935 for ssp in self.sniffsspcontexts:
2936 self.sniffsspcontexts[ssp], status = ssp.GSS_Passive(
2937 self.sniffsspcontexts[ssp],
2938 pkt.auth_verifier.auth_value,
2939 req_flags=GSS_S_FLAGS.GSS_S_ALLOW_MISSING_BINDINGS
2940 | GSS_C_FLAGS.GSS_C_DCE_STYLE,
2941 )
2942 if status == GSS_S_COMPLETE:
2943 self.auth_level = DCE_C_AUTHN_LEVEL(
2944 int(pkt.auth_verifier.auth_level)
2945 )
2946 self.ssp = ssp
2947 self.sspcontext = self.sniffsspcontexts[ssp]
2948 self.sniffsspcontexts[ssp] = None
2949 elif (
2950 self.sspcontext
2951 and pkt.auth_verifier
2952 and pkt.auth_verifier.is_protected()
2953 and body
2954 ):
2955 # This is a request/response
2956 if self.sspcontext.passive:
2957 self.ssp.GSS_Passive_set_Direction(
2958 self.sspcontext,
2959 IsAcceptor=DceRpc5Response in pkt,
2960 )
2961 if pkt.auth_verifier and pkt.auth_verifier.is_protected() and body:
2962 if self.sspcontext is None:
2963 return pkt
2964 if self.auth_level in (
2965 RPC_C_AUTHN_LEVEL.PKT_INTEGRITY,
2966 RPC_C_AUTHN_LEVEL.PKT_PRIVACY,
2967 ):
2968 # note: 'vt_trailer' is included in the pdu body
2969 # [MS-RPCE] sect 2.2.2.13
2970 # "The data structures MUST only appear in a request PDU, and they
2971 # SHOULD be placed in the PDU immediately after the stub data but
2972 # before the authentication padding octets. Therefore, for security
2973 # purposes, the verification trailer is considered part of the PDU
2974 # body."
2975 if pkt.vt_trailer:
2976 body += bytes(pkt.vt_trailer)
2977 # Account for padding when computing checksum/encryption
2978 if pkt.auth_padding:
2979 body += pkt.auth_padding
2980
2981 # Build pdu_header and sec_trailer
2982 pdu_header = pkt.copy()
2983 sec_trailer = pdu_header.auth_verifier
2984 # sec_trailer: include the sec_trailer but not the Authentication token
2985 authval_len = len(sec_trailer.auth_value)
2986 # Discard everything out of the header
2987 pdu_header.auth_padding = None
2988 pdu_header.auth_verifier = None
2989 pdu_header.payload.payload = NoPayload()
2990 pdu_header.vt_trailer = None
2991
2992 # [MS-RPCE] sect 2.2.2.12
2993 if self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY:
2994 _msgs = self.ssp.GSS_UnwrapEx(
2995 self.sspcontext,
2996 [
2997 # "PDU header"
2998 SSP.WRAP_MSG(
2999 conf_req_flag=False,
3000 sign=self.header_sign,
3001 data=bytes(pdu_header),
3002 ),
3003 # "PDU body"
3004 SSP.WRAP_MSG(
3005 conf_req_flag=True,
3006 sign=True,
3007 data=body,
3008 ),
3009 # "sec_trailer"
3010 SSP.WRAP_MSG(
3011 conf_req_flag=False,
3012 sign=self.header_sign,
3013 data=bytes(sec_trailer)[:-authval_len],
3014 ),
3015 ],
3016 pkt.auth_verifier.auth_value,
3017 )
3018 body = _msgs[1].data # PDU body
3019 elif self.auth_level == RPC_C_AUTHN_LEVEL.PKT_INTEGRITY:
3020 self.ssp.GSS_VerifyMICEx(
3021 self.sspcontext,
3022 [
3023 # "PDU header"
3024 SSP.MIC_MSG(
3025 sign=self.header_sign,
3026 data=bytes(pdu_header),
3027 ),
3028 # "PDU body"
3029 SSP.MIC_MSG(
3030 sign=True,
3031 data=body,
3032 ),
3033 # "sec_trailer"
3034 SSP.MIC_MSG(
3035 sign=self.header_sign,
3036 data=bytes(sec_trailer)[:-authval_len],
3037 ),
3038 ],
3039 pkt.auth_verifier.auth_value,
3040 )
3041 # Put padding back into the header
3042 if pkt.auth_padding:
3043 padlen = len(pkt.auth_padding)
3044 body, pkt.auth_padding = body[:-padlen], body[-padlen:]
3045 # Put back vt_trailer into the header, if present.
3046 if _SECTRAILER_MAGIC in body:
3047 body, pkt.vt_trailer = pkt.get_field("vt_trailer").getfield(
3048 pkt, body
3049 )
3050 # If it's a request / response, could be fragmented
3051 if isinstance(pkt.payload, (DceRpc5Request, DceRpc5Response)) and body:
3052 body = self._defragment(pkt, body)
3053 if not body:
3054 return
3055 # Get opnum and options
3056 opnum, opts = self._up_pkt(pkt)
3057 # Try to parse the payload
3058 if opnum is not None and self.rpc_bind_interface:
3059 # use opnum to parse the payload
3060 is_response = DceRpc5Response in pkt
3061 try:
3062 cls = self.rpc_bind_interface.opnums[opnum][is_response]
3063 except KeyError:
3064 log_runtime.warning(
3065 "Unknown opnum %s for interface %s"
3066 % (opnum, self.rpc_bind_interface)
3067 )
3068 pkt.payload[conf.raw_layer].load = body
3069 return pkt
3070 if body:
3071 orpc = None
3072 if self.rpc_bind_is_com:
3073 # If interface is a COM interface, start off by dissecting the
3074 # ORPCTHIS / ORPCTHAT argument
3075 from scapy.layers.msrpce.raw.ms_dcom import ORPCTHAT, ORPCTHIS
3076
3077 # [MS-DCOM] sect 2.2.13
3078 # "ORPCTHIS and ORPCTHAT structures MUST be marshaled using
3079 # the NDR (32) Transfer Syntax"
3080 if is_response:
3081 orpc = ORPCTHAT(body, ndr64=False)
3082 else:
3083 orpc = ORPCTHIS(body, ndr64=False)
3084 body = orpc.load
3085 orpc.remove_payload()
3086 # Dissect payload using class
3087 try:
3088 payload = cls(
3089 body, ndr64=self.ndr64, ndrendian=self.ndrendian, **opts
3090 )
3091 except Exception:
3092 if conf.debug_dissector:
3093 log_runtime.error("%s dissector failed", cls.__name__)
3094 if cls is not None:
3095 raise
3096 payload = conf.raw_layer(body, _internal=1)
3097 pkt.payload[conf.raw_layer].underlayer.remove_payload()
3098 if conf.padding_layer in payload:
3099 # Most likely, dissection failed.
3100 log_runtime.warning(
3101 "Padding detected when dissecting %s. Looks wrong." % cls
3102 )
3103 pad = payload[conf.padding_layer]
3104 pad.underlayer.payload = conf.raw_layer(load=pad.load)
3105 if orpc is not None:
3106 pkt /= orpc
3107 pkt /= payload
3108 # If a request was encrypted, we need to re-register it once re-parsed.
3109 if not is_response and self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY:
3110 self._up_pkt(pkt)
3111 elif not cls.fields_desc:
3112 # Request class has no payload
3113 pkt /= cls(ndr64=self.ndr64, ndrendian=self.ndrendian, **opts)
3114 elif body:
3115 pkt.payload[conf.raw_layer].load = body
3116 return pkt
3117
3118 def out_pkt(self, pkt):
3119 assert DceRpc5 in pkt
3120 # Register opnum and options
3121 self._up_pkt(pkt)
3122
3123 # If it's a request / response, we can frag it
3124 if isinstance(pkt.payload, (DceRpc5Request, DceRpc5Response)):
3125 # The list of packet responses
3126 pkts = []
3127 # Take the body payload, and eventually split it
3128 body = bytes(pkt.payload.payload)
3129
3130 for pkt, body in self._fragment(pkt, body):
3131 if pkt.auth_verifier is not None:
3132 # Verifier already set
3133 pkts.append(pkt)
3134 continue
3135
3136 # Sign / Encrypt
3137 if self.sspcontext:
3138 signature = None
3139 if self.auth_level in (
3140 RPC_C_AUTHN_LEVEL.PKT_INTEGRITY,
3141 RPC_C_AUTHN_LEVEL.PKT_PRIVACY,
3142 ):
3143 # Remember that vt_trailer is included in the PDU
3144 if pkt.vt_trailer:
3145 body += bytes(pkt.vt_trailer)
3146 # Account for padding when computing checksum/encryption
3147 if pkt.auth_padding is None:
3148 padlen = (-len(body)) % _COMMON_AUTH_PAD # authdata padding
3149 pkt.auth_padding = b"\x00" * padlen
3150 else:
3151 padlen = len(pkt.auth_padding)
3152 # Remember that padding IS SIGNED & ENCRYPTED
3153 body += pkt.auth_padding
3154 # Add the auth_verifier
3155 pkt.auth_verifier = CommonAuthVerifier(
3156 auth_type=self.ssp.auth_type,
3157 auth_level=self.auth_level,
3158 auth_context_id=self.auth_context_id,
3159 auth_pad_length=padlen,
3160 # Note: auth_value should have the correct length because
3161 # when using PFC_SUPPORT_HEADER_SIGN, auth_len
3162 # (and frag_len) is included in the token.. but this
3163 # creates a dependency loop as you'd need to know the token
3164 # length to compute the token. Windows solves this by
3165 # setting the 'Maximum Signature Length' (or something
3166 # similar) beforehand, instead of the real length.
3167 # See `gensec_sig_size` in samba.
3168 auth_value=b"\x00"
3169 * self.ssp.MaximumSignatureLength(self.sspcontext),
3170 )
3171 # Build pdu_header and sec_trailer
3172 pdu_header = pkt.copy()
3173 pdu_header.auth_len = len(pdu_header.auth_verifier) - 8
3174 pdu_header.frag_len = len(pdu_header)
3175 sec_trailer = pdu_header.auth_verifier
3176 # sec_trailer: include the sec_trailer but not the
3177 # Authentication token
3178 authval_len = len(sec_trailer.auth_value)
3179 # sec_trailer.auth_value = None
3180 # Discard everything out of the header
3181 pdu_header.auth_padding = None
3182 pdu_header.auth_verifier = None
3183 pdu_header.payload.payload = NoPayload()
3184 pdu_header.vt_trailer = None
3185 signature = None
3186 # [MS-RPCE] sect 2.2.2.12
3187 if self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY:
3188 _msgs, signature = self.ssp.GSS_WrapEx(
3189 self.sspcontext,
3190 [
3191 # "PDU header"
3192 SSP.WRAP_MSG(
3193 conf_req_flag=False,
3194 sign=self.header_sign,
3195 data=bytes(pdu_header),
3196 ),
3197 # "PDU body"
3198 SSP.WRAP_MSG(
3199 conf_req_flag=True,
3200 sign=True,
3201 data=body,
3202 ),
3203 # "sec_trailer"
3204 SSP.WRAP_MSG(
3205 conf_req_flag=False,
3206 sign=self.header_sign,
3207 data=bytes(sec_trailer)[:-authval_len],
3208 ),
3209 ],
3210 )
3211 s = _msgs[1].data # PDU body
3212 elif self.auth_level == RPC_C_AUTHN_LEVEL.PKT_INTEGRITY:
3213 signature = self.ssp.GSS_GetMICEx(
3214 self.sspcontext,
3215 [
3216 # "PDU header"
3217 SSP.MIC_MSG(
3218 sign=self.header_sign,
3219 data=bytes(pdu_header),
3220 ),
3221 # "PDU body"
3222 SSP.MIC_MSG(
3223 sign=True,
3224 data=body,
3225 ),
3226 # "sec_trailer"
3227 SSP.MIC_MSG(
3228 sign=self.header_sign,
3229 data=bytes(sec_trailer)[:-authval_len],
3230 ),
3231 ],
3232 pkt.auth_verifier.auth_value,
3233 )
3234 s = body
3235 else:
3236 raise ValueError("Impossible")
3237 # Put padding back in the header
3238 if padlen:
3239 s, pkt.auth_padding = s[:-padlen], s[-padlen:]
3240 # Put back vt_trailer into the header
3241 if pkt.vt_trailer:
3242 vtlen = len(pkt.vt_trailer)
3243 s, pkt.vt_trailer = s[:-vtlen], s[-vtlen:]
3244 else:
3245 s = body
3246
3247 # now inject the encrypted payload into the packet
3248 pkt.payload.payload = conf.raw_layer(load=s)
3249 # and the auth_value
3250 if signature:
3251 pkt.auth_verifier.auth_value = signature
3252 else:
3253 pkt.auth_verifier = None
3254 # Add to the list
3255 pkts.append(pkt)
3256 return pkts
3257 else:
3258 return [pkt]
3259
3260 def process(self, pkt: Packet) -> Optional[Packet]:
3261 """
3262 Used when DceRpcSession is used for passive sniffing.
3263 """
3264 pkt = super(DceRpcSession, self).process(pkt)
3265 if pkt is not None and DceRpc5 in pkt:
3266 rpkt = self.in_pkt(pkt)
3267 if rpkt is None:
3268 # We are passively dissecting a fragmented packet. Return
3269 # just the header showing that it was indeed, fragmented.
3270 pkt[DceRpc5].payload.remove_payload()
3271 return pkt
3272 return rpkt
3273 return pkt
3274
3275
3276class DceRpcSocket(StreamSocket):
3277 """
3278 A Wrapper around StreamSocket that uses a DceRpcSession
3279 """
3280
3281 def __init__(self, *args, **kwargs):
3282 self.transport = kwargs.pop("transport", None)
3283 self.session = DceRpcSession(
3284 ssp=kwargs.pop("ssp", None),
3285 auth_level=kwargs.pop("auth_level", None),
3286 auth_context_id=kwargs.pop("auth_context_id", None),
3287 support_header_signing=kwargs.pop("support_header_signing", True),
3288 )
3289 super(DceRpcSocket, self).__init__(*args, **kwargs)
3290
3291 def send(self, x, **kwargs):
3292 for pkt in self.session.out_pkt(x):
3293 if self.transport == DCERPC_Transport.NCACN_NP:
3294 # In this case DceRpcSocket wraps a SMB_RPC_SOCKET, call it directly.
3295 self.ins.send(pkt, **kwargs)
3296 else:
3297 super(DceRpcSocket, self).send(pkt, **kwargs)
3298
3299 def recv(self, x=None):
3300 pkt = super(DceRpcSocket, self).recv(x)
3301 if pkt is not None:
3302 return self.session.in_pkt(pkt)
3303
3304
3305# --- TODO cleanup below
3306
3307# Heuristically way to find the payload class
3308#
3309# To add a possible payload to a DCE/RPC packet, one must first create the
3310# packet class, then instead of binding layers using bind_layers, he must
3311# call DceRpcPayload.register_possible_payload() with the payload class as
3312# parameter.
3313#
3314# To be able to decide if the payload class is capable of handling the rest of
3315# the dissection, the classmethod can_handle() should be implemented in the
3316# payload class. This method is given the rest of the string to dissect as
3317# first argument, and the DceRpc packet instance as second argument. Based on
3318# this information, the method must return True if the class is capable of
3319# handling the dissection, False otherwise
3320
3321
3322class DceRpc4Payload(Packet):
3323 """Dummy class which use the dispatch_hook to find the payload class"""
3324
3325 _payload_class = []
3326
3327 @classmethod
3328 def dispatch_hook(cls, _pkt, _underlayer=None, *args, **kargs):
3329 """dispatch_hook to choose among different registered payloads"""
3330 for klass in cls._payload_class:
3331 if hasattr(klass, "can_handle") and klass.can_handle(_pkt, _underlayer):
3332 return klass
3333 log_runtime.warning("DCE/RPC payload class not found or undefined (using Raw)")
3334 return Raw
3335
3336 @classmethod
3337 def register_possible_payload(cls, pay):
3338 """Method to call from possible DCE/RPC endpoint to register it as
3339 possible payload"""
3340 cls._payload_class.append(pay)
3341
3342
3343bind_layers(DceRpc4, DceRpc4Payload)