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 interface.if_version == if_version and set(opnums) - set( 1209 interface.opnums 1210 ): 1211 # Interface is an extension of a previous interface 1212 interface.opnums.update(opnums) 1213 return 1214 elif interface.if_version != if_version: 1215 # Interface has a different version 1216 pass 1217 else: 1218 log_runtime.warning( 1219 "This interface is already registered: %s. Skip" % interface 1220 ) 1221 return 1222 else: 1223 raise ValueError( 1224 "An interface with the same UUID is already registered: %s" % interface 1225 ) 1226 DCE_RPC_INTERFACES_NAMES[uuid] = name 1227 DCE_RPC_INTERFACES_NAMES_rev[name.lower()] = uuid 1228 DCE_RPC_INTERFACES[(uuid, if_version)] = DceRpcInterface( 1229 name, 1230 uuid, 1231 version_tuple, 1232 if_version, 1233 opnums, 1234 ) 1235 # bind for build 1236 for opnum, operations in opnums.items(): 1237 bind_top_down(DceRpc5Request, operations.request, opnum=opnum) 1238 1239 1240def find_dcerpc_interface(name): 1241 """ 1242 Find an interface object through the name in the IDL 1243 """ 1244 try: 1245 return next(x for x in DCE_RPC_INTERFACES.values() if x.name == name) 1246 except StopIteration: 1247 raise AttributeError("Unknown interface !") 1248 1249 1250COM_INTERFACES = {} 1251 1252 1253class ComInterface: 1254 def __init__(self, name, uuid, opnums): 1255 self.name = name 1256 self.uuid = uuid 1257 self.opnums = opnums 1258 1259 def __repr__(self): 1260 return "<COM Interface %s>" % (self.name,) 1261 1262 1263def register_com_interface(name, uuid, opnums): 1264 """ 1265 Register a COM interface 1266 """ 1267 COM_INTERFACES[uuid] = ComInterface( 1268 name, 1269 uuid, 1270 opnums, 1271 ) 1272 1273 1274# --- NDR fields - [C706] chap 14 1275 1276 1277def _set_ctx_on(f, obj): 1278 if isinstance(f, _NDRPacket): 1279 f.ndr64 = obj.ndr64 1280 f.ndrendian = obj.ndrendian 1281 if isinstance(f, list): 1282 for x in f: 1283 if isinstance(x, _NDRPacket): 1284 x.ndr64 = obj.ndr64 1285 x.ndrendian = obj.ndrendian 1286 1287 1288def _e(ndrendian): 1289 return {"big": ">", "little": "<"}[ndrendian] 1290 1291 1292class _NDRPacket(Packet): 1293 __slots__ = ["ndr64", "ndrendian", "deferred_pointers", "request_packet"] 1294 1295 def __init__(self, *args, **kwargs): 1296 self.ndr64 = kwargs.pop("ndr64", False) 1297 self.ndrendian = kwargs.pop("ndrendian", "little") 1298 # request_packet is used in the session, so that a response packet 1299 # can resolve union arms if the case parameter is in the request. 1300 self.request_packet = kwargs.pop("request_packet", None) 1301 self.deferred_pointers = [] 1302 super(_NDRPacket, self).__init__(*args, **kwargs) 1303 1304 def do_dissect(self, s): 1305 _up = self.parent or self.underlayer 1306 if _up and isinstance(_up, _NDRPacket): 1307 self.ndr64 = _up.ndr64 1308 self.ndrendian = _up.ndrendian 1309 else: 1310 # See comment above NDRConstructedType 1311 return NDRConstructedType([]).read_deferred_pointers( 1312 self, super(_NDRPacket, self).do_dissect(s) 1313 ) 1314 return super(_NDRPacket, self).do_dissect(s) 1315 1316 def post_dissect(self, s): 1317 if self.deferred_pointers: 1318 # Can't trust the cache if there were deferred pointers 1319 self.raw_packet_cache = None 1320 return s 1321 1322 def do_build(self): 1323 _up = self.parent or self.underlayer 1324 for f in self.fields.values(): 1325 _set_ctx_on(f, self) 1326 if not _up or not isinstance(_up, _NDRPacket): 1327 # See comment above NDRConstructedType 1328 return NDRConstructedType([]).add_deferred_pointers( 1329 self, super(_NDRPacket, self).do_build() 1330 ) 1331 return super(_NDRPacket, self).do_build() 1332 1333 def default_payload_class(self, pkt): 1334 return conf.padding_layer 1335 1336 def clone_with(self, *args, **kwargs): 1337 pkt = super(_NDRPacket, self).clone_with(*args, **kwargs) 1338 # We need to copy deferred_pointers to not break pointer deferral 1339 # on build. 1340 pkt.deferred_pointers = self.deferred_pointers 1341 pkt.ndr64 = self.ndr64 1342 pkt.ndrendian = self.ndrendian 1343 return pkt 1344 1345 def copy(self): 1346 pkt = super(_NDRPacket, self).copy() 1347 pkt.deferred_pointers = self.deferred_pointers 1348 pkt.ndr64 = self.ndr64 1349 pkt.ndrendian = self.ndrendian 1350 return pkt 1351 1352 def show2(self, dump=False, indent=3, lvl="", label_lvl=""): 1353 return self.__class__( 1354 bytes(self), ndr64=self.ndr64, ndrendian=self.ndrendian 1355 ).show(dump, indent, lvl, label_lvl) 1356 1357 def getfield_and_val(self, attr): 1358 try: 1359 return Packet.getfield_and_val(self, attr) 1360 except ValueError: 1361 if self.request_packet: 1362 # Try to resolve the field from the request on failure 1363 try: 1364 return self.request_packet.getfield_and_val(attr) 1365 except AttributeError: 1366 pass 1367 raise 1368 1369 def valueof(self, request): 1370 """ 1371 Util to get the value of a NDRField, ignoring arrays, pointers, etc. 1372 """ 1373 val = self 1374 for ndr_field in request.split("."): 1375 fld, fval = val.getfield_and_val(ndr_field) 1376 val = fld.valueof(val, fval) 1377 return val 1378 1379 1380class _NDRAlign: 1381 def padlen(self, flen, pkt): 1382 return -flen % self._align[pkt.ndr64] 1383 1384 def original_length(self, pkt): 1385 # Find the length of the NDR frag to be able to pad properly 1386 while pkt: 1387 par = pkt.parent or pkt.underlayer 1388 if par and isinstance(par, _NDRPacket): 1389 pkt = par 1390 else: 1391 break 1392 return len(pkt.original) 1393 1394 1395class NDRAlign(_NDRAlign, ReversePadField): 1396 """ 1397 ReversePadField modified to fit NDR. 1398 1399 - If no align size is specified, use the one from the inner field 1400 - Size is calculated from the beginning of the NDR stream 1401 """ 1402 1403 def __init__(self, fld, align, padwith=None): 1404 super(NDRAlign, self).__init__(fld, align=align, padwith=padwith) 1405 1406 1407class _VirtualField(Field): 1408 # Hold a value but doesn't show up when building/dissecting 1409 def addfield(self, pkt, s, x): 1410 return s 1411 1412 def getfield(self, pkt, s): 1413 return s, None 1414 1415 1416class _NDRPacketMetaclass(Packet_metaclass): 1417 def __new__(cls, name, bases, dct): 1418 newcls = super(_NDRPacketMetaclass, cls).__new__(cls, name, bases, dct) 1419 conformants = dct.get("DEPORTED_CONFORMANTS", []) 1420 if conformants: 1421 amount = len(conformants) 1422 if amount == 1: 1423 newcls.fields_desc.insert( 1424 0, 1425 _VirtualField("max_count", None), 1426 ) 1427 else: 1428 newcls.fields_desc.insert( 1429 0, 1430 FieldListField( 1431 "max_counts", 1432 [], 1433 _VirtualField("", 0), 1434 count_from=lambda _: amount, 1435 ), 1436 ) 1437 return newcls # type: ignore 1438 1439 1440class NDRPacket(_NDRPacket, metaclass=_NDRPacketMetaclass): 1441 """ 1442 A NDR Packet. Handles pointer size & endianness 1443 """ 1444 1445 __slots__ = ["_align"] 1446 1447 # NDR64 pad structures 1448 # [MS-RPCE] 2.2.5.3.4.1 1449 ALIGNMENT = (1, 1) 1450 # [C706] sect 14.3.7 - Conformants max_count can be added to the beginning 1451 DEPORTED_CONFORMANTS = [] 1452 1453 1454# Primitive types 1455 1456 1457class _NDRValueOf: 1458 def valueof(self, pkt, x): 1459 return x 1460 1461 1462class _NDRLenField(_NDRValueOf, Field): 1463 """ 1464 Field similar to FieldLenField that takes size_of and adjust as arguments, 1465 and take the value of a size on build. 1466 """ 1467 1468 __slots__ = ["size_of", "adjust"] 1469 1470 def __init__(self, *args, **kwargs): 1471 self.size_of = kwargs.pop("size_of", None) 1472 self.adjust = kwargs.pop("adjust", lambda _, x: x) 1473 super(_NDRLenField, self).__init__(*args, **kwargs) 1474 1475 def i2m(self, pkt, x): 1476 if x is None and pkt is not None and self.size_of is not None: 1477 fld, fval = pkt.getfield_and_val(self.size_of) 1478 f = fld.i2len(pkt, fval) 1479 x = self.adjust(pkt, f) 1480 elif x is None: 1481 x = 0 1482 return x 1483 1484 1485class NDRByteField(_NDRLenField, ByteField): 1486 pass 1487 1488 1489class NDRSignedByteField(_NDRLenField, SignedByteField): 1490 pass 1491 1492 1493class _NDRField(_NDRLenField): 1494 FMT = "" 1495 ALIGN = (0, 0) 1496 1497 def getfield(self, pkt, s): 1498 return NDRAlign( 1499 Field("", 0, fmt=_e(pkt.ndrendian) + self.FMT), align=self.ALIGN 1500 ).getfield(pkt, s) 1501 1502 def addfield(self, pkt, s, val): 1503 return NDRAlign( 1504 Field("", 0, fmt=_e(pkt.ndrendian) + self.FMT), align=self.ALIGN 1505 ).addfield(pkt, s, self.i2m(pkt, val)) 1506 1507 1508class NDRShortField(_NDRField): 1509 FMT = "H" 1510 ALIGN = (2, 2) 1511 1512 1513class NDRSignedShortField(_NDRField): 1514 FMT = "h" 1515 ALIGN = (2, 2) 1516 1517 1518class NDRIntField(_NDRField): 1519 FMT = "I" 1520 ALIGN = (4, 4) 1521 1522 1523class NDRSignedIntField(_NDRField): 1524 FMT = "i" 1525 ALIGN = (4, 4) 1526 1527 1528class NDRLongField(_NDRField): 1529 FMT = "Q" 1530 ALIGN = (8, 8) 1531 1532 1533class NDRSignedLongField(_NDRField): 1534 FMT = "q" 1535 ALIGN = (8, 8) 1536 1537 1538class NDRIEEEFloatField(_NDRField): 1539 FMT = "f" 1540 ALIGN = (4, 4) 1541 1542 1543class NDRIEEEDoubleField(_NDRField): 1544 FMT = "d" 1545 ALIGN = (8, 8) 1546 1547 1548# Enum types 1549 1550 1551class _NDREnumField(_NDRValueOf, EnumField): 1552 # [MS-RPCE] sect 2.2.5.2 - Enums are 4 octets in NDR64 1553 FMTS = ["H", "I"] 1554 1555 def getfield(self, pkt, s): 1556 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64] 1557 return NDRAlign(Field("", 0, fmt=fmt), align=(2, 4)).getfield(pkt, s) 1558 1559 def addfield(self, pkt, s, val): 1560 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64] 1561 return NDRAlign(Field("", 0, fmt=fmt), align=(2, 4)).addfield( 1562 pkt, s, self.i2m(pkt, val) 1563 ) 1564 1565 1566class NDRInt3264EnumField(NDRAlign): 1567 def __init__(self, *args, **kwargs): 1568 super(NDRInt3264EnumField, self).__init__( 1569 _NDREnumField(*args, **kwargs), align=(2, 4) 1570 ) 1571 1572 1573class NDRIntEnumField(_NDRValueOf, NDRAlign): 1574 # v1_enum are always 4-octets, even in NDR32 1575 def __init__(self, *args, **kwargs): 1576 super(NDRIntEnumField, self).__init__( 1577 LEIntEnumField(*args, **kwargs), align=(4, 4) 1578 ) 1579 1580 1581# Special types 1582 1583 1584class NDRInt3264Field(_NDRLenField): 1585 FMTS = ["I", "Q"] 1586 1587 def getfield(self, pkt, s): 1588 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64] 1589 return NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(pkt, s) 1590 1591 def addfield(self, pkt, s, val): 1592 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64] 1593 return NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield( 1594 pkt, s, self.i2m(pkt, val) 1595 ) 1596 1597 1598class NDRSignedInt3264Field(NDRInt3264Field): 1599 FMTS = ["i", "q"] 1600 1601 1602# Pointer types 1603 1604 1605class NDRPointer(_NDRPacket): 1606 fields_desc = [ 1607 MultipleTypeField( 1608 [(XLELongField("referent_id", 1), lambda pkt: pkt and pkt.ndr64)], 1609 XLEIntField("referent_id", 1), 1610 ), 1611 PacketField("value", None, conf.raw_layer), 1612 ] 1613 1614 1615class NDRFullPointerField(_FieldContainer): 1616 """ 1617 A NDR Full/Unique pointer field encapsulation. 1618 1619 :param deferred: This pointer is deferred. This means that it's representation 1620 will not appear after the pointer. 1621 See [C706] 14.3.12.3 - Algorithm for Deferral of Referents 1622 """ 1623 1624 EMBEDDED = False 1625 1626 def __init__(self, fld, deferred=False, fmt="I"): 1627 self.fld = fld 1628 self.default = None 1629 self.deferred = deferred 1630 1631 def getfield(self, pkt, s): 1632 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64] 1633 remain, referent_id = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield( 1634 pkt, s 1635 ) 1636 if not self.EMBEDDED and referent_id == 0: 1637 return remain, None 1638 if self.deferred: 1639 # deferred 1640 ptr = NDRPointer( 1641 ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, referent_id=referent_id 1642 ) 1643 pkt.deferred_pointers.append((ptr, partial(self.fld.getfield, pkt))) 1644 return remain, ptr 1645 remain, val = self.fld.getfield(pkt, remain) 1646 return remain, NDRPointer( 1647 ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, referent_id=referent_id, value=val 1648 ) 1649 1650 def addfield(self, pkt, s, val): 1651 if val is not None and not isinstance(val, NDRPointer): 1652 raise ValueError( 1653 "Expected NDRPointer in %s. You are using it wrong!" % self.name 1654 ) 1655 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64] 1656 fld = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)) 1657 if not self.EMBEDDED and val is None: 1658 return fld.addfield(pkt, s, 0) 1659 else: 1660 _set_ctx_on(val.value, pkt) 1661 s = fld.addfield(pkt, s, val.referent_id) 1662 if self.deferred: 1663 # deferred 1664 pkt.deferred_pointers.append( 1665 ((lambda s: self.fld.addfield(pkt, s, val.value)), val) 1666 ) 1667 return s 1668 return self.fld.addfield(pkt, s, val.value) 1669 1670 def any2i(self, pkt, x): 1671 # User-friendly helper 1672 if x is not None and not isinstance(x, NDRPointer): 1673 return NDRPointer( 1674 referent_id=0x20000, 1675 value=self.fld.any2i(pkt, x), 1676 ) 1677 return x 1678 1679 # Can't use i2repr = Field.i2repr and so on on PY2 :/ 1680 def i2repr(self, pkt, val): 1681 return repr(val) 1682 1683 def i2h(self, pkt, x): 1684 return x 1685 1686 def h2i(self, pkt, x): 1687 return x 1688 1689 def i2len(self, pkt, x): 1690 if x is None: 1691 return 0 1692 return self.fld.i2len(pkt, x.value) 1693 1694 def valueof(self, pkt, x): 1695 if x is None: 1696 return x 1697 return self.fld.valueof(pkt, x.value) 1698 1699 1700class NDRRefEmbPointerField(NDRFullPointerField): 1701 """ 1702 A NDR Embedded Reference pointer 1703 """ 1704 1705 EMBEDDED = True 1706 1707 1708# Constructed types 1709 1710 1711# Note: this is utterly complex and will drive you crazy 1712 1713# If you have a NDRPacket that contains a deferred pointer on the top level 1714# (only happens in non DCE/RPC structures, such as in MS-PAC, where you have an NDR 1715# structure encapsulated in a non-NDR structure), there will be left-over deferred 1716# pointers when exiting dissection/build (deferred pointers are only computed when 1717# reaching a field that extends NDRConstructedType, which is normal: if you follow 1718# the DCE/RPC spec, pointers are never deferred in root structures) 1719# Therefore there is a special case forcing the build/dissection of any leftover 1720# pointers in NDRPacket, if Scapy detects that they won't be handled by any parent. 1721 1722# Implementation notes: I chose to set 'handles_deferred' inside the FIELD, rather 1723# than inside the PACKET. This is faster to compute because whether a constructed type 1724# should handle deferral or not is computed only once when loading, therefore Scapy 1725# knows in advance whether to handle deferred pointers or not. But it is technically 1726# incorrect: with this approach, a structure (packet) cannot be used in 2 code paths 1727# that have different pointer managements. I mean by that that if there was a 1728# structure that was directly embedded in a RPC request without a pointer but also 1729# embedded with a pointer in another RPC request, it would break. 1730# Fortunately this isn't the case: structures are never reused for 2 purposes. 1731# (or at least I never seen that... <i hope this works>) 1732 1733 1734class NDRConstructedType(object): 1735 def __init__(self, fields): 1736 self.handles_deferred = False 1737 self.ndr_fields = fields 1738 self.rec_check_deferral() 1739 1740 def rec_check_deferral(self): 1741 # We iterate through the fields within this constructed type. 1742 # If we have a pointer, mark this field as handling deferrance 1743 # and make all sub-constructed types not. 1744 for f in self.ndr_fields: 1745 if isinstance(f, NDRFullPointerField) and f.deferred: 1746 self.handles_deferred = True 1747 if isinstance(f, NDRConstructedType): 1748 f.rec_check_deferral() 1749 if f.handles_deferred: 1750 self.handles_deferred = True 1751 f.handles_deferred = False 1752 1753 def getfield(self, pkt, s): 1754 s, fval = super(NDRConstructedType, self).getfield(pkt, s) 1755 if isinstance(fval, _NDRPacket): 1756 # If a sub-packet we just dissected has deferred pointers, 1757 # pass it to parent packet to propagate. 1758 pkt.deferred_pointers.extend(fval.deferred_pointers) 1759 del fval.deferred_pointers[:] 1760 if self.handles_deferred: 1761 # This field handles deferral ! 1762 s = self.read_deferred_pointers(pkt, s) 1763 return s, fval 1764 1765 def read_deferred_pointers(self, pkt, s): 1766 # Now read content of the pointers that were deferred 1767 q = collections.deque() 1768 q.extend(pkt.deferred_pointers) 1769 del pkt.deferred_pointers[:] 1770 while q: 1771 # Recursively resolve pointers that were deferred 1772 ptr, getfld = q.popleft() 1773 s, val = getfld(s) 1774 ptr.value = val 1775 if isinstance(val, _NDRPacket): 1776 # Pointer resolves to a packet.. that may have deferred pointers? 1777 q.extend(val.deferred_pointers) 1778 del val.deferred_pointers[:] 1779 return s 1780 1781 def addfield(self, pkt, s, val): 1782 s = super(NDRConstructedType, self).addfield(pkt, s, val) 1783 if isinstance(val, _NDRPacket): 1784 # If a sub-packet we just dissected has deferred pointers, 1785 # pass it to parent packet to propagate. 1786 pkt.deferred_pointers.extend(val.deferred_pointers) 1787 del val.deferred_pointers[:] 1788 if self.handles_deferred: 1789 # This field handles deferral ! 1790 s = self.add_deferred_pointers(pkt, s) 1791 return s 1792 1793 def add_deferred_pointers(self, pkt, s): 1794 # Now add content of pointers that were deferred 1795 q = collections.deque() 1796 q.extend(pkt.deferred_pointers) 1797 del pkt.deferred_pointers[:] 1798 while q: 1799 addfld, fval = q.popleft() 1800 s = addfld(s) 1801 if isinstance(fval, NDRPointer) and isinstance(fval.value, _NDRPacket): 1802 q.extend(fval.value.deferred_pointers) 1803 del fval.value.deferred_pointers[:] 1804 return s 1805 1806 1807class _NDRPacketField(_NDRValueOf, PacketField): 1808 def m2i(self, pkt, m): 1809 return self.cls(m, ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, _parent=pkt) 1810 1811 1812# class _NDRPacketPadField(PadField): 1813# def padlen(self, flen, pkt): 1814# if pkt.ndr64: 1815# return -flen % self._align[1] 1816# else: 1817# return 0 1818 1819 1820class NDRPacketField(NDRConstructedType, NDRAlign): 1821 def __init__(self, name, default, pkt_cls, **kwargs): 1822 self.DEPORTED_CONFORMANTS = pkt_cls.DEPORTED_CONFORMANTS 1823 self.fld = _NDRPacketField(name, default, pkt_cls=pkt_cls, **kwargs) 1824 NDRAlign.__init__( 1825 self, 1826 # There is supposed to be padding after a struct in NDR64? 1827 # _NDRPacketPadField(fld, align=pkt_cls.ALIGNMENT), 1828 self.fld, 1829 align=pkt_cls.ALIGNMENT, 1830 ) 1831 NDRConstructedType.__init__(self, pkt_cls.fields_desc) 1832 1833 def getfield(self, pkt, x): 1834 # Handle deformed conformants max_count here 1835 if self.DEPORTED_CONFORMANTS: 1836 # C706 14.3.2: "In other words, the size information precedes the 1837 # structure and is aligned independently of the structure alignment." 1838 fld = NDRInt3264Field("", 0) 1839 max_counts = [] 1840 for _ in self.DEPORTED_CONFORMANTS: 1841 x, max_count = fld.getfield(pkt, x) 1842 max_counts.append(max_count) 1843 res, val = super(NDRPacketField, self).getfield(pkt, x) 1844 if len(max_counts) == 1: 1845 val.max_count = max_counts[0] 1846 else: 1847 val.max_counts = max_counts 1848 return res, val 1849 return super(NDRPacketField, self).getfield(pkt, x) 1850 1851 def addfield(self, pkt, s, x): 1852 # Handle deformed conformants max_count here 1853 if self.DEPORTED_CONFORMANTS: 1854 mcfld = NDRInt3264Field("", 0) 1855 if len(self.DEPORTED_CONFORMANTS) == 1: 1856 max_counts = [x.max_count] 1857 else: 1858 max_counts = x.max_counts 1859 for fldname, max_count in zip(self.DEPORTED_CONFORMANTS, max_counts): 1860 if max_count is None: 1861 fld, val = x.getfield_and_val(fldname) 1862 max_count = fld.i2len(x, val) 1863 s = mcfld.addfield(pkt, s, max_count) 1864 return super(NDRPacketField, self).addfield(pkt, s, x) 1865 return super(NDRPacketField, self).addfield(pkt, s, x) 1866 1867 1868# Array types 1869 1870 1871class _NDRPacketListField(NDRConstructedType, PacketListField): 1872 """ 1873 A PacketListField for NDR that can optionally pack the packets into NDRPointers 1874 """ 1875 1876 islist = 1 1877 holds_packets = 1 1878 1879 __slots__ = ["ptr_pack", "fld"] 1880 1881 def __init__(self, name, default, pkt_cls, **kwargs): 1882 self.ptr_pack = kwargs.pop("ptr_pack", False) 1883 if self.ptr_pack: 1884 self.fld = NDRFullPointerField( 1885 NDRPacketField("", None, pkt_cls), deferred=True 1886 ) 1887 else: 1888 self.fld = NDRPacketField("", None, pkt_cls) 1889 PacketListField.__init__(self, name, default, pkt_cls=pkt_cls, **kwargs) 1890 NDRConstructedType.__init__(self, [self.fld]) 1891 1892 def m2i(self, pkt, s): 1893 remain, val = self.fld.getfield(pkt, s) 1894 # A mistake here would be to use / instead of add_payload. It adds a copy 1895 # which breaks pointer defferal. Same applies elsewhere 1896 val.add_payload(conf.padding_layer(remain)) 1897 return val 1898 1899 def any2i(self, pkt, x): 1900 # User-friendly helper 1901 if isinstance(x, list): 1902 x = [self.fld.any2i(pkt, y) for y in x] 1903 return super(_NDRPacketListField, self).any2i(pkt, x) 1904 1905 def i2m(self, pkt, val): 1906 return self.fld.addfield(pkt, b"", val) 1907 1908 def i2len(self, pkt, x): 1909 return len(x) 1910 1911 def valueof(self, pkt, x): 1912 return [self.fld.valueof(pkt, y) for y in x] 1913 1914 1915class NDRFieldListField(NDRConstructedType, FieldListField): 1916 """ 1917 A FieldListField for NDR 1918 """ 1919 1920 islist = 1 1921 1922 def __init__(self, *args, **kwargs): 1923 kwargs.pop("ptr_pack", None) # TODO: unimplemented 1924 if "length_is" in kwargs: 1925 kwargs["count_from"] = kwargs.pop("length_is") 1926 elif "size_is" in kwargs: 1927 kwargs["count_from"] = kwargs.pop("size_is") 1928 FieldListField.__init__(self, *args, **kwargs) 1929 NDRConstructedType.__init__(self, [self.field]) 1930 1931 def i2len(self, pkt, x): 1932 return len(x) 1933 1934 def valueof(self, pkt, x): 1935 return [self.field.valueof(pkt, y) for y in x] 1936 1937 1938class NDRVaryingArray(_NDRPacket): 1939 fields_desc = [ 1940 MultipleTypeField( 1941 [(LELongField("offset", 0), lambda pkt: pkt and pkt.ndr64)], 1942 LEIntField("offset", 0), 1943 ), 1944 MultipleTypeField( 1945 [ 1946 ( 1947 LELongField("actual_count", None), 1948 lambda pkt: pkt and pkt.ndr64, 1949 ) 1950 ], 1951 LEIntField("actual_count", None), 1952 ), 1953 PacketField("value", None, conf.raw_layer), 1954 ] 1955 1956 1957class _NDRVarField(object): 1958 """ 1959 NDR Varying Array / String field 1960 """ 1961 1962 LENGTH_FROM = False 1963 COUNT_FROM = False 1964 1965 def __init__(self, *args, **kwargs): 1966 # size is either from the length_is, if specified, or the "actual_count" 1967 self.from_actual = "length_is" not in kwargs 1968 length_is = kwargs.pop("length_is", lambda pkt: pkt.actual_count) 1969 if self.LENGTH_FROM: 1970 kwargs["length_from"] = length_is 1971 elif self.COUNT_FROM: 1972 kwargs["count_from"] = length_is 1973 super(_NDRVarField, self).__init__(*args, **kwargs) 1974 1975 def getfield(self, pkt, s): 1976 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64] 1977 remain, offset = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(pkt, s) 1978 remain, actual_count = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield( 1979 pkt, remain 1980 ) 1981 final = NDRVaryingArray( 1982 ndr64=pkt.ndr64, 1983 ndrendian=pkt.ndrendian, 1984 offset=offset, 1985 actual_count=actual_count, 1986 ) 1987 if self.from_actual: 1988 remain, val = super(_NDRVarField, self).getfield(final, remain) 1989 else: 1990 remain, val = super(_NDRVarField, self).getfield(pkt, remain) 1991 final.value = super(_NDRVarField, self).i2h(pkt, val) 1992 return remain, final 1993 1994 def addfield(self, pkt, s, val): 1995 if not isinstance(val, NDRVaryingArray): 1996 raise ValueError( 1997 "Expected NDRVaryingArray in %s. You are using it wrong!" % self.name 1998 ) 1999 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64] 2000 _set_ctx_on(val.value, pkt) 2001 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(pkt, s, val.offset) 2002 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield( 2003 pkt, 2004 s, 2005 val.actual_count is None 2006 and super(_NDRVarField, self).i2len(pkt, val.value) 2007 or val.actual_count, 2008 ) 2009 return super(_NDRVarField, self).addfield( 2010 pkt, s, super(_NDRVarField, self).h2i(pkt, val.value) 2011 ) 2012 2013 def i2len(self, pkt, x): 2014 return super(_NDRVarField, self).i2len(pkt, x.value) 2015 2016 def any2i(self, pkt, x): 2017 # User-friendly helper 2018 if not isinstance(x, NDRVaryingArray): 2019 return NDRVaryingArray( 2020 value=super(_NDRVarField, self).any2i(pkt, x), 2021 ) 2022 return x 2023 2024 # Can't use i2repr = Field.i2repr and so on on PY2 :/ 2025 def i2repr(self, pkt, val): 2026 return repr(val) 2027 2028 def i2h(self, pkt, x): 2029 return x 2030 2031 def h2i(self, pkt, x): 2032 return x 2033 2034 def valueof(self, pkt, x): 2035 return super(_NDRVarField, self).valueof(pkt, x.value) 2036 2037 2038class NDRConformantArray(_NDRPacket): 2039 fields_desc = [ 2040 MultipleTypeField( 2041 [(LELongField("max_count", None), lambda pkt: pkt and pkt.ndr64)], 2042 LEIntField("max_count", None), 2043 ), 2044 MultipleTypeField( 2045 [ 2046 ( 2047 PacketListField( 2048 "value", 2049 [], 2050 conf.raw_layer, 2051 count_from=lambda pkt: pkt.max_count, 2052 ), 2053 ( 2054 lambda pkt: pkt.fields.get("value", None) 2055 and isinstance(pkt.fields["value"][0], Packet), 2056 lambda _, val: val and isinstance(val[0], Packet), 2057 ), 2058 ) 2059 ], 2060 FieldListField( 2061 "value", [], LEIntField("", 0), count_from=lambda pkt: pkt.max_count 2062 ), 2063 ), 2064 ] 2065 2066 2067class NDRConformantString(_NDRPacket): 2068 fields_desc = [ 2069 MultipleTypeField( 2070 [(LELongField("max_count", None), lambda pkt: pkt and pkt.ndr64)], 2071 LEIntField("max_count", None), 2072 ), 2073 StrField("value", ""), 2074 ] 2075 2076 2077class _NDRConfField(object): 2078 """ 2079 NDR Conformant Array / String field 2080 """ 2081 2082 CONFORMANT_STRING = False 2083 LENGTH_FROM = False 2084 COUNT_FROM = False 2085 2086 def __init__(self, *args, **kwargs): 2087 self.conformant_in_struct = kwargs.pop("conformant_in_struct", False) 2088 # size_is/max_is end up here, and is what defines a conformant field. 2089 if "size_is" in kwargs: 2090 size_is = kwargs.pop("size_is") 2091 if self.LENGTH_FROM: 2092 kwargs["length_from"] = size_is 2093 elif self.COUNT_FROM: 2094 kwargs["count_from"] = size_is 2095 super(_NDRConfField, self).__init__(*args, **kwargs) 2096 2097 def getfield(self, pkt, s): 2098 # [C706] - 14.3.7 Structures Containing Arrays 2099 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64] 2100 if self.conformant_in_struct: 2101 return super(_NDRConfField, self).getfield(pkt, s) 2102 remain, max_count = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield( 2103 pkt, s 2104 ) 2105 remain, val = super(_NDRConfField, self).getfield(pkt, remain) 2106 return remain, ( 2107 NDRConformantString if self.CONFORMANT_STRING else NDRConformantArray 2108 )(ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, max_count=max_count, value=val) 2109 2110 def addfield(self, pkt, s, val): 2111 if self.conformant_in_struct: 2112 return super(_NDRConfField, self).addfield(pkt, s, val) 2113 if self.CONFORMANT_STRING and not isinstance(val, NDRConformantString): 2114 raise ValueError( 2115 "Expected NDRConformantString in %s. You are using it wrong!" 2116 % self.name 2117 ) 2118 elif not self.CONFORMANT_STRING and not isinstance(val, NDRConformantArray): 2119 raise ValueError( 2120 "Expected NDRConformantArray in %s. You are using it wrong!" % self.name 2121 ) 2122 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64] 2123 _set_ctx_on(val.value, pkt) 2124 if val.value and isinstance(val.value[0], NDRVaryingArray): 2125 value = val.value[0] 2126 else: 2127 value = val.value 2128 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield( 2129 pkt, 2130 s, 2131 val.max_count is None 2132 and super(_NDRConfField, self).i2len(pkt, value) 2133 or val.max_count, 2134 ) 2135 return super(_NDRConfField, self).addfield(pkt, s, value) 2136 2137 def _subval(self, x): 2138 if self.conformant_in_struct: 2139 value = x 2140 elif ( 2141 not self.CONFORMANT_STRING 2142 and x.value 2143 and isinstance(x.value[0], NDRVaryingArray) 2144 ): 2145 value = x.value[0] 2146 else: 2147 value = x.value 2148 return value 2149 2150 def i2len(self, pkt, x): 2151 return super(_NDRConfField, self).i2len(pkt, self._subval(x)) 2152 2153 def any2i(self, pkt, x): 2154 # User-friendly helper 2155 if self.conformant_in_struct: 2156 return super(_NDRConfField, self).any2i(pkt, x) 2157 if self.CONFORMANT_STRING and not isinstance(x, NDRConformantString): 2158 return NDRConformantString( 2159 value=super(_NDRConfField, self).any2i(pkt, x), 2160 ) 2161 elif not isinstance(x, NDRConformantArray): 2162 return NDRConformantArray( 2163 value=super(_NDRConfField, self).any2i(pkt, x), 2164 ) 2165 return x 2166 2167 # Can't use i2repr = Field.i2repr and so on on PY2 :/ 2168 def i2repr(self, pkt, val): 2169 return repr(val) 2170 2171 def i2h(self, pkt, x): 2172 return x 2173 2174 def h2i(self, pkt, x): 2175 return x 2176 2177 def valueof(self, pkt, x): 2178 return super(_NDRConfField, self).valueof(pkt, self._subval(x)) 2179 2180 2181class NDRVarPacketListField(_NDRVarField, _NDRPacketListField): 2182 """ 2183 NDR Varying PacketListField. Unused 2184 """ 2185 2186 COUNT_FROM = True 2187 2188 2189class NDRConfPacketListField(_NDRConfField, _NDRPacketListField): 2190 """ 2191 NDR Conformant PacketListField 2192 """ 2193 2194 COUNT_FROM = True 2195 2196 2197class NDRConfVarPacketListField(_NDRConfField, _NDRVarField, _NDRPacketListField): 2198 """ 2199 NDR Conformant Varying PacketListField 2200 """ 2201 2202 COUNT_FROM = True 2203 2204 2205class NDRConfFieldListField(_NDRConfField, NDRFieldListField): 2206 """ 2207 NDR Conformant FieldListField 2208 """ 2209 2210 COUNT_FROM = True 2211 2212 2213class NDRConfVarFieldListField(_NDRConfField, _NDRVarField, NDRFieldListField): 2214 """ 2215 NDR Conformant Varying FieldListField 2216 """ 2217 2218 COUNT_FROM = True 2219 2220 2221# NDR String fields 2222 2223 2224class _NDRUtf16(Field): 2225 def h2i(self, pkt, x): 2226 encoding = {"big": "utf-16be", "little": "utf-16le"}[pkt.ndrendian] 2227 return plain_str(x).encode(encoding) 2228 2229 def i2h(self, pkt, x): 2230 encoding = {"big": "utf-16be", "little": "utf-16le"}[pkt.ndrendian] 2231 return bytes_encode(x).decode(encoding, errors="replace") 2232 2233 2234class NDRConfStrLenField(_NDRConfField, _NDRValueOf, StrLenField): 2235 """ 2236 NDR Conformant StrLenField. 2237 2238 This is not a "string" per NDR, but an a conformant byte array 2239 (e.g. tower_octet_string) 2240 """ 2241 2242 CONFORMANT_STRING = True 2243 LENGTH_FROM = True 2244 2245 2246class NDRConfStrLenFieldUtf16(_NDRConfField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16): 2247 """ 2248 NDR Conformant StrLenField. 2249 2250 See NDRConfLenStrField for comment. 2251 """ 2252 2253 CONFORMANT_STRING = True 2254 ON_WIRE_SIZE_UTF16 = False 2255 LENGTH_FROM = True 2256 2257 2258class NDRVarStrLenField(_NDRVarField, StrLenField): 2259 """ 2260 NDR Varying StrLenField 2261 """ 2262 2263 LENGTH_FROM = True 2264 2265 2266class NDRVarStrLenFieldUtf16(_NDRVarField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16): 2267 """ 2268 NDR Varying StrLenField 2269 """ 2270 2271 ON_WIRE_SIZE_UTF16 = False 2272 LENGTH_FROM = True 2273 2274 2275class NDRConfVarStrLenField(_NDRConfField, _NDRVarField, _NDRValueOf, StrLenField): 2276 """ 2277 NDR Conformant Varying StrLenField 2278 """ 2279 2280 LENGTH_FROM = True 2281 2282 2283class NDRConfVarStrLenFieldUtf16( 2284 _NDRConfField, _NDRVarField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16 2285): 2286 """ 2287 NDR Conformant Varying StrLenField 2288 """ 2289 2290 ON_WIRE_SIZE_UTF16 = False 2291 LENGTH_FROM = True 2292 2293 2294class NDRConfVarStrNullField(_NDRConfField, _NDRVarField, _NDRValueOf, StrNullField): 2295 """ 2296 NDR Conformant Varying StrNullField 2297 """ 2298 2299 NULLFIELD = True 2300 2301 2302class NDRConfVarStrNullFieldUtf16( 2303 _NDRConfField, _NDRVarField, _NDRValueOf, StrNullFieldUtf16, _NDRUtf16 2304): 2305 """ 2306 NDR Conformant Varying StrNullFieldUtf16 2307 """ 2308 2309 ON_WIRE_SIZE_UTF16 = False 2310 NULLFIELD = True 2311 2312 2313# Union type 2314 2315 2316class NDRUnion(_NDRPacket): 2317 fields_desc = [ 2318 IntField("tag", 0), 2319 PacketField("value", None, conf.raw_layer), 2320 ] 2321 2322 2323class _NDRUnionField(MultipleTypeField): 2324 __slots__ = ["switch_fmt", "align"] 2325 2326 def __init__(self, flds, dflt, align, switch_fmt): 2327 self.switch_fmt = switch_fmt 2328 self.align = align 2329 super(_NDRUnionField, self).__init__(flds, dflt) 2330 2331 def getfield(self, pkt, s): 2332 fmt = _e(pkt.ndrendian) + self.switch_fmt[pkt.ndr64] 2333 remain, tag = NDRAlign(Field("", 0, fmt=fmt), align=self.align).getfield(pkt, s) 2334 fld, _ = super(_NDRUnionField, self)._find_fld_pkt_val(pkt, NDRUnion(tag=tag)) 2335 remain, val = fld.getfield(pkt, remain) 2336 return remain, NDRUnion( 2337 tag=tag, value=val, ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, _parent=pkt 2338 ) 2339 2340 def addfield(self, pkt, s, val): 2341 fmt = _e(pkt.ndrendian) + self.switch_fmt[pkt.ndr64] 2342 if not isinstance(val, NDRUnion): 2343 raise ValueError( 2344 "Expected NDRUnion in %s. You are using it wrong!" % self.name 2345 ) 2346 _set_ctx_on(val.value, pkt) 2347 # First, align the whole tag+union against the align param 2348 s = NDRAlign(Field("", 0, fmt=fmt), align=self.align).addfield(pkt, s, val.tag) 2349 # Then, compute the subfield with its own alignment 2350 return super(_NDRUnionField, self).addfield(pkt, s, val) 2351 2352 def _find_fld_pkt_val(self, pkt, val): 2353 fld, val = super(_NDRUnionField, self)._find_fld_pkt_val(pkt, val) 2354 return fld, val.value 2355 2356 # Can't use i2repr = Field.i2repr and so on on PY2 :/ 2357 def i2repr(self, pkt, val): 2358 return repr(val) 2359 2360 def i2h(self, pkt, x): 2361 return x 2362 2363 def h2i(self, pkt, x): 2364 return x 2365 2366 def valueof(self, pkt, x): 2367 fld, val = self._find_fld_pkt_val(pkt, x) 2368 return fld.valueof(pkt, x.value) 2369 2370 2371class NDRUnionField(NDRConstructedType, _NDRUnionField): 2372 def __init__(self, flds, dflt, align, switch_fmt): 2373 _NDRUnionField.__init__(self, flds, dflt, align=align, switch_fmt=switch_fmt) 2374 NDRConstructedType.__init__(self, [x[0] for x in flds] + [dflt]) 2375 2376 def any2i(self, pkt, x): 2377 # User-friendly helper 2378 if x: 2379 if not isinstance(x, NDRUnion): 2380 raise ValueError("Invalid value for %s; should be NDRUnion" % self.name) 2381 else: 2382 x.value = _NDRUnionField.any2i(self, pkt, x) 2383 return x 2384 2385 2386# Misc 2387 2388 2389class NDRRecursiveField(Field): 2390 """ 2391 A special Field that is used for pointer recursion 2392 """ 2393 2394 def __init__(self, name, fmt="I"): 2395 super(NDRRecursiveField, self).__init__(name, None, fmt=fmt) 2396 2397 def getfield(self, pkt, s): 2398 return NDRFullPointerField( 2399 NDRPacketField("", None, pkt.__class__), deferred=True 2400 ).getfield(pkt, s) 2401 2402 def addfield(self, pkt, s, val): 2403 return NDRFullPointerField( 2404 NDRPacketField("", None, pkt.__class__), deferred=True 2405 ).addfield(pkt, s, val) 2406 2407 2408# The very few NDR-specific structures 2409 2410 2411class NDRContextHandle(NDRPacket): 2412 ALIGNMENT = (4, 4) 2413 fields_desc = [ 2414 LEIntField("attributes", 0), 2415 StrFixedLenField("uuid", b"", length=16), 2416 ] 2417 2418 def guess_payload_class(self, payload): 2419 return conf.padding_layer 2420 2421 2422# --- Type Serialization Version 1 - [MSRPCE] sect 2.2.6 2423 2424 2425def _get_ndrtype1_endian(pkt): 2426 if pkt.underlayer is None: 2427 return "<" 2428 return {0x00: ">", 0x10: "<"}.get(pkt.underlayer.Endianness, "<") 2429 2430 2431class NDRSerialization1Header(Packet): 2432 fields_desc = [ 2433 ByteField("Version", 1), 2434 ByteEnumField("Endianness", 0x10, {0x00: "big", 0x10: "little"}), 2435 LEShortField("CommonHeaderLength", 8), 2436 XLEIntField("Filler", 0xCCCCCCCC), 2437 ] 2438 2439 2440class NDRSerialization1PrivateHeader(Packet): 2441 fields_desc = [ 2442 EField( 2443 LEIntField("ObjectBufferLength", 0), endianness_from=_get_ndrtype1_endian 2444 ), 2445 XLEIntField("Filler", 0), 2446 ] 2447 2448 2449def ndr_deserialize1(b, cls, ndr64=False): 2450 """ 2451 Deserialize Type Serialization Version 1 according to [MS-RPCE] sect 2.2.6 2452 """ 2453 if issubclass(cls, NDRPacket): 2454 # We use an intermediary class for two reasons: 2455 # - it properly sets deferred pointers 2456 # - it uses NDRPacketField which handles deported conformant fields 2457 class _cls(NDRPacket): 2458 fields_desc = [ 2459 NDRFullPointerField(NDRPacketField("pkt", None, cls)), 2460 ] 2461 2462 hdr = NDRSerialization1Header(b[:8]) / NDRSerialization1PrivateHeader(b[8:16]) 2463 endian = {0x00: "big", 0x10: "little"}[hdr.Endianness] 2464 padlen = (-hdr.ObjectBufferLength) % _TYPE1_S_PAD 2465 # padlen should be 0 (pad included in length), but some implementations 2466 # implement apparently misread the spec 2467 return ( 2468 hdr 2469 / _cls( 2470 b[16 : 20 + hdr.ObjectBufferLength], 2471 ndr64=ndr64, 2472 ndrendian=endian, 2473 ).pkt 2474 / conf.padding_layer(b[20 + padlen + hdr.ObjectBufferLength :]) 2475 ) 2476 return NDRSerialization1Header(b[:8]) / cls(b[8:]) 2477 2478 2479def ndr_serialize1(pkt): 2480 """ 2481 Serialize Type Serialization Version 1 2482 """ 2483 pkt = pkt.copy() 2484 endian = getattr(pkt, "ndrendian", "little") 2485 if not isinstance(pkt, NDRSerialization1Header): 2486 if not isinstance(pkt, NDRPacket): 2487 return bytes(NDRSerialization1Header(Endianness=endian) / pkt) 2488 if isinstance(pkt, NDRPointer): 2489 cls = pkt.value.__class__ 2490 else: 2491 cls = pkt.__class__ 2492 val = pkt 2493 pkt_len = len(pkt) 2494 # ObjectBufferLength: 2495 # > It MUST include the padding length and exclude the header itself 2496 pkt = NDRSerialization1Header( 2497 Endianness=endian 2498 ) / NDRSerialization1PrivateHeader( 2499 ObjectBufferLength=pkt_len + (-pkt_len) % _TYPE1_S_PAD 2500 ) 2501 else: 2502 cls = pkt.value.__class__ 2503 val = pkt.payload.payload 2504 pkt.payload.remove_payload() 2505 2506 # See above about why we need an intermediary class 2507 class _cls(NDRPacket): 2508 fields_desc = [ 2509 NDRFullPointerField(NDRPacketField("pkt", None, cls)), 2510 ] 2511 2512 ret = bytes(pkt / _cls(pkt=val)) 2513 return ret + (-len(ret) % _TYPE1_S_PAD) * b"\x00" 2514 2515 2516class _NDRSerializeType1: 2517 def __init__(self, *args, **kwargs): 2518 super(_NDRSerializeType1, self).__init__(*args, **kwargs) 2519 2520 def i2m(self, pkt, val): 2521 return ndr_serialize1(val) 2522 2523 def m2i(self, pkt, s): 2524 return ndr_deserialize1(s, self.cls, ndr64=False) 2525 2526 def i2len(self, pkt, val): 2527 return len(self.i2m(pkt, val)) 2528 2529 2530class NDRSerializeType1PacketField(_NDRSerializeType1, PacketField): 2531 __slots__ = ["ptr"] 2532 2533 2534class NDRSerializeType1PacketLenField(_NDRSerializeType1, PacketLenField): 2535 __slots__ = ["ptr"] 2536 2537 2538class NDRSerializeType1PacketListField(_NDRSerializeType1, PacketListField): 2539 __slots__ = ["ptr"] 2540 2541 2542# --- DCE/RPC session 2543 2544 2545class DceRpcSession(DefaultSession): 2546 """ 2547 A DCE/RPC session within a TCP socket. 2548 """ 2549 2550 def __init__(self, *args, **kwargs): 2551 self.rpc_bind_interface = None 2552 self.ndr64 = False 2553 self.ndrendian = "little" 2554 self.support_header_signing = kwargs.pop("support_header_signing", True) 2555 self.header_sign = conf.dcerpc_force_header_signing 2556 self.ssp = kwargs.pop("ssp", None) 2557 self.sspcontext = kwargs.pop("sspcontext", None) 2558 self.auth_level = kwargs.pop("auth_level", None) 2559 self.auth_context_id = kwargs.pop("auth_context_id", 0) 2560 self.map_callid_opnum = {} 2561 self.frags = collections.defaultdict(lambda: b"") 2562 self.sniffsspcontexts = {} # Unfinished contexts for passive 2563 if conf.dcerpc_session_enable and conf.winssps_passive: 2564 for ssp in conf.winssps_passive: 2565 self.sniffsspcontexts[ssp] = None 2566 super(DceRpcSession, self).__init__(*args, **kwargs) 2567 2568 def _up_pkt(self, pkt): 2569 """ 2570 Common function to handle the DCE/RPC session: what interfaces are bind, 2571 opnums, etc. 2572 """ 2573 opnum = None 2574 opts = {} 2575 if DceRpc5Bind in pkt or DceRpc5AlterContext in pkt: 2576 # bind => get which RPC interface 2577 for ctx in pkt.context_elem: 2578 if_uuid = ctx.abstract_syntax.if_uuid 2579 if_version = ctx.abstract_syntax.if_version 2580 try: 2581 self.rpc_bind_interface = DCE_RPC_INTERFACES[(if_uuid, if_version)] 2582 except KeyError: 2583 self.rpc_bind_interface = None 2584 log_runtime.warning( 2585 "Unknown RPC interface %s. Try loading the IDL" % if_uuid 2586 ) 2587 elif DceRpc5BindAck in pkt or DceRpc5AlterContextResp in pkt: 2588 # bind ack => is it NDR64 2589 for res in pkt.results: 2590 if res.result == 0: # Accepted 2591 self.ndrendian = {0: "big", 1: "little"}[pkt[DceRpc5].endian] 2592 if res.transfer_syntax.sprintf("%if_uuid%") == "NDR64": 2593 self.ndr64 = True 2594 elif DceRpc5Request in pkt: 2595 # request => match opnum with callID 2596 opnum = pkt.opnum 2597 self.map_callid_opnum[pkt.call_id] = opnum, pkt[DceRpc5Request].payload 2598 elif DceRpc5Response in pkt: 2599 # response => get opnum from table 2600 try: 2601 opnum, opts["request_packet"] = self.map_callid_opnum[pkt.call_id] 2602 del self.map_callid_opnum[pkt.call_id] 2603 except KeyError: 2604 log_runtime.info("Unknown call_id %s in DCE/RPC session" % pkt.call_id) 2605 # Bind / Alter request/response specific 2606 if ( 2607 DceRpc5Bind in pkt 2608 or DceRpc5AlterContext in pkt 2609 or DceRpc5BindAck in pkt 2610 or DceRpc5AlterContextResp in pkt 2611 ): 2612 # Detect if "Header Signing" is in use 2613 if pkt.pfc_flags & 0x04: # PFC_SUPPORT_HEADER_SIGN 2614 self.header_sign = True 2615 return opnum, opts 2616 2617 # [C706] sect 12.6.2 - Fragmentation and Reassembly 2618 # Since the connection-oriented transport guarantees sequentiality, the receiver 2619 # will always receive the fragments in order. 2620 2621 def _defragment(self, pkt): 2622 """ 2623 Function to defragment DCE/RPC packets. 2624 """ 2625 uid = pkt.call_id 2626 if pkt.pfc_flags.PFC_FIRST_FRAG and pkt.pfc_flags.PFC_LAST_FRAG: 2627 # Not fragmented 2628 return pkt 2629 if pkt.pfc_flags.PFC_FIRST_FRAG or uid in self.frags: 2630 # Packet is fragmented 2631 self.frags[uid] += pkt[DceRpc5].payload.payload.original 2632 if pkt.pfc_flags.PFC_LAST_FRAG: 2633 pkt[DceRpc5].payload.remove_payload() 2634 pkt[DceRpc5].payload /= self.frags[uid] 2635 return pkt 2636 else: 2637 # Not fragmented 2638 return pkt 2639 2640 def _fragment(self, pkt): 2641 """ 2642 Function to fragment DCE/RPC packets. 2643 """ 2644 # unimplemented 2645 pass 2646 2647 # [MS-RPCE] sect 3.3.1.5.2.2 2648 2649 # The PDU header, PDU body, and sec_trailer MUST be passed in the input message, in 2650 # this order, to GSS_WrapEx, GSS_UnwrapEx, GSS_GetMICEx, and GSS_VerifyMICEx. For 2651 # integrity protection the sign flag for that PDU segment MUST be set to TRUE, else 2652 # it MUST be set to FALSE. For confidentiality protection, the conf_req_flag for 2653 # that PDU segment MUST be set to TRUE, else it MUST be set to FALSE. 2654 2655 # If the authentication level is RPC_C_AUTHN_LEVEL_PKT_PRIVACY, the PDU body will 2656 # be encrypted. 2657 # The PDU body from the output message of GSS_UnwrapEx represents the plain text 2658 # version of the PDU body. The PDU header and sec_trailer output from the output 2659 # message SHOULD be ignored. 2660 # Similarly the signature output SHOULD be ignored. 2661 2662 def in_pkt(self, pkt): 2663 # Defragment 2664 pkt = self._defragment(pkt) 2665 if not pkt: 2666 return 2667 # Get opnum and options 2668 opnum, opts = self._up_pkt(pkt) 2669 # Check for encrypted payloads 2670 body = None 2671 if conf.raw_layer in pkt.payload: 2672 body = bytes(pkt.payload[conf.raw_layer]) 2673 # If we are doing passive sniffing 2674 if conf.dcerpc_session_enable and conf.winssps_passive: 2675 # We have Windows SSPs, and no current context 2676 if pkt.auth_verifier and pkt.auth_verifier.is_ssp(): 2677 # This is a bind/alter/auth3 req/resp 2678 for ssp in self.sniffsspcontexts: 2679 self.sniffsspcontexts[ssp], status = ssp.GSS_Passive( 2680 self.sniffsspcontexts[ssp], 2681 pkt.auth_verifier.auth_value, 2682 ) 2683 if status == GSS_S_COMPLETE: 2684 self.auth_level = DCE_C_AUTHN_LEVEL( 2685 int(pkt.auth_verifier.auth_level) 2686 ) 2687 self.ssp = ssp 2688 self.sspcontext = self.sniffsspcontexts[ssp] 2689 self.sniffsspcontexts[ssp] = None 2690 elif ( 2691 self.sspcontext 2692 and pkt.auth_verifier 2693 and pkt.auth_verifier.is_protected() 2694 and body 2695 ): 2696 # This is a request/response 2697 if self.sspcontext.passive: 2698 self.ssp.GSS_Passive_set_Direction( 2699 self.sspcontext, 2700 IsAcceptor=DceRpc5Response in pkt, 2701 ) 2702 if pkt.auth_verifier and pkt.auth_verifier.is_protected() and body: 2703 if self.sspcontext is None: 2704 return pkt 2705 if self.auth_level in ( 2706 RPC_C_AUTHN_LEVEL.PKT_INTEGRITY, 2707 RPC_C_AUTHN_LEVEL.PKT_PRIVACY, 2708 ): 2709 # note: 'vt_trailer' is included in the pdu body 2710 # [MS-RPCE] sect 2.2.2.13 2711 # "The data structures MUST only appear in a request PDU, and they 2712 # SHOULD be placed in the PDU immediately after the stub data but 2713 # before the authentication padding octets. Therefore, for security 2714 # purposes, the verification trailer is considered part of the PDU 2715 # body." 2716 if pkt.vt_trailer: 2717 body += bytes(pkt.vt_trailer) 2718 # Account for padding when computing checksum/encryption 2719 if pkt.auth_padding: 2720 body += pkt.auth_padding 2721 2722 # Build pdu_header and sec_trailer 2723 pdu_header = pkt.copy() 2724 sec_trailer = pdu_header.auth_verifier 2725 # sec_trailer: include the sec_trailer but not the Authentication token 2726 authval_len = len(sec_trailer.auth_value) 2727 # Discard everything out of the header 2728 pdu_header.auth_padding = None 2729 pdu_header.auth_verifier = None 2730 pdu_header.payload.payload = NoPayload() 2731 pdu_header.vt_trailer = None 2732 2733 # [MS-RPCE] sect 2.2.2.12 2734 if self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY: 2735 _msgs = self.ssp.GSS_UnwrapEx( 2736 self.sspcontext, 2737 [ 2738 # "PDU header" 2739 SSP.WRAP_MSG( 2740 conf_req_flag=False, 2741 sign=self.header_sign, 2742 data=bytes(pdu_header), 2743 ), 2744 # "PDU body" 2745 SSP.WRAP_MSG( 2746 conf_req_flag=True, 2747 sign=True, 2748 data=body, 2749 ), 2750 # "sec_trailer" 2751 SSP.WRAP_MSG( 2752 conf_req_flag=False, 2753 sign=self.header_sign, 2754 data=bytes(sec_trailer)[:-authval_len], 2755 ), 2756 ], 2757 pkt.auth_verifier.auth_value, 2758 ) 2759 body = _msgs[1].data # PDU body 2760 elif self.auth_level == RPC_C_AUTHN_LEVEL.PKT_INTEGRITY: 2761 self.ssp.GSS_VerifyMICEx( 2762 self.sspcontext, 2763 [ 2764 # "PDU header" 2765 SSP.MIC_MSG( 2766 sign=self.header_sign, 2767 data=bytes(pdu_header), 2768 ), 2769 # "PDU body" 2770 SSP.MIC_MSG( 2771 sign=True, 2772 data=body, 2773 ), 2774 # "sec_trailer" 2775 SSP.MIC_MSG( 2776 sign=self.header_sign, 2777 data=bytes(sec_trailer)[:-authval_len], 2778 ), 2779 ], 2780 pkt.auth_verifier.auth_value, 2781 ) 2782 # Put padding back into the header 2783 if pkt.auth_padding: 2784 padlen = len(pkt.auth_padding) 2785 body, pkt.auth_padding = body[:-padlen], body[-padlen:] 2786 # Put back vt_trailer into the header 2787 if pkt.vt_trailer: 2788 vtlen = len(pkt.vt_trailer) 2789 body, pkt.vt_trailer = body[:-vtlen], body[-vtlen:] 2790 # Try to parse the payload 2791 if opnum is not None and self.rpc_bind_interface: 2792 # use opnum to parse the payload 2793 is_response = DceRpc5Response in pkt 2794 try: 2795 cls = self.rpc_bind_interface.opnums[opnum][is_response] 2796 except KeyError: 2797 log_runtime.warning( 2798 "Unknown opnum %s for interface %s" 2799 % (opnum, self.rpc_bind_interface) 2800 ) 2801 pkt.payload[conf.raw_layer].load = body 2802 return pkt 2803 if body: 2804 # Dissect payload using class 2805 payload = cls(body, ndr64=self.ndr64, ndrendian=self.ndrendian, **opts) 2806 pkt.payload[conf.raw_layer].underlayer.remove_payload() 2807 pkt /= payload 2808 elif not cls.fields_desc: 2809 # Request class has no payload 2810 pkt /= cls(ndr64=self.ndr64, ndrendian=self.ndrendian, **opts) 2811 elif body: 2812 pkt.payload[conf.raw_layer].load = body 2813 return pkt 2814 2815 def out_pkt(self, pkt): 2816 assert DceRpc5 in pkt 2817 self._up_pkt(pkt) 2818 if pkt.auth_verifier is not None: 2819 # Verifier already set 2820 return [pkt] 2821 if self.sspcontext and isinstance( 2822 pkt.payload, (DceRpc5Request, DceRpc5Response) 2823 ): 2824 body = bytes(pkt.payload.payload) 2825 signature = None 2826 if self.auth_level in ( 2827 RPC_C_AUTHN_LEVEL.PKT_INTEGRITY, 2828 RPC_C_AUTHN_LEVEL.PKT_PRIVACY, 2829 ): 2830 # Account for padding when computing checksum/encryption 2831 if pkt.auth_padding is None: 2832 padlen = (-len(body)) % _COMMON_AUTH_PAD # authdata padding 2833 pkt.auth_padding = b"\x00" * padlen 2834 else: 2835 padlen = len(pkt.auth_padding) 2836 # Remember that vt_trailer is included in the PDU 2837 if pkt.vt_trailer: 2838 body += bytes(pkt.vt_trailer) 2839 # Remember that padding IS SIGNED & ENCRYPTED 2840 body += pkt.auth_padding 2841 # Add the auth_verifier 2842 pkt.auth_verifier = CommonAuthVerifier( 2843 auth_type=self.ssp.auth_type, 2844 auth_level=self.auth_level, 2845 auth_context_id=self.auth_context_id, 2846 auth_pad_length=padlen, 2847 # Note: auth_value should have the correct length because when 2848 # using PFC_SUPPORT_HEADER_SIGN, auth_len (and frag_len) is 2849 # included in the token.. but this creates a dependency loop as 2850 # you'd need to know the token length to compute the token. 2851 # Windows solves this by setting the 'Maximum Signature Length' 2852 # (or something similar) beforehand, instead of the real length. 2853 # See `gensec_sig_size` in samba. 2854 auth_value=b"\x00" 2855 * self.ssp.MaximumSignatureLength(self.sspcontext), 2856 ) 2857 # Build pdu_header and sec_trailer 2858 pdu_header = pkt.copy() 2859 pdu_header.auth_len = len(pdu_header.auth_verifier) - 8 2860 pdu_header.frag_len = len(pdu_header) 2861 sec_trailer = pdu_header.auth_verifier 2862 # sec_trailer: include the sec_trailer but not the Authentication token 2863 authval_len = len(sec_trailer.auth_value) 2864 # sec_trailer.auth_value = None 2865 # Discard everything out of the header 2866 pdu_header.auth_padding = None 2867 pdu_header.auth_verifier = None 2868 pdu_header.payload.payload = NoPayload() 2869 pdu_header.vt_trailer = None 2870 signature = None 2871 # [MS-RPCE] sect 2.2.2.12 2872 if self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY: 2873 _msgs, signature = self.ssp.GSS_WrapEx( 2874 self.sspcontext, 2875 [ 2876 # "PDU header" 2877 SSP.WRAP_MSG( 2878 conf_req_flag=False, 2879 sign=self.header_sign, 2880 data=bytes(pdu_header), 2881 ), 2882 # "PDU body" 2883 SSP.WRAP_MSG( 2884 conf_req_flag=True, 2885 sign=True, 2886 data=body, 2887 ), 2888 # "sec_trailer" 2889 SSP.WRAP_MSG( 2890 conf_req_flag=False, 2891 sign=self.header_sign, 2892 data=bytes(sec_trailer)[:-authval_len], 2893 ), 2894 ], 2895 ) 2896 s = _msgs[1].data # PDU body 2897 elif self.auth_level == RPC_C_AUTHN_LEVEL.PKT_INTEGRITY: 2898 signature = self.ssp.GSS_GetMICEx( 2899 self.sspcontext, 2900 [ 2901 # "PDU header" 2902 SSP.MIC_MSG( 2903 sign=self.header_sign, 2904 data=bytes(pdu_header), 2905 ), 2906 # "PDU body" 2907 SSP.MIC_MSG( 2908 sign=True, 2909 data=body, 2910 ), 2911 # "sec_trailer" 2912 SSP.MIC_MSG( 2913 sign=self.header_sign, 2914 data=bytes(sec_trailer)[:-authval_len], 2915 ), 2916 ], 2917 pkt.auth_verifier.auth_value, 2918 ) 2919 s = body 2920 else: 2921 raise ValueError("Impossible") 2922 # Put padding back in the header 2923 if padlen: 2924 s, pkt.auth_padding = s[:-padlen], s[-padlen:] 2925 # Put back vt_trailer into the header 2926 if pkt.vt_trailer: 2927 vtlen = len(pkt.vt_trailer) 2928 s, pkt.vt_trailer = s[:-vtlen], s[-vtlen:] 2929 else: 2930 s = body 2931 2932 # now inject the encrypted payload into the packet 2933 pkt.payload.payload = conf.raw_layer(load=s) 2934 # and the auth_value 2935 if signature: 2936 pkt.auth_verifier.auth_value = signature 2937 else: 2938 pkt.auth_verifier = None 2939 return [pkt] 2940 2941 def process(self, pkt: Packet) -> Optional[Packet]: 2942 pkt = super(DceRpcSession, self).process(pkt) 2943 if pkt is not None and DceRpc5 in pkt: 2944 return self.in_pkt(pkt) 2945 return pkt 2946 2947 2948class DceRpcSocket(StreamSocket): 2949 """ 2950 A Wrapper around StreamSocket that uses a DceRpcSession 2951 """ 2952 2953 def __init__(self, *args, **kwargs): 2954 self.session = DceRpcSession( 2955 ssp=kwargs.pop("ssp", None), 2956 auth_level=kwargs.pop("auth_level", None), 2957 auth_context_id=kwargs.pop("auth_context_id", None), 2958 support_header_signing=kwargs.pop("support_header_signing", True), 2959 ) 2960 super(DceRpcSocket, self).__init__(*args, **kwargs) 2961 2962 def send(self, x, **kwargs): 2963 for pkt in self.session.out_pkt(x): 2964 return super(DceRpcSocket, self).send(pkt, **kwargs) 2965 2966 def recv(self, x=None): 2967 pkt = super(DceRpcSocket, self).recv(x) 2968 if pkt is not None: 2969 return self.session.in_pkt(pkt) 2970 2971 2972# --- TODO cleanup below 2973 2974# Heuristically way to find the payload class 2975# 2976# To add a possible payload to a DCE/RPC packet, one must first create the 2977# packet class, then instead of binding layers using bind_layers, he must 2978# call DceRpcPayload.register_possible_payload() with the payload class as 2979# parameter. 2980# 2981# To be able to decide if the payload class is capable of handling the rest of 2982# the dissection, the classmethod can_handle() should be implemented in the 2983# payload class. This method is given the rest of the string to dissect as 2984# first argument, and the DceRpc packet instance as second argument. Based on 2985# this information, the method must return True if the class is capable of 2986# handling the dissection, False otherwise 2987 2988 2989class DceRpc4Payload(Packet): 2990 """Dummy class which use the dispatch_hook to find the payload class""" 2991 2992 _payload_class = [] 2993 2994 @classmethod 2995 def dispatch_hook(cls, _pkt, _underlayer=None, *args, **kargs): 2996 """dispatch_hook to choose among different registered payloads""" 2997 for klass in cls._payload_class: 2998 if hasattr(klass, "can_handle") and klass.can_handle(_pkt, _underlayer): 2999 return klass 3000 log_runtime.warning("DCE/RPC payload class not found or undefined (using Raw)") 3001 return Raw 3002 3003 @classmethod 3004 def register_possible_payload(cls, pay): 3005 """Method to call from possible DCE/RPC endpoint to register it as 3006 possible payload""" 3007 cls._payload_class.append(pay) 3008 3009 3010bind_layers(DceRpc4, DceRpc4Payload) 3011