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