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