Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/layers/dcerpc.py: 49%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1161 statements  

1# SPDX-License-Identifier: GPL-2.0-or-later 

2# This file is part of Scapy 

3# See https://scapy.net/ for more information 

4# Copyright (C) Gabriel Potter 

5 

6# scapy.contrib.description = DCE/RPC 

7# scapy.contrib.status = loads 

8 

9""" 

10DCE/RPC 

11Distributed Computing Environment / Remote Procedure Calls 

12 

13Based on [C706] - aka DCE/RPC 1.1 

14https://pubs.opengroup.org/onlinepubs/9629399/toc.pdf 

15 

16And on [MS-RPCE] 

17https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rpce/290c38b1-92fe-4229-91e6-4fc376610c15 

18 

19.. note:: 

20 Please read the documentation over 

21 `DCE/RPC <https://scapy.readthedocs.io/en/latest/layers/dcerpc.html>`_ 

22""" 

23 

24from functools import partial 

25 

26import collections 

27import struct 

28from enum import IntEnum 

29from uuid import UUID 

30from scapy.base_classes import Packet_metaclass 

31 

32from scapy.config import conf 

33from scapy.compat import bytes_encode, plain_str 

34from scapy.error import log_runtime 

35from scapy.layers.dns import DNSStrField 

36from scapy.layers.ntlm import ( 

37 NTLM_Header, 

38 NTLMSSP_MESSAGE_SIGNATURE, 

39) 

40from scapy.packet import ( 

41 Packet, 

42 Raw, 

43 bind_bottom_up, 

44 bind_layers, 

45 bind_top_down, 

46 NoPayload, 

47) 

48from scapy.fields import ( 

49 _FieldContainer, 

50 BitEnumField, 

51 BitField, 

52 ByteEnumField, 

53 ByteField, 

54 ConditionalField, 

55 EnumField, 

56 Field, 

57 FieldLenField, 

58 FieldListField, 

59 FlagsField, 

60 IntField, 

61 LEIntEnumField, 

62 LEIntField, 

63 LELongField, 

64 LEShortEnumField, 

65 LEShortField, 

66 LenField, 

67 MultipleTypeField, 

68 PacketField, 

69 PacketLenField, 

70 PacketListField, 

71 PadField, 

72 ReversePadField, 

73 ShortEnumField, 

74 ShortField, 

75 SignedByteField, 

76 StrField, 

77 StrFixedLenField, 

78 StrLenField, 

79 StrLenFieldUtf16, 

80 StrNullField, 

81 StrNullFieldUtf16, 

82 TrailerField, 

83 UUIDEnumField, 

84 UUIDField, 

85 XByteField, 

86 XLEIntField, 

87 XLELongField, 

88 XLEShortField, 

89 XShortField, 

90 XStrFixedLenField, 

91) 

92from scapy.sessions import DefaultSession 

93from scapy.supersocket import StreamSocket 

94 

95from scapy.layers.kerberos import ( 

96 KRB_InnerToken, 

97 Kerberos, 

98) 

99from scapy.layers.gssapi import ( 

100 GSSAPI_BLOB, 

101 GSSAPI_BLOB_SIGNATURE, 

102 GSS_S_COMPLETE, 

103 GSS_S_FLAGS, 

104 GSS_C_FLAGS, 

105 SSP, 

106) 

107from scapy.layers.inet import TCP 

108 

109from scapy.contrib.rtps.common_types import ( 

110 EField, 

111 EPacket, 

112 EPacketField, 

113 EPacketListField, 

114) 

115 

116# Typing imports 

117from typing import ( 

118 Optional, 

119) 

120 

121# the alignment of auth_pad 

122# This is 4 in [C706] 13.2.6.1 but was updated to 16 in [MS-RPCE] 2.2.2.11 

123_COMMON_AUTH_PAD = 16 

124# the alignment of the NDR Type 1 serialization private header 

125# ([MS-RPCE] sect 2.2.6.2) 

126_TYPE1_S_PAD = 8 

127 

128# DCE/RPC Packet 

129DCE_RPC_TYPE = { 

130 0: "request", 

131 1: "ping", 

132 2: "response", 

133 3: "fault", 

134 4: "working", 

135 5: "no_call", 

136 6: "reject", 

137 7: "acknowledge", 

138 8: "connectionless_cancel", 

139 9: "frag_ack", 

140 10: "cancel_ack", 

141 11: "bind", 

142 12: "bind_ack", 

143 13: "bind_nak", 

144 14: "alter_context", 

145 15: "alter_context_resp", 

146 16: "auth3", 

147 17: "shutdown", 

148 18: "co_cancel", 

149 19: "orphaned", 

150} 

151_DCE_RPC_4_FLAGS1 = [ 

152 "reserved_01", 

153 "last_frag", 

154 "frag", 

155 "no_frag_ack", 

156 "maybe", 

157 "idempotent", 

158 "broadcast", 

159 "reserved_7", 

160] 

161_DCE_RPC_4_FLAGS2 = [ 

162 "reserved_0", 

163 "cancel_pending", 

164 "reserved_2", 

165 "reserved_3", 

166 "reserved_4", 

167 "reserved_5", 

168 "reserved_6", 

169 "reserved_7", 

170] 

171DCE_RPC_TRANSFER_SYNTAXES = { 

172 UUID("00000000-0000-0000-0000-000000000000"): "NULL", 

173 UUID("6cb71c2c-9812-4540-0300-000000000000"): "Bind Time Feature Negotiation", 

174 UUID("8a885d04-1ceb-11c9-9fe8-08002b104860"): "NDR 2.0", 

175 UUID("71710533-beba-4937-8319-b5dbef9ccc36"): "NDR64", 

176} 

177DCE_RPC_INTERFACES_NAMES = {} 

178DCE_RPC_INTERFACES_NAMES_rev = {} 

179 

180 

181class DCERPC_Transport(IntEnum): 

182 NCACN_IP_TCP = 1 

183 NCACN_NP = 2 

184 # TODO: add more.. if people use them? 

185 

186 

187def _dce_rpc_endianness(pkt): 

188 """ 

189 Determine the right endianness sign for a given DCE/RPC packet 

190 """ 

191 if pkt.endian == 0: # big endian 

192 return ">" 

193 elif pkt.endian == 1: # little endian 

194 return "<" 

195 else: 

196 return "!" 

197 

198 

199class _EField(EField): 

200 def __init__(self, fld): 

201 super(_EField, self).__init__(fld, endianness_from=_dce_rpc_endianness) 

202 

203 

204class DceRpc(Packet): 

205 """DCE/RPC packet""" 

206 

207 @classmethod 

208 def dispatch_hook(cls, _pkt=None, *args, **kargs): 

209 if _pkt and len(_pkt) >= 1: 

210 ver = ord(_pkt[0:1]) 

211 if ver == 4: 

212 return DceRpc4 

213 elif ver == 5: 

214 return DceRpc5 

215 return DceRpc5 

216 

217 @classmethod 

218 def tcp_reassemble(cls, data, metadata, session): 

219 if data[0:1] == b"\x05": 

220 return DceRpc5.tcp_reassemble(data, metadata, session) 

221 return DceRpc(data) 

222 

223 

224bind_bottom_up(TCP, DceRpc, sport=135) 

225bind_layers(TCP, DceRpc, dport=135) 

226 

227 

228class _DceRpcPayload(Packet): 

229 @property 

230 def endianness(self): 

231 if not self.underlayer: 

232 return "!" 

233 return _dce_rpc_endianness(self.underlayer) 

234 

235 

236# sect 12.5 

237 

238_drep = [ 

239 BitEnumField("endian", 1, 4, ["big", "little"]), 

240 BitEnumField("encoding", 0, 4, ["ASCII", "EBCDIC"]), 

241 ByteEnumField("float", 0, ["IEEE", "VAX", "CRAY", "IBM"]), 

242 ByteField("reserved1", 0), 

243] 

244 

245 

246class DceRpc4(DceRpc): 

247 """ 

248 DCE/RPC v4 'connection-less' packet 

249 """ 

250 

251 name = "DCE/RPC v4" 

252 fields_desc = ( 

253 [ 

254 ByteEnumField( 

255 "rpc_vers", 4, {4: "4 (connection-less)", 5: "5 (connection-oriented)"} 

256 ), 

257 ByteEnumField("ptype", 0, DCE_RPC_TYPE), 

258 FlagsField("flags1", 0, 8, _DCE_RPC_4_FLAGS1), 

259 FlagsField("flags2", 0, 8, _DCE_RPC_4_FLAGS2), 

260 ] 

261 + _drep 

262 + [ 

263 XByteField("serial_hi", 0), 

264 _EField(UUIDField("object", None)), 

265 _EField(UUIDField("if_id", None)), 

266 _EField(UUIDField("act_id", None)), 

267 _EField(IntField("server_boot", 0)), 

268 _EField(IntField("if_vers", 1)), 

269 _EField(IntField("seqnum", 0)), 

270 _EField(ShortField("opnum", 0)), 

271 _EField(XShortField("ihint", 0xFFFF)), 

272 _EField(XShortField("ahint", 0xFFFF)), 

273 _EField(LenField("len", None, fmt="H")), 

274 _EField(ShortField("fragnum", 0)), 

275 ByteEnumField("auth_proto", 0, ["none", "OSF DCE Private Key"]), 

276 XByteField("serial_lo", 0), 

277 ] 

278 ) 

279 

280 

281# Exceptionally, we define those 3 here. 

282 

283 

284class NL_AUTH_MESSAGE(Packet): 

285 # [MS-NRPC] sect 2.2.1.3.1 

286 name = "NL_AUTH_MESSAGE" 

287 fields_desc = [ 

288 LEIntEnumField( 

289 "MessageType", 

290 0x00000000, 

291 { 

292 0x00000000: "Request", 

293 0x00000001: "Response", 

294 }, 

295 ), 

296 FlagsField( 

297 "Flags", 

298 0, 

299 -32, 

300 [ 

301 "NETBIOS_DOMAIN_NAME", 

302 "NETBIOS_COMPUTER_NAME", 

303 "DNS_DOMAIN_NAME", 

304 "DNS_HOST_NAME", 

305 "NETBIOS_COMPUTER_NAME_UTF8", 

306 ], 

307 ), 

308 ConditionalField( 

309 StrNullField("NetbiosDomainName", ""), 

310 lambda pkt: pkt.Flags.NETBIOS_DOMAIN_NAME, 

311 ), 

312 ConditionalField( 

313 StrNullField("NetbiosComputerName", ""), 

314 lambda pkt: pkt.Flags.NETBIOS_COMPUTER_NAME, 

315 ), 

316 ConditionalField( 

317 DNSStrField("DnsDomainName", ""), 

318 lambda pkt: pkt.Flags.DNS_DOMAIN_NAME, 

319 ), 

320 ConditionalField( 

321 DNSStrField("DnsHostName", ""), 

322 lambda pkt: pkt.Flags.DNS_HOST_NAME, 

323 ), 

324 ConditionalField( 

325 # What the fuck? Why are they doing this 

326 # The spec is just wrong 

327 DNSStrField("NetbiosComputerNameUtf8", ""), 

328 lambda pkt: pkt.Flags.NETBIOS_COMPUTER_NAME_UTF8, 

329 ), 

330 ] 

331 

332 

333class NL_AUTH_SIGNATURE(Packet): 

334 # [MS-NRPC] sect 2.2.1.3.2/2.2.1.3.3 

335 name = "NL_AUTH_(SHA2_)SIGNATURE" 

336 fields_desc = [ 

337 LEShortEnumField( 

338 "SignatureAlgorithm", 

339 0x0077, 

340 { 

341 0x0077: "HMAC-MD5", 

342 0x0013: "HMAC-SHA256", 

343 }, 

344 ), 

345 LEShortEnumField( 

346 "SealAlgorithm", 

347 0xFFFF, 

348 { 

349 0xFFFF: "Unencrypted", 

350 0x007A: "RC4", 

351 0x001A: "AES-128", 

352 }, 

353 ), 

354 XLEShortField("Pad", 0xFFFF), 

355 ShortField("Flags", 0), 

356 XStrFixedLenField("SequenceNumber", b"", length=8), 

357 XStrFixedLenField("Checksum", b"", length=8), 

358 ConditionalField( 

359 XStrFixedLenField("Confounder", b"", length=8), 

360 lambda pkt: pkt.SealAlgorithm != 0xFFFF, 

361 ), 

362 MultipleTypeField( 

363 [ 

364 ( 

365 StrFixedLenField("Reserved2", b"", length=24), 

366 lambda pkt: pkt.SignatureAlgorithm == 0x0013, 

367 ), 

368 ], 

369 StrField("Reserved2", b""), 

370 ), 

371 ] 

372 

373 

374# [MS-RPCE] sect 2.2.1.1.7 

375# https://learn.microsoft.com/en-us/windows/win32/rpc/authentication-service-constants 

376# rpcdce.h 

377 

378 

379class RPC_C_AUTHN(IntEnum): 

380 NONE = 0x00 

381 DCE_PRIVATE = 0x01 

382 DCE_PUBLIC = 0x02 

383 DEC_PUBLIC = 0x04 

384 GSS_NEGOTIATE = 0x09 

385 WINNT = 0x0A 

386 GSS_SCHANNEL = 0x0E 

387 GSS_KERBEROS = 0x10 

388 DPA = 0x11 

389 MSN = 0x12 

390 KERNEL = 0x14 

391 DIGEST = 0x15 

392 NEGO_EXTENDED = 0x1E 

393 PKU2U = 0x1F 

394 LIVE_SSP = 0x20 

395 LIVEXP_SSP = 0x23 

396 CLOUD_AP = 0x24 

397 NETLOGON = 0x44 

398 MSONLINE = 0x52 

399 MQ = 0x64 

400 DEFAULT = 0xFFFFFFFF 

401 

402 

403class RPC_C_AUTHN_LEVEL(IntEnum): 

404 DEFAULT = 0x0 

405 NONE = 0x1 

406 CONNECT = 0x2 

407 CALL = 0x3 

408 PKT = 0x4 

409 PKT_INTEGRITY = 0x5 

410 PKT_PRIVACY = 0x6 

411 

412 

413DCE_C_AUTHN_LEVEL = RPC_C_AUTHN_LEVEL # C706 name 

414 

415 

416# C706 sect 13.2.6.1 

417 

418 

419class CommonAuthVerifier(Packet): 

420 name = "Common Authentication Verifier" 

421 fields_desc = [ 

422 ByteEnumField( 

423 "auth_type", 

424 0, 

425 RPC_C_AUTHN, 

426 ), 

427 ByteEnumField("auth_level", 0, RPC_C_AUTHN_LEVEL), 

428 ByteField("auth_pad_length", None), 

429 ByteField("auth_reserved", 0), 

430 XLEIntField("auth_context_id", 0), 

431 MultipleTypeField( 

432 [ 

433 # SPNEGO 

434 ( 

435 PacketLenField( 

436 "auth_value", 

437 GSSAPI_BLOB(), 

438 GSSAPI_BLOB, 

439 length_from=lambda pkt: pkt.parent.auth_len, 

440 ), 

441 lambda pkt: pkt.auth_type == 0x09 and pkt.parent and 

442 # Bind/Alter 

443 pkt.parent.ptype in [11, 12, 13, 14, 15, 16], 

444 ), 

445 ( 

446 PacketLenField( 

447 "auth_value", 

448 GSSAPI_BLOB_SIGNATURE(), 

449 GSSAPI_BLOB_SIGNATURE, 

450 length_from=lambda pkt: pkt.parent.auth_len, 

451 ), 

452 lambda pkt: pkt.auth_type == 0x09 

453 and pkt.parent 

454 and ( 

455 # Other 

456 not pkt.parent 

457 or pkt.parent.ptype not in [11, 12, 13, 14, 15, 16] 

458 ), 

459 ), 

460 # Kerberos 

461 ( 

462 PacketLenField( 

463 "auth_value", 

464 Kerberos(), 

465 Kerberos, 

466 length_from=lambda pkt: pkt.parent.auth_len, 

467 ), 

468 lambda pkt: pkt.auth_type == 0x10 and pkt.parent and 

469 # Bind/Alter 

470 pkt.parent.ptype in [11, 12, 13, 14, 15, 16], 

471 ), 

472 ( 

473 PacketLenField( 

474 "auth_value", 

475 KRB_InnerToken(), 

476 KRB_InnerToken, 

477 length_from=lambda pkt: pkt.parent.auth_len, 

478 ), 

479 lambda pkt: pkt.auth_type == 0x10 

480 and pkt.parent 

481 and ( 

482 # Other 

483 not pkt.parent 

484 or pkt.parent.ptype not in [11, 12, 13, 14, 15, 16] 

485 ), 

486 ), 

487 # NTLM 

488 ( 

489 PacketLenField( 

490 "auth_value", 

491 NTLM_Header(), 

492 NTLM_Header, 

493 length_from=lambda pkt: pkt.parent.auth_len, 

494 ), 

495 lambda pkt: pkt.auth_type in [0x0A, 0xFF] and pkt.parent and 

496 # Bind/Alter 

497 pkt.parent.ptype in [11, 12, 13, 14, 15, 16], 

498 ), 

499 ( 

500 PacketLenField( 

501 "auth_value", 

502 NTLMSSP_MESSAGE_SIGNATURE(), 

503 NTLMSSP_MESSAGE_SIGNATURE, 

504 length_from=lambda pkt: pkt.parent.auth_len, 

505 ), 

506 lambda pkt: pkt.auth_type in [0x0A, 0xFF] 

507 and pkt.parent 

508 and ( 

509 # Other 

510 not pkt.parent 

511 or pkt.parent.ptype not in [11, 12, 13, 14, 15, 16] 

512 ), 

513 ), 

514 # NetLogon 

515 ( 

516 PacketLenField( 

517 "auth_value", 

518 NL_AUTH_MESSAGE(), 

519 NL_AUTH_MESSAGE, 

520 length_from=lambda pkt: pkt.parent.auth_len, 

521 ), 

522 lambda pkt: pkt.auth_type == 0x44 and pkt.parent and 

523 # Bind/Alter 

524 pkt.parent.ptype in [11, 12, 13, 14, 15], 

525 ), 

526 ( 

527 PacketLenField( 

528 "auth_value", 

529 NL_AUTH_SIGNATURE(), 

530 NL_AUTH_SIGNATURE, 

531 length_from=lambda pkt: pkt.parent.auth_len, 

532 ), 

533 lambda pkt: pkt.auth_type == 0x44 

534 and ( 

535 # Other 

536 not pkt.parent 

537 or pkt.parent.ptype not in [11, 12, 13, 14, 15] 

538 ), 

539 ), 

540 ], 

541 PacketLenField( 

542 "auth_value", 

543 None, 

544 conf.raw_layer, 

545 length_from=lambda pkt: pkt.parent and pkt.parent.auth_len or 0, 

546 ), 

547 ), 

548 ] 

549 

550 def is_protected(self): 

551 if not self.auth_value: 

552 return False 

553 if self.parent and self.parent.ptype in [11, 12, 13, 14, 15, 16]: 

554 return False 

555 return True 

556 

557 def is_ssp(self): 

558 if not self.auth_value: 

559 return False 

560 if self.parent and self.parent.ptype not in [11, 12, 13, 14, 15, 16]: 

561 return False 

562 return True 

563 

564 def default_payload_class(self, pkt): 

565 return conf.padding_layer 

566 

567 

568# [MS-RPCE] sect 2.2.2.13 - Verification Trailer 

569_SECTRAILER_MAGIC = b"\x8a\xe3\x13\x71\x02\xf4\x36\x71" 

570 

571 

572class DceRpcSecVTCommand(Packet): 

573 name = "Verification trailer command" 

574 fields_desc = [ 

575 BitField("SEC_VT_MUST_PROCESS_COMMAND", 0, 1, tot_size=-2), 

576 BitField("SEC_VT_COMMAND_END", 0, 1), 

577 BitEnumField( 

578 "Command", 

579 0, 

580 -14, 

581 { 

582 0x0001: "SEC_VT_COMMAND_BITMASK_1", 

583 0x0002: "SEC_VT_COMMAND_PCONTEXT", 

584 0x0003: "SEC_VT_COMMAND_HEADER2", 

585 }, 

586 end_tot_size=-2, 

587 ), 

588 LEShortField("Length", None), 

589 ] 

590 

591 def guess_payload_class(self, payload): 

592 if self.Command == 0x0001: 

593 return DceRpcSecVTBitmask 

594 elif self.Command == 0x0002: 

595 return DceRpcSecVTPcontext 

596 elif self.Command == 0x0003: 

597 return DceRpcSecVTHeader2 

598 return conf.raw_payload 

599 

600 

601# [MS-RPCE] sect 2.2.2.13.2 

602 

603 

604class DceRpcSecVTBitmask(Packet): 

605 name = "rpc_sec_vt_bitmask" 

606 fields_desc = [ 

607 LEIntField("bits", 1), 

608 ] 

609 

610 def default_payload_class(self, pkt): 

611 return conf.padding_layer 

612 

613 

614# [MS-RPCE] sect 2.2.2.13.4 

615 

616 

617class DceRpcSecVTPcontext(Packet): 

618 name = "rpc_sec_vt_pcontext" 

619 fields_desc = [ 

620 UUIDEnumField( 

621 "InterfaceId", 

622 None, 

623 ( 

624 DCE_RPC_INTERFACES_NAMES.get, 

625 lambda x: DCE_RPC_INTERFACES_NAMES_rev.get(x.lower()), 

626 ), 

627 uuid_fmt=UUIDField.FORMAT_LE, 

628 ), 

629 LEIntField("Version", 0), 

630 UUIDEnumField( 

631 "TransferSyntax", 

632 None, 

633 DCE_RPC_TRANSFER_SYNTAXES, 

634 uuid_fmt=UUIDField.FORMAT_LE, 

635 ), 

636 LEIntField("TransferVersion", 0), 

637 ] 

638 

639 def default_payload_class(self, pkt): 

640 return conf.padding_layer 

641 

642 

643# [MS-RPCE] sect 2.2.2.13.3 

644 

645 

646class DceRpcSecVTHeader2(Packet): 

647 name = "rpc_sec_vt_header2" 

648 fields_desc = [ 

649 ByteField("PTYPE", 0), 

650 ByteField("Reserved1", 0), 

651 LEShortField("Reserved2", 0), 

652 LEIntField("drep", 0), 

653 LEIntField("call_id", 0), 

654 LEShortField("p_cont_id", 0), 

655 LEShortField("opnum", 0), 

656 ] 

657 

658 def default_payload_class(self, pkt): 

659 return conf.padding_layer 

660 

661 

662class DceRpcSecVT(Packet): 

663 name = "Verification trailer" 

664 fields_desc = [ 

665 XStrFixedLenField("rpc_sec_verification_trailer", _SECTRAILER_MAGIC, length=8), 

666 PacketListField("commands", [], DceRpcSecVTCommand), 

667 ] 

668 

669 

670class _VerifTrailerField(PacketField): 

671 def getfield( 

672 self, 

673 pkt, 

674 s, 

675 ): 

676 if _SECTRAILER_MAGIC in s: 

677 # a bit ugly 

678 ind = s.index(_SECTRAILER_MAGIC) 

679 sectrailer_bytes, remain = bytes(s[:-ind]), bytes(s[-ind:]) 

680 vt_trailer = self.m2i(pkt, sectrailer_bytes) 

681 if not isinstance(vt_trailer.payload, NoPayload): 

682 # bad parse 

683 return s, None 

684 return remain, vt_trailer 

685 return s, None 

686 

687 

688# sect 12.6.3 

689 

690 

691_DCE_RPC_5_FLAGS = { 

692 0x01: "PFC_FIRST_FRAG", 

693 0x02: "PFC_LAST_FRAG", 

694 0x04: "PFC_PENDING_CANCEL", 

695 0x08: "PFC_RESERVED_1", 

696 0x10: "PFC_CONC_MPX", 

697 0x20: "PFC_DID_NOT_EXECUTE", 

698 0x40: "PFC_MAYBE", 

699 0x80: "PFC_OBJECT_UUID", 

700} 

701 

702# [MS-RPCE] sect 2.2.2.3 

703 

704_DCE_RPC_5_FLAGS_2 = _DCE_RPC_5_FLAGS.copy() 

705_DCE_RPC_5_FLAGS_2[0x04] = "PFC_SUPPORT_HEADER_SIGN" 

706 

707 

708_DCE_RPC_ERROR_CODES = { 

709 # Appendix N 

710 0x1C010001: "nca_s_comm_failure", 

711 0x1C010002: "nca_s_op_rng_error", 

712 0x1C010003: "nca_s_unk_if", 

713 0x1C010006: "nca_s_wrong_boot_time", 

714 0x1C010009: "nca_s_you_crashed", 

715 0x1C01000B: "nca_s_proto_error", 

716 0x1C010013: "nca_s_out_args_too_big", 

717 0x1C010014: "nca_s_server_too_busy", 

718 0x1C010015: "nca_s_fault_string_too_long", 

719 0x1C010017: "nca_s_unsupported_type", 

720 0x1C000001: "nca_s_fault_int_div_by_zero", 

721 0x1C000002: "nca_s_fault_addr_error", 

722 0x1C000003: "nca_s_fault_fp_div_zero", 

723 0x1C000004: "nca_s_fault_fp_underflow", 

724 0x1C000005: "nca_s_fault_fp_overflow", 

725 0x1C000006: "nca_s_fault_invalid_tag", 

726 0x1C000007: "nca_s_fault_invalid_bound", 

727 0x1C000008: "nca_s_rpc_version_mismatch", 

728 0x1C000009: "nca_s_unspec_reject", 

729 0x1C00000A: "nca_s_bad_actid", 

730 0x1C00000B: "nca_s_who_are_you_failed", 

731 0x1C00000C: "nca_s_manager_not_entered", 

732 0x1C00000D: "nca_s_fault_cancel", 

733 0x1C00000E: "nca_s_fault_ill_inst", 

734 0x1C00000F: "nca_s_fault_fp_error", 

735 0x1C000010: "nca_s_fault_int_overflow", 

736 0x1C000012: "nca_s_fault_unspec", 

737 0x1C000013: "nca_s_fault_remote_comm_failure", 

738 0x1C000014: "nca_s_fault_pipe_empty", 

739 0x1C000015: "nca_s_fault_pipe_closed", 

740 0x1C000016: "nca_s_fault_pipe_order", 

741 0x1C000017: "nca_s_fault_pipe_discipline", 

742 0x1C000018: "nca_s_fault_pipe_comm_error", 

743 0x1C000019: "nca_s_fault_pipe_memory", 

744 0x1C00001A: "nca_s_fault_context_mismatch", 

745 0x1C00001B: "nca_s_fault_remote_no_memory", 

746 0x1C00001C: "nca_s_invalid_pres_context_id", 

747 0x1C00001D: "nca_s_unsupported_authn_level", 

748 0x1C00001F: "nca_s_invalid_checksum", 

749 0x1C000020: "nca_s_invalid_crc", 

750 0x1C000021: "nca_s_fault_user_defined", 

751 0x1C000022: "nca_s_fault_tx_open_failed", 

752 0x1C000023: "nca_s_fault_codeset_conv_error", 

753 0x1C000024: "nca_s_fault_object_not_found", 

754 0x1C000025: "nca_s_fault_no_client_stub", 

755 # [MS-ERREF] 

756 0x000006D3: "RPC_S_UNKNOWN_AUTHN_SERVICE", 

757 0x000006F7: "RPC_X_BAD_STUB_DATA", 

758 # [MS-RPCE] 

759 0x000006D8: "EPT_S_CANT_PERFORM_OP", 

760} 

761 

762_DCE_RPC_REJECTION_REASONS = { 

763 0: "REASON_NOT_SPECIFIED", 

764 1: "TEMPORARY_CONGESTION", 

765 2: "LOCAL_LIMIT_EXCEEDED", 

766 3: "CALLED_PADDR_UNKNOWN", 

767 4: "PROTOCOL_VERSION_NOT_SUPPORTED", 

768 5: "DEFAULT_CONTEXT_NOT_SUPPORTED", 

769 6: "USER_DATA_NOT_READABLE", 

770 7: "NO_PSAP_AVAILABLE", 

771 8: "AUTHENTICATION_TYPE_NOT_RECOGNIZED", 

772 9: "INVALID_CHECKSUM", 

773} 

774 

775 

776class DceRpc5(DceRpc): 

777 """ 

778 DCE/RPC v5 'connection-oriented' packet 

779 """ 

780 

781 name = "DCE/RPC v5" 

782 fields_desc = ( 

783 [ 

784 ByteEnumField( 

785 "rpc_vers", 5, {4: "4 (connection-less)", 5: "5 (connection-oriented)"} 

786 ), 

787 ByteField("rpc_vers_minor", 0), 

788 ByteEnumField("ptype", 0, DCE_RPC_TYPE), 

789 MultipleTypeField( 

790 # [MS-RPCE] sect 2.2.2.3 

791 [ 

792 ( 

793 FlagsField("pfc_flags", 0x3, 8, _DCE_RPC_5_FLAGS_2), 

794 lambda pkt: pkt.ptype in [11, 12, 13, 14, 15, 16], 

795 ) 

796 ], 

797 FlagsField("pfc_flags", 0x3, 8, _DCE_RPC_5_FLAGS), 

798 ), 

799 ] 

800 + _drep 

801 + [ 

802 ByteField("reserved2", 0), 

803 _EField(ShortField("frag_len", None)), 

804 _EField( 

805 FieldLenField( 

806 "auth_len", 

807 None, 

808 fmt="H", 

809 length_of="auth_verifier", 

810 adjust=lambda pkt, x: 0 if not x else (x - 8), 

811 ) 

812 ), 

813 _EField(IntField("call_id", None)), 

814 # Now let's proceed with trailer fields, i.e. at the end of the PACKET 

815 # (below all payloads, etc.). Have a look at Figure 3 in sect 2.2.2.13 

816 # of [MS-RPCE] but note the following: 

817 # - auth_verifier includes sec_trailer + the authentication token 

818 # - auth_padding is the authentication padding 

819 # - vt_trailer is the verification trailer 

820 ConditionalField( 

821 TrailerField( 

822 PacketLenField( 

823 "auth_verifier", 

824 None, 

825 CommonAuthVerifier, 

826 length_from=lambda pkt: pkt.auth_len + 8, 

827 ) 

828 ), 

829 lambda pkt: pkt.auth_len != 0, 

830 ), 

831 ConditionalField( 

832 TrailerField( 

833 StrLenField( 

834 "auth_padding", 

835 None, 

836 length_from=lambda pkt: pkt.auth_verifier.auth_pad_length, 

837 ) 

838 ), 

839 lambda pkt: pkt.auth_len != 0, 

840 ), 

841 TrailerField( 

842 _VerifTrailerField("vt_trailer", None, DceRpcSecVT), 

843 ), 

844 ] 

845 ) 

846 

847 def do_dissect(self, s): 

848 # Overload do_dissect to only include the current layer in dissection. 

849 # This allows to support TrailerFields, even in the case where multiple DceRpc5 

850 # packets are concatenated 

851 frag_len = self.get_field("frag_len").getfield(self, s[8:10])[1] 

852 s, remain = s[:frag_len], s[frag_len:] 

853 return super(DceRpc5, self).do_dissect(s) + remain 

854 

855 def extract_padding(self, s): 

856 # Now, take any data that doesn't fit in the current fragment and make it 

857 # padding. The caller is responsible for looking for eventual padding and 

858 # creating the next fragment, etc. 

859 pay_len = self.frag_len - len(self.original) + len(s) 

860 return s[:pay_len], s[pay_len:] 

861 

862 def post_build(self, pkt, pay): 

863 if ( 

864 self.auth_verifier 

865 and self.auth_padding is None 

866 and self.auth_verifier.auth_pad_length is None 

867 ): 

868 # Compute auth_len and add padding 

869 auth_len = self.get_field("auth_len").getfield(self, pkt[10:12])[1] + 8 

870 auth_verifier, pay = pay[-auth_len:], pay[:-auth_len] 

871 pdu_len = len(pay) 

872 if self.payload: 

873 pdu_len -= len(self.payload.self_build()) 

874 padlen = (-pdu_len) % _COMMON_AUTH_PAD 

875 auth_verifier = ( 

876 auth_verifier[:2] + struct.pack("B", padlen) + auth_verifier[3:] 

877 ) 

878 pay = pay + (padlen * b"\x00") + auth_verifier 

879 if self.frag_len is None: 

880 # Compute frag_len 

881 length = len(pkt) + len(pay) 

882 pkt = ( 

883 pkt[:8] 

884 + self.get_field("frag_len").addfield(self, b"", length) 

885 + pkt[10:] 

886 ) 

887 return pkt + pay 

888 

889 def answers(self, pkt): 

890 return isinstance(pkt, DceRpc5) and pkt[DceRpc5].call_id == self.call_id 

891 

892 @classmethod 

893 def tcp_reassemble(cls, data, _, session): 

894 if data[0:1] != b"\x05": 

895 return 

896 endian = struct.unpack("!B", data[4:5])[0] >> 4 

897 if endian not in [0, 1]: 

898 return 

899 length = struct.unpack(("<" if endian else ">") + "H", data[8:10])[0] 

900 if len(data) >= length: 

901 if conf.dcerpc_session_enable: 

902 # If DCE/RPC sessions are enabled, use them ! 

903 if "dcerpcsess" not in session: 

904 session["dcerpcsess"] = dcerpcsess = DceRpcSession() 

905 else: 

906 dcerpcsess = session["dcerpcsess"] 

907 return dcerpcsess.process(DceRpc5(data)) 

908 return DceRpc5(data) 

909 

910 

911# sec 12.6.3.1 

912 

913 

914class DceRpc5AbstractSyntax(EPacket): 

915 name = "Presentation Syntax (p_syntax_id_t)" 

916 fields_desc = [ 

917 _EField( 

918 UUIDEnumField( 

919 "if_uuid", 

920 None, 

921 ( 

922 # Those are dynamic 

923 DCE_RPC_INTERFACES_NAMES.get, 

924 lambda x: DCE_RPC_INTERFACES_NAMES_rev.get(x.lower()), 

925 ), 

926 ) 

927 ), 

928 _EField(IntField("if_version", 3)), 

929 ] 

930 

931 

932class DceRpc5TransferSyntax(EPacket): 

933 name = "Presentation Transfer Syntax (p_syntax_id_t)" 

934 fields_desc = [ 

935 _EField( 

936 UUIDEnumField( 

937 "if_uuid", 

938 None, 

939 DCE_RPC_TRANSFER_SYNTAXES, 

940 ) 

941 ), 

942 _EField(IntField("if_version", 3)), 

943 ] 

944 

945 

946class DceRpc5Context(EPacket): 

947 name = "Presentation Context (p_cont_elem_t)" 

948 fields_desc = [ 

949 _EField(ShortField("cont_id", 0)), 

950 FieldLenField("n_transfer_syn", None, count_of="transfer_syntaxes", fmt="B"), 

951 ByteField("reserved", 0), 

952 EPacketField("abstract_syntax", None, DceRpc5AbstractSyntax), 

953 EPacketListField( 

954 "transfer_syntaxes", 

955 None, 

956 DceRpc5TransferSyntax, 

957 count_from=lambda pkt: pkt.n_transfer_syn, 

958 endianness_from=_dce_rpc_endianness, 

959 ), 

960 ] 

961 

962 

963class DceRpc5Result(EPacket): 

964 name = "Context negotiation Result" 

965 fields_desc = [ 

966 _EField( 

967 ShortEnumField( 

968 "result", 0, ["acceptance", "user_rejection", "provider_rejection"] 

969 ) 

970 ), 

971 _EField( 

972 ShortEnumField( 

973 "reason", 

974 0, 

975 _DCE_RPC_REJECTION_REASONS, 

976 ) 

977 ), 

978 EPacketField("transfer_syntax", None, DceRpc5TransferSyntax), 

979 ] 

980 

981 

982class DceRpc5PortAny(EPacket): 

983 name = "Port Any (port_any_t)" 

984 fields_desc = [ 

985 _EField(FieldLenField("length", None, length_of="port_spec", fmt="H")), 

986 _EField(StrLenField("port_spec", b"", length_from=lambda pkt: pkt.length)), 

987 ] 

988 

989 

990# sec 12.6.4.3 

991 

992 

993class DceRpc5Bind(_DceRpcPayload): 

994 name = "DCE/RPC v5 - Bind" 

995 fields_desc = [ 

996 _EField(ShortField("max_xmit_frag", 5840)), 

997 _EField(ShortField("max_recv_frag", 8192)), 

998 _EField(IntField("assoc_group_id", 0)), 

999 # p_cont_list_t 

1000 _EField( 

1001 FieldLenField("n_context_elem", None, count_of="context_elem", fmt="B") 

1002 ), 

1003 StrFixedLenField("reserved", 0, length=3), 

1004 EPacketListField( 

1005 "context_elem", 

1006 [], 

1007 DceRpc5Context, 

1008 endianness_from=_dce_rpc_endianness, 

1009 count_from=lambda pkt: pkt.n_context_elem, 

1010 ), 

1011 ] 

1012 

1013 

1014bind_layers(DceRpc5, DceRpc5Bind, ptype=11) 

1015 

1016# sec 12.6.4.4 

1017 

1018 

1019class DceRpc5BindAck(_DceRpcPayload): 

1020 name = "DCE/RPC v5 - Bind Ack" 

1021 fields_desc = [ 

1022 _EField(ShortField("max_xmit_frag", 5840)), 

1023 _EField(ShortField("max_recv_frag", 8192)), 

1024 _EField(IntField("assoc_group_id", 0)), 

1025 PadField( 

1026 EPacketField("sec_addr", None, DceRpc5PortAny), 

1027 align=4, 

1028 ), 

1029 # p_result_list_t 

1030 _EField(FieldLenField("n_results", None, count_of="results", fmt="B")), 

1031 StrFixedLenField("reserved", 0, length=3), 

1032 EPacketListField( 

1033 "results", 

1034 [], 

1035 DceRpc5Result, 

1036 endianness_from=_dce_rpc_endianness, 

1037 count_from=lambda pkt: pkt.n_results, 

1038 ), 

1039 ] 

1040 

1041 

1042bind_layers(DceRpc5, DceRpc5BindAck, ptype=12) 

1043 

1044# sec 12.6.4.5 

1045 

1046 

1047class DceRpc5Version(EPacket): 

1048 name = "version_t" 

1049 fields_desc = [ 

1050 ByteField("major", 0), 

1051 ByteField("minor", 0), 

1052 ] 

1053 

1054 

1055class DceRpc5BindNak(_DceRpcPayload): 

1056 name = "DCE/RPC v5 - Bind Nak" 

1057 fields_desc = [ 

1058 _EField( 

1059 ShortEnumField("provider_reject_reason", 0, _DCE_RPC_REJECTION_REASONS) 

1060 ), 

1061 # p_rt_versions_supported_t 

1062 _EField(FieldLenField("n_protocols", None, count_of="protocols", fmt="B")), 

1063 EPacketListField( 

1064 "protocols", 

1065 [], 

1066 DceRpc5Version, 

1067 count_from=lambda pkt: pkt.n_protocols, 

1068 endianness_from=_dce_rpc_endianness, 

1069 ), 

1070 # [MS-RPCE] sect 2.2.2.9 

1071 ConditionalField( 

1072 ReversePadField( 

1073 _EField( 

1074 UUIDEnumField( 

1075 "signature", 

1076 None, 

1077 { 

1078 UUID( 

1079 "90740320-fad0-11d3-82d7-009027b130ab" 

1080 ): "Extended Error", 

1081 }, 

1082 ) 

1083 ), 

1084 align=8, 

1085 ), 

1086 lambda pkt: pkt.fields.get("signature", None) 

1087 or ( 

1088 pkt.underlayer 

1089 and pkt.underlayer.frag_len >= 24 + pkt.n_protocols * 2 + 16 

1090 ), 

1091 ), 

1092 ] 

1093 

1094 

1095bind_layers(DceRpc5, DceRpc5BindNak, ptype=13) 

1096 

1097 

1098# sec 12.6.4.1 

1099 

1100 

1101class DceRpc5AlterContext(_DceRpcPayload): 

1102 name = "DCE/RPC v5 - AlterContext" 

1103 fields_desc = DceRpc5Bind.fields_desc 

1104 

1105 

1106bind_layers(DceRpc5, DceRpc5AlterContext, ptype=14) 

1107 

1108 

1109# sec 12.6.4.2 

1110 

1111 

1112class DceRpc5AlterContextResp(_DceRpcPayload): 

1113 name = "DCE/RPC v5 - AlterContextResp" 

1114 fields_desc = DceRpc5BindAck.fields_desc 

1115 

1116 

1117bind_layers(DceRpc5, DceRpc5AlterContextResp, ptype=15) 

1118 

1119# [MS-RPCE] sect 2.2.2.10 - rpc_auth_3 

1120 

1121 

1122class DceRpc5Auth3(Packet): 

1123 name = "DCE/RPC v5 - Auth3" 

1124 fields_desc = [StrFixedLenField("pad", b"", length=4)] 

1125 

1126 

1127bind_layers(DceRpc5, DceRpc5Auth3, ptype=16) 

1128 

1129# sec 12.6.4.7 

1130 

1131 

1132class DceRpc5Fault(_DceRpcPayload): 

1133 name = "DCE/RPC v5 - Fault" 

1134 fields_desc = [ 

1135 _EField(IntField("alloc_hint", 0)), 

1136 _EField(ShortField("cont_id", 0)), 

1137 ByteField("cancel_count", 0), 

1138 FlagsField("reserved", 0, -8, {0x1: "RPC extended error"}), 

1139 _EField(LEIntEnumField("status", 0, _DCE_RPC_ERROR_CODES)), 

1140 IntField("reserved2", 0), 

1141 ] 

1142 

1143 

1144bind_layers(DceRpc5, DceRpc5Fault, ptype=3) 

1145 

1146 

1147# sec 12.6.4.9 

1148 

1149 

1150class DceRpc5Request(_DceRpcPayload): 

1151 name = "DCE/RPC v5 - Request" 

1152 fields_desc = [ 

1153 _EField(IntField("alloc_hint", 0)), 

1154 _EField(ShortField("cont_id", 0)), 

1155 _EField(ShortField("opnum", 0)), 

1156 ConditionalField( 

1157 PadField( 

1158 _EField(UUIDField("object", None)), 

1159 align=8, 

1160 ), 

1161 lambda pkt: pkt.underlayer and pkt.underlayer.pfc_flags.PFC_OBJECT_UUID, 

1162 ), 

1163 ] 

1164 

1165 

1166bind_layers(DceRpc5, DceRpc5Request, ptype=0) 

1167 

1168# sec 12.6.4.10 

1169 

1170 

1171class DceRpc5Response(_DceRpcPayload): 

1172 name = "DCE/RPC v5 - Response" 

1173 fields_desc = [ 

1174 _EField(IntField("alloc_hint", 0)), 

1175 _EField(ShortField("cont_id", 0)), 

1176 ByteField("cancel_count", 0), 

1177 ByteField("reserved", 0), 

1178 ] 

1179 

1180 

1181bind_layers(DceRpc5, DceRpc5Response, ptype=2) 

1182 

1183# --- API 

1184 

1185DceRpcOp = collections.namedtuple("DceRpcOp", ["request", "response"]) 

1186DCE_RPC_INTERFACES = {} 

1187 

1188 

1189class DceRpcInterface: 

1190 def __init__(self, name, uuid, version_tuple, if_version, opnums): 

1191 self.name = name 

1192 self.uuid = uuid 

1193 self.major_version, self.minor_version = version_tuple 

1194 self.if_version = if_version 

1195 self.opnums = opnums 

1196 

1197 def __repr__(self): 

1198 return "<DCE/RPC Interface %s v%s.%s>" % ( 

1199 self.name, 

1200 self.major_version, 

1201 self.minor_version, 

1202 ) 

1203 

1204 

1205def register_dcerpc_interface(name, uuid, version, opnums): 

1206 """ 

1207 Register a DCE/RPC interface 

1208 """ 

1209 version_tuple = tuple(map(int, version.split("."))) 

1210 assert len(version_tuple) == 2, "Version should be in format 'X.X' !" 

1211 if_version = (version_tuple[1] << 16) + version_tuple[0] 

1212 if (uuid, if_version) in DCE_RPC_INTERFACES: 

1213 # Interface is already registered. 

1214 interface = DCE_RPC_INTERFACES[(uuid, if_version)] 

1215 if interface.name == name: 

1216 if set(opnums) - set(interface.opnums): 

1217 # Interface is an extension of a previous interface 

1218 interface.opnums.update(opnums) 

1219 else: 

1220 log_runtime.warning( 

1221 "This interface is already registered: %s. Skip" % interface 

1222 ) 

1223 return 

1224 else: 

1225 raise ValueError( 

1226 "An interface with the same UUID is already registered: %s" % interface 

1227 ) 

1228 else: 

1229 # New interface 

1230 DCE_RPC_INTERFACES_NAMES[uuid] = name 

1231 DCE_RPC_INTERFACES_NAMES_rev[name.lower()] = uuid 

1232 DCE_RPC_INTERFACES[(uuid, if_version)] = DceRpcInterface( 

1233 name, 

1234 uuid, 

1235 version_tuple, 

1236 if_version, 

1237 opnums, 

1238 ) 

1239 # bind for build 

1240 for opnum, operations in opnums.items(): 

1241 bind_top_down(DceRpc5Request, operations.request, opnum=opnum) 

1242 

1243 

1244def find_dcerpc_interface(name): 

1245 """ 

1246 Find an interface object through the name in the IDL 

1247 """ 

1248 try: 

1249 return next(x for x in DCE_RPC_INTERFACES.values() if x.name == name) 

1250 except StopIteration: 

1251 raise AttributeError("Unknown interface !") 

1252 

1253 

1254COM_INTERFACES = {} 

1255 

1256 

1257class ComInterface: 

1258 def __init__(self, name, uuid, opnums): 

1259 self.name = name 

1260 self.uuid = uuid 

1261 self.opnums = opnums 

1262 

1263 def __repr__(self): 

1264 return "<COM Interface %s>" % (self.name,) 

1265 

1266 

1267def register_com_interface(name, uuid, opnums): 

1268 """ 

1269 Register a COM interface 

1270 """ 

1271 COM_INTERFACES[uuid] = ComInterface( 

1272 name, 

1273 uuid, 

1274 opnums, 

1275 ) 

1276 

1277 

1278# --- NDR fields - [C706] chap 14 

1279 

1280 

1281def _set_ctx_on(f, obj): 

1282 if isinstance(f, _NDRPacket): 

1283 f.ndr64 = obj.ndr64 

1284 f.ndrendian = obj.ndrendian 

1285 if isinstance(f, list): 

1286 for x in f: 

1287 if isinstance(x, _NDRPacket): 

1288 x.ndr64 = obj.ndr64 

1289 x.ndrendian = obj.ndrendian 

1290 

1291 

1292def _e(ndrendian): 

1293 return {"big": ">", "little": "<"}[ndrendian] 

1294 

1295 

1296class _NDRPacket(Packet): 

1297 __slots__ = ["ndr64", "ndrendian", "deferred_pointers", "request_packet"] 

1298 

1299 def __init__(self, *args, **kwargs): 

1300 self.ndr64 = kwargs.pop("ndr64", False) 

1301 self.ndrendian = kwargs.pop("ndrendian", "little") 

1302 # request_packet is used in the session, so that a response packet 

1303 # can resolve union arms if the case parameter is in the request. 

1304 self.request_packet = kwargs.pop("request_packet", None) 

1305 self.deferred_pointers = [] 

1306 super(_NDRPacket, self).__init__(*args, **kwargs) 

1307 

1308 def do_dissect(self, s): 

1309 _up = self.parent or self.underlayer 

1310 if _up and isinstance(_up, _NDRPacket): 

1311 self.ndr64 = _up.ndr64 

1312 self.ndrendian = _up.ndrendian 

1313 else: 

1314 # See comment above NDRConstructedType 

1315 return NDRConstructedType([]).read_deferred_pointers( 

1316 self, super(_NDRPacket, self).do_dissect(s) 

1317 ) 

1318 return super(_NDRPacket, self).do_dissect(s) 

1319 

1320 def post_dissect(self, s): 

1321 if self.deferred_pointers: 

1322 # Can't trust the cache if there were deferred pointers 

1323 self.raw_packet_cache = None 

1324 return s 

1325 

1326 def do_build(self): 

1327 _up = self.parent or self.underlayer 

1328 for f in self.fields.values(): 

1329 _set_ctx_on(f, self) 

1330 if not _up or not isinstance(_up, _NDRPacket): 

1331 # See comment above NDRConstructedType 

1332 return NDRConstructedType([]).add_deferred_pointers( 

1333 self, super(_NDRPacket, self).do_build() 

1334 ) 

1335 return super(_NDRPacket, self).do_build() 

1336 

1337 def default_payload_class(self, pkt): 

1338 return conf.padding_layer 

1339 

1340 def clone_with(self, *args, **kwargs): 

1341 pkt = super(_NDRPacket, self).clone_with(*args, **kwargs) 

1342 # We need to copy deferred_pointers to not break pointer deferral 

1343 # on build. 

1344 pkt.deferred_pointers = self.deferred_pointers 

1345 pkt.ndr64 = self.ndr64 

1346 pkt.ndrendian = self.ndrendian 

1347 return pkt 

1348 

1349 def copy(self): 

1350 pkt = super(_NDRPacket, self).copy() 

1351 pkt.deferred_pointers = self.deferred_pointers 

1352 pkt.ndr64 = self.ndr64 

1353 pkt.ndrendian = self.ndrendian 

1354 return pkt 

1355 

1356 def show2(self, dump=False, indent=3, lvl="", label_lvl=""): 

1357 return self.__class__( 

1358 bytes(self), ndr64=self.ndr64, ndrendian=self.ndrendian 

1359 ).show(dump, indent, lvl, label_lvl) 

1360 

1361 def getfield_and_val(self, attr): 

1362 try: 

1363 return Packet.getfield_and_val(self, attr) 

1364 except ValueError: 

1365 if self.request_packet: 

1366 # Try to resolve the field from the request on failure 

1367 try: 

1368 return self.request_packet.getfield_and_val(attr) 

1369 except AttributeError: 

1370 pass 

1371 raise 

1372 

1373 def valueof(self, request): 

1374 """ 

1375 Util to get the value of a NDRField, ignoring arrays, pointers, etc. 

1376 """ 

1377 val = self 

1378 for ndr_field in request.split("."): 

1379 fld, fval = val.getfield_and_val(ndr_field) 

1380 val = fld.valueof(val, fval) 

1381 return val 

1382 

1383 

1384class _NDRAlign: 

1385 def padlen(self, flen, pkt): 

1386 return -flen % self._align[pkt.ndr64] 

1387 

1388 def original_length(self, pkt): 

1389 # Find the length of the NDR frag to be able to pad properly 

1390 while pkt: 

1391 par = pkt.parent or pkt.underlayer 

1392 if par and isinstance(par, _NDRPacket): 

1393 pkt = par 

1394 else: 

1395 break 

1396 return len(pkt.original) 

1397 

1398 

1399class NDRAlign(_NDRAlign, ReversePadField): 

1400 """ 

1401 ReversePadField modified to fit NDR. 

1402 

1403 - If no align size is specified, use the one from the inner field 

1404 - Size is calculated from the beginning of the NDR stream 

1405 """ 

1406 

1407 def __init__(self, fld, align, padwith=None): 

1408 super(NDRAlign, self).__init__(fld, align=align, padwith=padwith) 

1409 

1410 

1411class _VirtualField(Field): 

1412 # Hold a value but doesn't show up when building/dissecting 

1413 def addfield(self, pkt, s, x): 

1414 return s 

1415 

1416 def getfield(self, pkt, s): 

1417 return s, None 

1418 

1419 

1420class _NDRPacketMetaclass(Packet_metaclass): 

1421 def __new__(cls, name, bases, dct): 

1422 newcls = super(_NDRPacketMetaclass, cls).__new__(cls, name, bases, dct) 

1423 conformants = dct.get("DEPORTED_CONFORMANTS", []) 

1424 if conformants: 

1425 amount = len(conformants) 

1426 if amount == 1: 

1427 newcls.fields_desc.insert( 

1428 0, 

1429 _VirtualField("max_count", None), 

1430 ) 

1431 else: 

1432 newcls.fields_desc.insert( 

1433 0, 

1434 FieldListField( 

1435 "max_counts", 

1436 [], 

1437 _VirtualField("", 0), 

1438 count_from=lambda _: amount, 

1439 ), 

1440 ) 

1441 return newcls # type: ignore 

1442 

1443 

1444class NDRPacket(_NDRPacket, metaclass=_NDRPacketMetaclass): 

1445 """ 

1446 A NDR Packet. Handles pointer size & endianness 

1447 """ 

1448 

1449 __slots__ = ["_align"] 

1450 

1451 # NDR64 pad structures 

1452 # [MS-RPCE] 2.2.5.3.4.1 

1453 ALIGNMENT = (1, 1) 

1454 # [C706] sect 14.3.7 - Conformants max_count can be added to the beginning 

1455 DEPORTED_CONFORMANTS = [] 

1456 

1457 

1458# Primitive types 

1459 

1460 

1461class _NDRValueOf: 

1462 def valueof(self, pkt, x): 

1463 return x 

1464 

1465 

1466class _NDRLenField(_NDRValueOf, Field): 

1467 """ 

1468 Field similar to FieldLenField that takes size_of and adjust as arguments, 

1469 and take the value of a size on build. 

1470 """ 

1471 

1472 __slots__ = ["size_of", "adjust"] 

1473 

1474 def __init__(self, *args, **kwargs): 

1475 self.size_of = kwargs.pop("size_of", None) 

1476 self.adjust = kwargs.pop("adjust", lambda _, x: x) 

1477 super(_NDRLenField, self).__init__(*args, **kwargs) 

1478 

1479 def i2m(self, pkt, x): 

1480 if x is None and pkt is not None and self.size_of is not None: 

1481 fld, fval = pkt.getfield_and_val(self.size_of) 

1482 f = fld.i2len(pkt, fval) 

1483 x = self.adjust(pkt, f) 

1484 elif x is None: 

1485 x = 0 

1486 return x 

1487 

1488 

1489class NDRByteField(_NDRLenField, ByteField): 

1490 pass 

1491 

1492 

1493class NDRSignedByteField(_NDRLenField, SignedByteField): 

1494 pass 

1495 

1496 

1497class _NDRField(_NDRLenField): 

1498 FMT = "" 

1499 ALIGN = (0, 0) 

1500 

1501 def getfield(self, pkt, s): 

1502 return NDRAlign( 

1503 Field("", 0, fmt=_e(pkt.ndrendian) + self.FMT), align=self.ALIGN 

1504 ).getfield(pkt, s) 

1505 

1506 def addfield(self, pkt, s, val): 

1507 return NDRAlign( 

1508 Field("", 0, fmt=_e(pkt.ndrendian) + self.FMT), align=self.ALIGN 

1509 ).addfield(pkt, s, self.i2m(pkt, val)) 

1510 

1511 

1512class NDRShortField(_NDRField): 

1513 FMT = "H" 

1514 ALIGN = (2, 2) 

1515 

1516 

1517class NDRSignedShortField(_NDRField): 

1518 FMT = "h" 

1519 ALIGN = (2, 2) 

1520 

1521 

1522class NDRIntField(_NDRField): 

1523 FMT = "I" 

1524 ALIGN = (4, 4) 

1525 

1526 

1527class NDRSignedIntField(_NDRField): 

1528 FMT = "i" 

1529 ALIGN = (4, 4) 

1530 

1531 

1532class NDRLongField(_NDRField): 

1533 FMT = "Q" 

1534 ALIGN = (8, 8) 

1535 

1536 

1537class NDRSignedLongField(_NDRField): 

1538 FMT = "q" 

1539 ALIGN = (8, 8) 

1540 

1541 

1542class NDRIEEEFloatField(_NDRField): 

1543 FMT = "f" 

1544 ALIGN = (4, 4) 

1545 

1546 

1547class NDRIEEEDoubleField(_NDRField): 

1548 FMT = "d" 

1549 ALIGN = (8, 8) 

1550 

1551 

1552# Enum types 

1553 

1554 

1555class _NDREnumField(_NDRValueOf, EnumField): 

1556 # [MS-RPCE] sect 2.2.5.2 - Enums are 4 octets in NDR64 

1557 FMTS = ["H", "I"] 

1558 

1559 def getfield(self, pkt, s): 

1560 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64] 

1561 return NDRAlign(Field("", 0, fmt=fmt), align=(2, 4)).getfield(pkt, s) 

1562 

1563 def addfield(self, pkt, s, val): 

1564 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64] 

1565 return NDRAlign(Field("", 0, fmt=fmt), align=(2, 4)).addfield( 

1566 pkt, s, self.i2m(pkt, val) 

1567 ) 

1568 

1569 

1570class NDRInt3264EnumField(NDRAlign): 

1571 def __init__(self, *args, **kwargs): 

1572 super(NDRInt3264EnumField, self).__init__( 

1573 _NDREnumField(*args, **kwargs), align=(2, 4) 

1574 ) 

1575 

1576 

1577class NDRIntEnumField(_NDRValueOf, NDRAlign): 

1578 # v1_enum are always 4-octets, even in NDR32 

1579 def __init__(self, *args, **kwargs): 

1580 super(NDRIntEnumField, self).__init__( 

1581 LEIntEnumField(*args, **kwargs), align=(4, 4) 

1582 ) 

1583 

1584 

1585# Special types 

1586 

1587 

1588class NDRInt3264Field(_NDRLenField): 

1589 FMTS = ["I", "Q"] 

1590 

1591 def getfield(self, pkt, s): 

1592 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64] 

1593 return NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(pkt, s) 

1594 

1595 def addfield(self, pkt, s, val): 

1596 fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64] 

1597 return NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield( 

1598 pkt, s, self.i2m(pkt, val) 

1599 ) 

1600 

1601 

1602class NDRSignedInt3264Field(NDRInt3264Field): 

1603 FMTS = ["i", "q"] 

1604 

1605 

1606# Pointer types 

1607 

1608 

1609class NDRPointer(_NDRPacket): 

1610 fields_desc = [ 

1611 MultipleTypeField( 

1612 [(XLELongField("referent_id", 1), lambda pkt: pkt and pkt.ndr64)], 

1613 XLEIntField("referent_id", 1), 

1614 ), 

1615 PacketField("value", None, conf.raw_layer), 

1616 ] 

1617 

1618 

1619class NDRFullPointerField(_FieldContainer): 

1620 """ 

1621 A NDR Full/Unique pointer field encapsulation. 

1622 

1623 :param EMBEDDED: This pointer is embedded. This means that it's representation 

1624 will not appear after the pointer (pointer deferral applies). 

1625 See [C706] 14.3.12.3 - Algorithm for Deferral of Referents 

1626 """ 

1627 

1628 EMBEDDED = False 

1629 EMBEDDED_REF = False 

1630 

1631 def __init__(self, fld, ref=False, fmt="I"): 

1632 self.fld = fld 

1633 self.ref = ref 

1634 self.default = None 

1635 

1636 def getfield(self, pkt, s): 

1637 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64] 

1638 remain, referent_id = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield( 

1639 pkt, s 

1640 ) 

1641 

1642 # No value 

1643 if referent_id == 0 and not self.EMBEDDED_REF: 

1644 return remain, None 

1645 

1646 # With value 

1647 if self.EMBEDDED: 

1648 # deferred 

1649 ptr = NDRPointer( 

1650 ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, referent_id=referent_id 

1651 ) 

1652 pkt.deferred_pointers.append((ptr, partial(self.fld.getfield, pkt))) 

1653 return remain, ptr 

1654 

1655 remain, val = self.fld.getfield(pkt, remain) 

1656 return remain, NDRPointer( 

1657 ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, referent_id=referent_id, value=val 

1658 ) 

1659 

1660 def addfield(self, pkt, s, val): 

1661 if val is not None and not isinstance(val, NDRPointer): 

1662 raise ValueError( 

1663 "Expected NDRPointer in %s. You are using it wrong!" % self.name 

1664 ) 

1665 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64] 

1666 fld = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)) 

1667 

1668 # No value 

1669 if val is None and not self.EMBEDDED_REF: 

1670 return fld.addfield(pkt, s, 0) 

1671 

1672 # With value 

1673 _set_ctx_on(val.value, pkt) 

1674 s = fld.addfield(pkt, s, val.referent_id) 

1675 if self.EMBEDDED: 

1676 # deferred 

1677 pkt.deferred_pointers.append( 

1678 ((lambda s: self.fld.addfield(pkt, s, val.value)), val) 

1679 ) 

1680 return s 

1681 

1682 return self.fld.addfield(pkt, s, val.value) 

1683 

1684 def any2i(self, pkt, x): 

1685 # User-friendly helper 

1686 if x is not None and not isinstance(x, NDRPointer): 

1687 return NDRPointer( 

1688 referent_id=0x20000, 

1689 value=self.fld.any2i(pkt, x), 

1690 ) 

1691 return x 

1692 

1693 # Can't use i2repr = Field.i2repr and so on on PY2 :/ 

1694 def i2repr(self, pkt, val): 

1695 return repr(val) 

1696 

1697 def i2h(self, pkt, x): 

1698 return x 

1699 

1700 def h2i(self, pkt, x): 

1701 return x 

1702 

1703 def i2len(self, pkt, x): 

1704 if x is None: 

1705 return 0 

1706 return self.fld.i2len(pkt, x.value) 

1707 

1708 def valueof(self, pkt, x): 

1709 if x is None: 

1710 return x 

1711 return self.fld.valueof(pkt, x.value) 

1712 

1713 

1714class NDRFullEmbPointerField(NDRFullPointerField): 

1715 """ 

1716 A NDR Embedded Full pointer. 

1717 

1718 Same as NDRFullPointerField with EMBEDDED = True. 

1719 """ 

1720 

1721 EMBEDDED = True 

1722 

1723 

1724class NDRRefEmbPointerField(NDRFullPointerField): 

1725 """ 

1726 A NDR Embedded Reference pointer. 

1727 

1728 Same as NDRFullPointerField with EMBEDDED = True and EMBEDDED_REF = True. 

1729 """ 

1730 

1731 EMBEDDED = True 

1732 EMBEDDED_REF = True 

1733 

1734 

1735# Constructed types 

1736 

1737 

1738# Note: this is utterly complex and will drive you crazy 

1739 

1740# If you have a NDRPacket that contains a deferred pointer on the top level 

1741# (only happens in non DCE/RPC structures, such as in MS-PAC, where you have an NDR 

1742# structure encapsulated in a non-NDR structure), there will be left-over deferred 

1743# pointers when exiting dissection/build (deferred pointers are only computed when 

1744# reaching a field that extends NDRConstructedType, which is normal: if you follow 

1745# the DCE/RPC spec, pointers are never deferred in root structures) 

1746# Therefore there is a special case forcing the build/dissection of any leftover 

1747# pointers in NDRPacket, if Scapy detects that they won't be handled by any parent. 

1748 

1749# Implementation notes: I chose to set 'handles_deferred' inside the FIELD, rather 

1750# than inside the PACKET. This is faster to compute because whether a constructed type 

1751# should handle deferral or not is computed only once when loading, therefore Scapy 

1752# knows in advance whether to handle deferred pointers or not. But it is technically 

1753# incorrect: with this approach, a structure (packet) cannot be used in 2 code paths 

1754# that have different pointer managements. I mean by that that if there was a 

1755# structure that was directly embedded in a RPC request without a pointer but also 

1756# embedded with a pointer in another RPC request, it would break. 

1757# Fortunately this isn't the case: structures are never reused for 2 purposes. 

1758# (or at least I never seen that... <i hope this works>) 

1759 

1760 

1761class NDRConstructedType(object): 

1762 def __init__(self, fields): 

1763 self.handles_deferred = False 

1764 self.ndr_fields = fields 

1765 self.rec_check_deferral() 

1766 

1767 def rec_check_deferral(self): 

1768 # We iterate through the fields within this constructed type. 

1769 # If we have a pointer, mark this field as handling deferrance 

1770 # and make all sub-constructed types not. 

1771 for f in self.ndr_fields: 

1772 if isinstance(f, NDRFullPointerField) and f.EMBEDDED: 

1773 self.handles_deferred = True 

1774 if isinstance(f, NDRConstructedType): 

1775 f.rec_check_deferral() 

1776 if f.handles_deferred: 

1777 self.handles_deferred = True 

1778 f.handles_deferred = False 

1779 

1780 def getfield(self, pkt, s): 

1781 s, fval = super(NDRConstructedType, self).getfield(pkt, s) 

1782 if isinstance(fval, _NDRPacket): 

1783 # If a sub-packet we just dissected has deferred pointers, 

1784 # pass it to parent packet to propagate. 

1785 pkt.deferred_pointers.extend(fval.deferred_pointers) 

1786 del fval.deferred_pointers[:] 

1787 if self.handles_deferred: 

1788 # This field handles deferral ! 

1789 s = self.read_deferred_pointers(pkt, s) 

1790 return s, fval 

1791 

1792 def read_deferred_pointers(self, pkt, s): 

1793 # Now read content of the pointers that were deferred 

1794 q = collections.deque() 

1795 q.extend(pkt.deferred_pointers) 

1796 del pkt.deferred_pointers[:] 

1797 while q: 

1798 # Recursively resolve pointers that were deferred 

1799 ptr, getfld = q.popleft() 

1800 s, val = getfld(s) 

1801 ptr.value = val 

1802 if isinstance(val, _NDRPacket): 

1803 # Pointer resolves to a packet.. that may have deferred pointers? 

1804 q.extend(val.deferred_pointers) 

1805 del val.deferred_pointers[:] 

1806 return s 

1807 

1808 def addfield(self, pkt, s, val): 

1809 s = super(NDRConstructedType, self).addfield(pkt, s, val) 

1810 if isinstance(val, _NDRPacket): 

1811 # If a sub-packet we just dissected has deferred pointers, 

1812 # pass it to parent packet to propagate. 

1813 pkt.deferred_pointers.extend(val.deferred_pointers) 

1814 del val.deferred_pointers[:] 

1815 if self.handles_deferred: 

1816 # This field handles deferral ! 

1817 s = self.add_deferred_pointers(pkt, s) 

1818 return s 

1819 

1820 def add_deferred_pointers(self, pkt, s): 

1821 # Now add content of pointers that were deferred 

1822 q = collections.deque() 

1823 q.extend(pkt.deferred_pointers) 

1824 del pkt.deferred_pointers[:] 

1825 while q: 

1826 addfld, fval = q.popleft() 

1827 s = addfld(s) 

1828 if isinstance(fval, NDRPointer) and isinstance(fval.value, _NDRPacket): 

1829 q.extend(fval.value.deferred_pointers) 

1830 del fval.value.deferred_pointers[:] 

1831 return s 

1832 

1833 

1834class _NDRPacketField(_NDRValueOf, PacketField): 

1835 def m2i(self, pkt, m): 

1836 return self.cls(m, ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, _parent=pkt) 

1837 

1838 

1839class _NDRPacketPadField(PadField): 

1840 # [MS-RPCE] 2.2.5.3.4.1 Structure with Trailing Gap 

1841 # Structures have extra alignment/padding in NDR64. 

1842 def padlen(self, flen, pkt): 

1843 if pkt.ndr64: 

1844 return -flen % self._align[1] 

1845 else: 

1846 return 0 

1847 

1848 

1849class NDRPacketField(NDRConstructedType, NDRAlign): 

1850 def __init__(self, name, default, pkt_cls, **kwargs): 

1851 self.DEPORTED_CONFORMANTS = pkt_cls.DEPORTED_CONFORMANTS 

1852 self.fld = _NDRPacketField(name, default, pkt_cls=pkt_cls, **kwargs) 

1853 NDRAlign.__init__( 

1854 self, 

1855 _NDRPacketPadField(self.fld, align=pkt_cls.ALIGNMENT), 

1856 align=pkt_cls.ALIGNMENT, 

1857 ) 

1858 NDRConstructedType.__init__(self, pkt_cls.fields_desc) 

1859 

1860 def getfield(self, pkt, x): 

1861 # Handle deformed conformants max_count here 

1862 if self.DEPORTED_CONFORMANTS: 

1863 # C706 14.3.2: "In other words, the size information precedes the 

1864 # structure and is aligned independently of the structure alignment." 

1865 fld = NDRInt3264Field("", 0) 

1866 max_counts = [] 

1867 for _ in self.DEPORTED_CONFORMANTS: 

1868 x, max_count = fld.getfield(pkt, x) 

1869 max_counts.append(max_count) 

1870 res, val = super(NDRPacketField, self).getfield(pkt, x) 

1871 if len(max_counts) == 1: 

1872 val.max_count = max_counts[0] 

1873 else: 

1874 val.max_counts = max_counts 

1875 return res, val 

1876 return super(NDRPacketField, self).getfield(pkt, x) 

1877 

1878 def addfield(self, pkt, s, x): 

1879 # Handle deformed conformants max_count here 

1880 if self.DEPORTED_CONFORMANTS: 

1881 mcfld = NDRInt3264Field("", 0) 

1882 if len(self.DEPORTED_CONFORMANTS) == 1: 

1883 max_counts = [x.max_count] 

1884 else: 

1885 max_counts = x.max_counts 

1886 for fldname, max_count in zip(self.DEPORTED_CONFORMANTS, max_counts): 

1887 if max_count is None: 

1888 fld, val = x.getfield_and_val(fldname) 

1889 max_count = fld.i2len(x, val) 

1890 s = mcfld.addfield(pkt, s, max_count) 

1891 return super(NDRPacketField, self).addfield(pkt, s, x) 

1892 return super(NDRPacketField, self).addfield(pkt, s, x) 

1893 

1894 

1895# Array types 

1896 

1897 

1898class _NDRPacketListField(NDRConstructedType, PacketListField): 

1899 """ 

1900 A PacketListField for NDR that can optionally pack the packets into NDRPointers 

1901 """ 

1902 

1903 islist = 1 

1904 holds_packets = 1 

1905 

1906 __slots__ = ["ptr_pack", "fld"] 

1907 

1908 def __init__(self, name, default, pkt_cls, **kwargs): 

1909 self.ptr_pack = kwargs.pop("ptr_pack", False) 

1910 if self.ptr_pack: 

1911 self.fld = NDRFullEmbPointerField(NDRPacketField("", None, pkt_cls)) 

1912 else: 

1913 self.fld = NDRPacketField("", None, pkt_cls) 

1914 PacketListField.__init__(self, name, default, pkt_cls=pkt_cls, **kwargs) 

1915 NDRConstructedType.__init__(self, [self.fld]) 

1916 

1917 def m2i(self, pkt, s): 

1918 remain, val = self.fld.getfield(pkt, s) 

1919 # A mistake here would be to use / instead of add_payload. It adds a copy 

1920 # which breaks pointer defferal. Same applies elsewhere 

1921 val.add_payload(conf.padding_layer(remain)) 

1922 return val 

1923 

1924 def any2i(self, pkt, x): 

1925 # User-friendly helper 

1926 if isinstance(x, list): 

1927 x = [self.fld.any2i(pkt, y) for y in x] 

1928 return super(_NDRPacketListField, self).any2i(pkt, x) 

1929 

1930 def i2m(self, pkt, val): 

1931 return self.fld.addfield(pkt, b"", val) 

1932 

1933 def i2len(self, pkt, x): 

1934 return len(x) 

1935 

1936 def valueof(self, pkt, x): 

1937 return [self.fld.valueof(pkt, y) for y in x] 

1938 

1939 

1940class NDRFieldListField(NDRConstructedType, FieldListField): 

1941 """ 

1942 A FieldListField for NDR 

1943 """ 

1944 

1945 islist = 1 

1946 

1947 def __init__(self, *args, **kwargs): 

1948 kwargs.pop("ptr_pack", None) # TODO: unimplemented 

1949 if "length_is" in kwargs: 

1950 kwargs["count_from"] = kwargs.pop("length_is") 

1951 elif "size_is" in kwargs: 

1952 kwargs["count_from"] = kwargs.pop("size_is") 

1953 FieldListField.__init__(self, *args, **kwargs) 

1954 NDRConstructedType.__init__(self, [self.field]) 

1955 

1956 def i2len(self, pkt, x): 

1957 return len(x) 

1958 

1959 def valueof(self, pkt, x): 

1960 return [self.field.valueof(pkt, y) for y in x] 

1961 

1962 

1963class NDRVaryingArray(_NDRPacket): 

1964 fields_desc = [ 

1965 MultipleTypeField( 

1966 [(LELongField("offset", 0), lambda pkt: pkt and pkt.ndr64)], 

1967 LEIntField("offset", 0), 

1968 ), 

1969 MultipleTypeField( 

1970 [ 

1971 ( 

1972 LELongField("actual_count", None), 

1973 lambda pkt: pkt and pkt.ndr64, 

1974 ) 

1975 ], 

1976 LEIntField("actual_count", None), 

1977 ), 

1978 PacketField("value", None, conf.raw_layer), 

1979 ] 

1980 

1981 

1982class _NDRVarField: 

1983 """ 

1984 NDR Varying Array / String field 

1985 """ 

1986 

1987 LENGTH_FROM = False 

1988 COUNT_FROM = False 

1989 

1990 def __init__(self, *args, **kwargs): 

1991 # We build the length_is function by taking into account both the 

1992 # actual_count (from the varying field) and a potentially provided 

1993 # length_is field. 

1994 if "length_is" in kwargs: 

1995 _length_is = kwargs.pop("length_is") 

1996 length_is = lambda pkt: (_length_is(pkt.underlayer) or pkt.actual_count) 

1997 else: 

1998 length_is = lambda pkt: pkt.actual_count 

1999 # Pass it to the sub-field (actually subclass) 

2000 if self.LENGTH_FROM: 

2001 kwargs["length_from"] = length_is 

2002 elif self.COUNT_FROM: 

2003 kwargs["count_from"] = length_is 

2004 super(_NDRVarField, self).__init__(*args, **kwargs) 

2005 

2006 def getfield(self, pkt, s): 

2007 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64] 

2008 remain, offset = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(pkt, s) 

2009 remain, actual_count = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield( 

2010 pkt, remain 

2011 ) 

2012 final = NDRVaryingArray( 

2013 ndr64=pkt.ndr64, 

2014 ndrendian=pkt.ndrendian, 

2015 offset=offset, 

2016 actual_count=actual_count, 

2017 _underlayer=pkt, 

2018 ) 

2019 remain, val = super(_NDRVarField, self).getfield(final, remain) 

2020 final.value = super(_NDRVarField, self).i2h(pkt, val) 

2021 return remain, final 

2022 

2023 def addfield(self, pkt, s, val): 

2024 if not isinstance(val, NDRVaryingArray): 

2025 raise ValueError( 

2026 "Expected NDRVaryingArray in %s. You are using it wrong!" % self.name 

2027 ) 

2028 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64] 

2029 _set_ctx_on(val.value, pkt) 

2030 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(pkt, s, val.offset) 

2031 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield( 

2032 pkt, 

2033 s, 

2034 val.actual_count is None 

2035 and super(_NDRVarField, self).i2len(pkt, val.value) 

2036 or val.actual_count, 

2037 ) 

2038 return super(_NDRVarField, self).addfield( 

2039 pkt, s, super(_NDRVarField, self).h2i(pkt, val.value) 

2040 ) 

2041 

2042 def i2len(self, pkt, x): 

2043 return super(_NDRVarField, self).i2len(pkt, x.value) 

2044 

2045 def any2i(self, pkt, x): 

2046 # User-friendly helper 

2047 if not isinstance(x, NDRVaryingArray): 

2048 return NDRVaryingArray( 

2049 value=super(_NDRVarField, self).any2i(pkt, x), 

2050 ) 

2051 return x 

2052 

2053 # Can't use i2repr = Field.i2repr and so on on PY2 :/ 

2054 def i2repr(self, pkt, val): 

2055 return repr(val) 

2056 

2057 def i2h(self, pkt, x): 

2058 return x 

2059 

2060 def h2i(self, pkt, x): 

2061 return x 

2062 

2063 def valueof(self, pkt, x): 

2064 return super(_NDRVarField, self).valueof(pkt, x.value) 

2065 

2066 

2067class NDRConformantArray(_NDRPacket): 

2068 fields_desc = [ 

2069 MultipleTypeField( 

2070 [(LELongField("max_count", None), lambda pkt: pkt and pkt.ndr64)], 

2071 LEIntField("max_count", None), 

2072 ), 

2073 MultipleTypeField( 

2074 [ 

2075 ( 

2076 PacketListField( 

2077 "value", 

2078 [], 

2079 conf.raw_layer, 

2080 count_from=lambda pkt: pkt.max_count, 

2081 ), 

2082 ( 

2083 lambda pkt: pkt.fields.get("value", None) 

2084 and isinstance(pkt.fields["value"][0], Packet), 

2085 lambda _, val: val and isinstance(val[0], Packet), 

2086 ), 

2087 ) 

2088 ], 

2089 FieldListField( 

2090 "value", [], LEIntField("", 0), count_from=lambda pkt: pkt.max_count 

2091 ), 

2092 ), 

2093 ] 

2094 

2095 

2096class NDRConformantString(_NDRPacket): 

2097 fields_desc = [ 

2098 MultipleTypeField( 

2099 [(LELongField("max_count", None), lambda pkt: pkt and pkt.ndr64)], 

2100 LEIntField("max_count", None), 

2101 ), 

2102 StrField("value", ""), 

2103 ] 

2104 

2105 

2106class _NDRConfField: 

2107 """ 

2108 NDR Conformant Array / String field 

2109 """ 

2110 

2111 CONFORMANT_STRING = False 

2112 LENGTH_FROM = False 

2113 COUNT_FROM = False 

2114 

2115 def __init__(self, *args, **kwargs): 

2116 self.conformant_in_struct = kwargs.pop("conformant_in_struct", False) 

2117 # size_is/max_is end up here, and is what defines a conformant field. 

2118 if "size_is" in kwargs: 

2119 size_is = kwargs.pop("size_is") 

2120 if self.LENGTH_FROM: 

2121 kwargs["length_from"] = size_is 

2122 elif self.COUNT_FROM: 

2123 kwargs["count_from"] = size_is 

2124 super(_NDRConfField, self).__init__(*args, **kwargs) 

2125 

2126 def getfield(self, pkt, s): 

2127 # [C706] - 14.3.7 Structures Containing Arrays 

2128 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64] 

2129 if self.conformant_in_struct: 

2130 return super(_NDRConfField, self).getfield(pkt, s) 

2131 remain, max_count = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield( 

2132 pkt, s 

2133 ) 

2134 remain, val = super(_NDRConfField, self).getfield(pkt, remain) 

2135 return remain, ( 

2136 NDRConformantString if self.CONFORMANT_STRING else NDRConformantArray 

2137 )(ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, max_count=max_count, value=val) 

2138 

2139 def addfield(self, pkt, s, val): 

2140 if self.conformant_in_struct: 

2141 return super(_NDRConfField, self).addfield(pkt, s, val) 

2142 if self.CONFORMANT_STRING and not isinstance(val, NDRConformantString): 

2143 raise ValueError( 

2144 "Expected NDRConformantString in %s. You are using it wrong!" 

2145 % self.name 

2146 ) 

2147 elif not self.CONFORMANT_STRING and not isinstance(val, NDRConformantArray): 

2148 raise ValueError( 

2149 "Expected NDRConformantArray in %s. You are using it wrong!" % self.name 

2150 ) 

2151 fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64] 

2152 _set_ctx_on(val.value, pkt) 

2153 if val.value and isinstance(val.value[0], NDRVaryingArray): 

2154 value = val.value[0] 

2155 else: 

2156 value = val.value 

2157 s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield( 

2158 pkt, 

2159 s, 

2160 val.max_count is None 

2161 and super(_NDRConfField, self).i2len(pkt, value) 

2162 or val.max_count, 

2163 ) 

2164 return super(_NDRConfField, self).addfield(pkt, s, value) 

2165 

2166 def _subval(self, x): 

2167 if self.conformant_in_struct: 

2168 value = x 

2169 elif ( 

2170 not self.CONFORMANT_STRING 

2171 and x.value 

2172 and isinstance(x.value[0], NDRVaryingArray) 

2173 ): 

2174 value = x.value[0] 

2175 else: 

2176 value = x.value 

2177 return value 

2178 

2179 def i2len(self, pkt, x): 

2180 return super(_NDRConfField, self).i2len(pkt, self._subval(x)) 

2181 

2182 def any2i(self, pkt, x): 

2183 # User-friendly helper 

2184 if self.conformant_in_struct: 

2185 return super(_NDRConfField, self).any2i(pkt, x) 

2186 if self.CONFORMANT_STRING and not isinstance(x, NDRConformantString): 

2187 return NDRConformantString( 

2188 value=super(_NDRConfField, self).any2i(pkt, x), 

2189 ) 

2190 elif not isinstance(x, NDRConformantArray): 

2191 return NDRConformantArray( 

2192 value=super(_NDRConfField, self).any2i(pkt, x), 

2193 ) 

2194 return x 

2195 

2196 # Can't use i2repr = Field.i2repr and so on on PY2 :/ 

2197 def i2repr(self, pkt, val): 

2198 return repr(val) 

2199 

2200 def i2h(self, pkt, x): 

2201 return x 

2202 

2203 def h2i(self, pkt, x): 

2204 return x 

2205 

2206 def valueof(self, pkt, x): 

2207 return super(_NDRConfField, self).valueof(pkt, self._subval(x)) 

2208 

2209 

2210class NDRVarPacketListField(_NDRVarField, _NDRPacketListField): 

2211 """ 

2212 NDR Varying PacketListField. Unused 

2213 """ 

2214 

2215 COUNT_FROM = True 

2216 

2217 

2218class NDRConfPacketListField(_NDRConfField, _NDRPacketListField): 

2219 """ 

2220 NDR Conformant PacketListField 

2221 """ 

2222 

2223 COUNT_FROM = True 

2224 

2225 

2226class NDRConfVarPacketListField(_NDRConfField, _NDRVarField, _NDRPacketListField): 

2227 """ 

2228 NDR Conformant Varying PacketListField 

2229 """ 

2230 

2231 COUNT_FROM = True 

2232 

2233 

2234class NDRConfFieldListField(_NDRConfField, NDRFieldListField): 

2235 """ 

2236 NDR Conformant FieldListField 

2237 """ 

2238 

2239 COUNT_FROM = True 

2240 

2241 

2242class NDRConfVarFieldListField(_NDRConfField, _NDRVarField, NDRFieldListField): 

2243 """ 

2244 NDR Conformant Varying FieldListField 

2245 """ 

2246 

2247 COUNT_FROM = True 

2248 

2249 

2250# NDR String fields 

2251 

2252 

2253class _NDRUtf16(Field): 

2254 def h2i(self, pkt, x): 

2255 encoding = {"big": "utf-16be", "little": "utf-16le"}[pkt.ndrendian] 

2256 return plain_str(x).encode(encoding) 

2257 

2258 def i2h(self, pkt, x): 

2259 encoding = {"big": "utf-16be", "little": "utf-16le"}[pkt.ndrendian] 

2260 return bytes_encode(x).decode(encoding, errors="replace") 

2261 

2262 

2263class NDRConfStrLenField(_NDRConfField, _NDRValueOf, StrLenField): 

2264 """ 

2265 NDR Conformant StrLenField. 

2266 

2267 This is not a "string" per NDR, but an a conformant byte array 

2268 (e.g. tower_octet_string) 

2269 """ 

2270 

2271 CONFORMANT_STRING = True 

2272 LENGTH_FROM = True 

2273 

2274 

2275class NDRConfStrLenFieldUtf16(_NDRConfField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16): 

2276 """ 

2277 NDR Conformant StrLenFieldUtf16. 

2278 

2279 See NDRConfLenStrField for comment. 

2280 """ 

2281 

2282 CONFORMANT_STRING = True 

2283 ON_WIRE_SIZE_UTF16 = False 

2284 LENGTH_FROM = True 

2285 

2286 

2287class NDRVarStrLenField(_NDRVarField, StrLenField): 

2288 """ 

2289 NDR Varying StrLenField 

2290 """ 

2291 

2292 LENGTH_FROM = True 

2293 

2294 

2295class NDRVarStrLenFieldUtf16(_NDRVarField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16): 

2296 """ 

2297 NDR Varying StrLenFieldUtf16 

2298 """ 

2299 

2300 ON_WIRE_SIZE_UTF16 = False 

2301 LENGTH_FROM = True 

2302 

2303 

2304class NDRConfVarStrLenField(_NDRConfField, _NDRVarField, _NDRValueOf, StrLenField): 

2305 """ 

2306 NDR Conformant Varying StrLenField 

2307 """ 

2308 

2309 LENGTH_FROM = True 

2310 

2311 

2312class NDRConfVarStrLenFieldUtf16( 

2313 _NDRConfField, _NDRVarField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16 

2314): 

2315 """ 

2316 NDR Conformant Varying StrLenFieldUtf16 

2317 """ 

2318 

2319 ON_WIRE_SIZE_UTF16 = False 

2320 LENGTH_FROM = True 

2321 

2322 

2323class NDRConfVarStrNullField(_NDRConfField, _NDRVarField, _NDRValueOf, StrNullField): 

2324 """ 

2325 NDR Conformant Varying StrNullField 

2326 """ 

2327 

2328 NULLFIELD = True 

2329 

2330 

2331class NDRConfVarStrNullFieldUtf16( 

2332 _NDRConfField, _NDRVarField, _NDRValueOf, StrNullFieldUtf16, _NDRUtf16 

2333): 

2334 """ 

2335 NDR Conformant Varying StrNullFieldUtf16 

2336 """ 

2337 

2338 ON_WIRE_SIZE_UTF16 = False 

2339 NULLFIELD = True 

2340 

2341 

2342# Union type 

2343 

2344 

2345class NDRUnion(_NDRPacket): 

2346 fields_desc = [ 

2347 IntField("tag", 0), 

2348 PacketField("value", None, conf.raw_layer), 

2349 ] 

2350 

2351 

2352class _NDRUnionField(MultipleTypeField): 

2353 __slots__ = ["switch_fmt", "align"] 

2354 

2355 def __init__(self, flds, dflt, align, switch_fmt): 

2356 self.switch_fmt = switch_fmt 

2357 self.align = align 

2358 super(_NDRUnionField, self).__init__(flds, dflt) 

2359 

2360 def getfield(self, pkt, s): 

2361 fmt = _e(pkt.ndrendian) + self.switch_fmt[pkt.ndr64] 

2362 remain, tag = NDRAlign(Field("", 0, fmt=fmt), align=self.align).getfield(pkt, s) 

2363 fld, _ = super(_NDRUnionField, self)._find_fld_pkt_val(pkt, NDRUnion(tag=tag)) 

2364 remain, val = fld.getfield(pkt, remain) 

2365 return remain, NDRUnion( 

2366 tag=tag, value=val, ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, _parent=pkt 

2367 ) 

2368 

2369 def addfield(self, pkt, s, val): 

2370 fmt = _e(pkt.ndrendian) + self.switch_fmt[pkt.ndr64] 

2371 if not isinstance(val, NDRUnion): 

2372 raise ValueError( 

2373 "Expected NDRUnion in %s. You are using it wrong!" % self.name 

2374 ) 

2375 _set_ctx_on(val.value, pkt) 

2376 # First, align the whole tag+union against the align param 

2377 s = NDRAlign(Field("", 0, fmt=fmt), align=self.align).addfield(pkt, s, val.tag) 

2378 # Then, compute the subfield with its own alignment 

2379 return super(_NDRUnionField, self).addfield(pkt, s, val) 

2380 

2381 def _find_fld_pkt_val(self, pkt, val): 

2382 fld, val = super(_NDRUnionField, self)._find_fld_pkt_val(pkt, val) 

2383 return fld, val.value 

2384 

2385 # Can't use i2repr = Field.i2repr and so on on PY2 :/ 

2386 def i2repr(self, pkt, val): 

2387 return repr(val) 

2388 

2389 def i2h(self, pkt, x): 

2390 return x 

2391 

2392 def h2i(self, pkt, x): 

2393 return x 

2394 

2395 def valueof(self, pkt, x): 

2396 fld, val = self._find_fld_pkt_val(pkt, x) 

2397 return fld.valueof(pkt, x.value) 

2398 

2399 

2400class NDRUnionField(NDRConstructedType, _NDRUnionField): 

2401 def __init__(self, flds, dflt, align, switch_fmt): 

2402 _NDRUnionField.__init__(self, flds, dflt, align=align, switch_fmt=switch_fmt) 

2403 NDRConstructedType.__init__(self, [x[0] for x in flds] + [dflt]) 

2404 

2405 def any2i(self, pkt, x): 

2406 # User-friendly helper 

2407 if x: 

2408 if not isinstance(x, NDRUnion): 

2409 raise ValueError("Invalid value for %s; should be NDRUnion" % self.name) 

2410 else: 

2411 x.value = _NDRUnionField.any2i(self, pkt, x) 

2412 return x 

2413 

2414 

2415# Misc 

2416 

2417 

2418class NDRRecursiveField(Field): 

2419 """ 

2420 A special Field that is used for pointer recursion 

2421 """ 

2422 

2423 def __init__(self, name, fmt="I"): 

2424 super(NDRRecursiveField, self).__init__(name, None, fmt=fmt) 

2425 

2426 def getfield(self, pkt, s): 

2427 return NDRFullEmbPointerField(NDRPacketField("", None, pkt.__class__)).getfield( 

2428 pkt, s 

2429 ) 

2430 

2431 def addfield(self, pkt, s, val): 

2432 return NDRFullEmbPointerField(NDRPacketField("", None, pkt.__class__)).addfield( 

2433 pkt, s, val 

2434 ) 

2435 

2436 

2437# The very few NDR-specific structures 

2438 

2439 

2440class NDRContextHandle(NDRPacket): 

2441 ALIGNMENT = (4, 4) 

2442 fields_desc = [ 

2443 LEIntField("attributes", 0), 

2444 StrFixedLenField("uuid", b"", length=16), 

2445 ] 

2446 

2447 def guess_payload_class(self, payload): 

2448 return conf.padding_layer 

2449 

2450 

2451# --- Type Serialization Version 1 - [MSRPCE] sect 2.2.6 

2452 

2453 

2454def _get_ndrtype1_endian(pkt): 

2455 if pkt.underlayer is None: 

2456 return "<" 

2457 return {0x00: ">", 0x10: "<"}.get(pkt.underlayer.Endianness, "<") 

2458 

2459 

2460class NDRSerialization1Header(Packet): 

2461 fields_desc = [ 

2462 ByteField("Version", 1), 

2463 ByteEnumField("Endianness", 0x10, {0x00: "big", 0x10: "little"}), 

2464 LEShortField("CommonHeaderLength", 8), 

2465 XLEIntField("Filler", 0xCCCCCCCC), 

2466 ] 

2467 

2468 

2469class NDRSerialization1PrivateHeader(Packet): 

2470 fields_desc = [ 

2471 EField( 

2472 LEIntField("ObjectBufferLength", 0), endianness_from=_get_ndrtype1_endian 

2473 ), 

2474 XLEIntField("Filler", 0), 

2475 ] 

2476 

2477 

2478def ndr_deserialize1(b, cls, ndr64=False): 

2479 """ 

2480 Deserialize Type Serialization Version 1 according to [MS-RPCE] sect 2.2.6 

2481 """ 

2482 if issubclass(cls, NDRPacket): 

2483 # We use an intermediary class for two reasons: 

2484 # - it properly sets deferred pointers 

2485 # - it uses NDRPacketField which handles deported conformant fields 

2486 class _cls(NDRPacket): 

2487 fields_desc = [ 

2488 NDRFullPointerField(NDRPacketField("pkt", None, cls)), 

2489 ] 

2490 

2491 hdr = NDRSerialization1Header(b[:8]) / NDRSerialization1PrivateHeader(b[8:16]) 

2492 endian = {0x00: "big", 0x10: "little"}[hdr.Endianness] 

2493 padlen = (-hdr.ObjectBufferLength) % _TYPE1_S_PAD 

2494 # padlen should be 0 (pad included in length), but some implementations 

2495 # implement apparently misread the spec 

2496 return ( 

2497 hdr 

2498 / _cls( 

2499 b[16 : 20 + hdr.ObjectBufferLength], 

2500 ndr64=ndr64, 

2501 ndrendian=endian, 

2502 ).pkt 

2503 / conf.padding_layer(b[20 + padlen + hdr.ObjectBufferLength :]) 

2504 ) 

2505 return NDRSerialization1Header(b[:8]) / cls(b[8:]) 

2506 

2507 

2508def ndr_serialize1(pkt): 

2509 """ 

2510 Serialize Type Serialization Version 1 

2511 """ 

2512 pkt = pkt.copy() 

2513 endian = getattr(pkt, "ndrendian", "little") 

2514 if not isinstance(pkt, NDRSerialization1Header): 

2515 if not isinstance(pkt, NDRPacket): 

2516 return bytes(NDRSerialization1Header(Endianness=endian) / pkt) 

2517 if isinstance(pkt, NDRPointer): 

2518 cls = pkt.value.__class__ 

2519 else: 

2520 cls = pkt.__class__ 

2521 val = pkt 

2522 pkt_len = len(pkt) 

2523 # ObjectBufferLength: 

2524 # > It MUST include the padding length and exclude the header itself 

2525 pkt = NDRSerialization1Header( 

2526 Endianness=endian 

2527 ) / NDRSerialization1PrivateHeader( 

2528 ObjectBufferLength=pkt_len + (-pkt_len) % _TYPE1_S_PAD 

2529 ) 

2530 else: 

2531 cls = pkt.value.__class__ 

2532 val = pkt.payload.payload 

2533 pkt.payload.remove_payload() 

2534 

2535 # See above about why we need an intermediary class 

2536 class _cls(NDRPacket): 

2537 fields_desc = [ 

2538 NDRFullPointerField(NDRPacketField("pkt", None, cls)), 

2539 ] 

2540 

2541 ret = bytes(pkt / _cls(pkt=val)) 

2542 return ret + (-len(ret) % _TYPE1_S_PAD) * b"\x00" 

2543 

2544 

2545class _NDRSerializeType1: 

2546 def __init__(self, *args, **kwargs): 

2547 super(_NDRSerializeType1, self).__init__(*args, **kwargs) 

2548 

2549 def i2m(self, pkt, val): 

2550 return ndr_serialize1(val) 

2551 

2552 def m2i(self, pkt, s): 

2553 return ndr_deserialize1(s, self.cls, ndr64=False) 

2554 

2555 def i2len(self, pkt, val): 

2556 return len(self.i2m(pkt, val)) 

2557 

2558 

2559class NDRSerializeType1PacketField(_NDRSerializeType1, PacketField): 

2560 __slots__ = ["ptr"] 

2561 

2562 

2563class NDRSerializeType1PacketLenField(_NDRSerializeType1, PacketLenField): 

2564 __slots__ = ["ptr"] 

2565 

2566 

2567class NDRSerializeType1PacketListField(_NDRSerializeType1, PacketListField): 

2568 __slots__ = ["ptr"] 

2569 

2570 

2571# --- DCE/RPC session 

2572 

2573 

2574class DceRpcSession(DefaultSession): 

2575 """ 

2576 A DCE/RPC session within a TCP socket. 

2577 """ 

2578 

2579 def __init__(self, *args, **kwargs): 

2580 self.rpc_bind_interface = None 

2581 self.ndr64 = False 

2582 self.ndrendian = "little" 

2583 self.support_header_signing = kwargs.pop("support_header_signing", True) 

2584 self.header_sign = conf.dcerpc_force_header_signing 

2585 self.ssp = kwargs.pop("ssp", None) 

2586 self.sspcontext = kwargs.pop("sspcontext", None) 

2587 self.auth_level = kwargs.pop("auth_level", None) 

2588 self.auth_context_id = kwargs.pop("auth_context_id", 0) 

2589 self.map_callid_opnum = {} 

2590 self.frags = collections.defaultdict(lambda: b"") 

2591 self.sniffsspcontexts = {} # Unfinished contexts for passive 

2592 if conf.dcerpc_session_enable and conf.winssps_passive: 

2593 for ssp in conf.winssps_passive: 

2594 self.sniffsspcontexts[ssp] = None 

2595 super(DceRpcSession, self).__init__(*args, **kwargs) 

2596 

2597 def _up_pkt(self, pkt): 

2598 """ 

2599 Common function to handle the DCE/RPC session: what interfaces are bind, 

2600 opnums, etc. 

2601 """ 

2602 opnum = None 

2603 opts = {} 

2604 if DceRpc5Bind in pkt or DceRpc5AlterContext in pkt: 

2605 # bind => get which RPC interface 

2606 for ctx in pkt.context_elem: 

2607 if_uuid = ctx.abstract_syntax.if_uuid 

2608 if_version = ctx.abstract_syntax.if_version 

2609 try: 

2610 self.rpc_bind_interface = DCE_RPC_INTERFACES[(if_uuid, if_version)] 

2611 except KeyError: 

2612 self.rpc_bind_interface = None 

2613 log_runtime.warning( 

2614 "Unknown RPC interface %s. Try loading the IDL" % if_uuid 

2615 ) 

2616 elif DceRpc5BindAck in pkt or DceRpc5AlterContextResp in pkt: 

2617 # bind ack => is it NDR64 

2618 for res in pkt.results: 

2619 if res.result == 0: # Accepted 

2620 self.ndrendian = {0: "big", 1: "little"}[pkt[DceRpc5].endian] 

2621 if res.transfer_syntax.sprintf("%if_uuid%") == "NDR64": 

2622 self.ndr64 = True 

2623 elif DceRpc5Request in pkt: 

2624 # request => match opnum with callID 

2625 opnum = pkt.opnum 

2626 self.map_callid_opnum[pkt.call_id] = opnum, pkt[DceRpc5Request].payload 

2627 elif DceRpc5Response in pkt: 

2628 # response => get opnum from table 

2629 try: 

2630 opnum, opts["request_packet"] = self.map_callid_opnum[pkt.call_id] 

2631 del self.map_callid_opnum[pkt.call_id] 

2632 except KeyError: 

2633 log_runtime.info("Unknown call_id %s in DCE/RPC session" % pkt.call_id) 

2634 # Bind / Alter request/response specific 

2635 if ( 

2636 DceRpc5Bind in pkt 

2637 or DceRpc5AlterContext in pkt 

2638 or DceRpc5BindAck in pkt 

2639 or DceRpc5AlterContextResp in pkt 

2640 ): 

2641 # Detect if "Header Signing" is in use 

2642 if pkt.pfc_flags & 0x04: # PFC_SUPPORT_HEADER_SIGN 

2643 self.header_sign = True 

2644 return opnum, opts 

2645 

2646 # [C706] sect 12.6.2 - Fragmentation and Reassembly 

2647 # Since the connection-oriented transport guarantees sequentiality, the receiver 

2648 # will always receive the fragments in order. 

2649 

2650 def _defragment(self, pkt, body=None): 

2651 """ 

2652 Function to defragment DCE/RPC packets. 

2653 """ 

2654 uid = pkt.call_id 

2655 if pkt.pfc_flags.PFC_FIRST_FRAG and pkt.pfc_flags.PFC_LAST_FRAG: 

2656 # Not fragmented 

2657 return body 

2658 if pkt.pfc_flags.PFC_FIRST_FRAG or uid in self.frags: 

2659 # Packet is fragmented 

2660 if body is None: 

2661 body = pkt[DceRpc5].payload.payload.original 

2662 self.frags[uid] += body 

2663 if pkt.pfc_flags.PFC_LAST_FRAG: 

2664 return self.frags[uid] 

2665 else: 

2666 # Not fragmented 

2667 return body 

2668 

2669 # C706 sect 12.5.2.15 - PDU Body Length 

2670 # "The maximum PDU body size is 65528 bytes." 

2671 MAX_PDU_BODY_SIZE = 4176 

2672 

2673 def _fragment(self, pkt, body): 

2674 """ 

2675 Function to fragment DCE/RPC packets. 

2676 """ 

2677 if len(body) > self.MAX_PDU_BODY_SIZE: 

2678 # Clear any PFC_*_FRAG flag 

2679 pkt.pfc_flags &= 0xFC 

2680 

2681 # Iterate through fragments 

2682 cur = None 

2683 while body: 

2684 # Create a fragment 

2685 pkt_frag = pkt.copy() 

2686 

2687 if cur is None: 

2688 # It's the first one 

2689 pkt_frag.pfc_flags += "PFC_FIRST_FRAG" 

2690 

2691 # Split 

2692 cur, body = ( 

2693 body[: self.MAX_PDU_BODY_SIZE], 

2694 body[self.MAX_PDU_BODY_SIZE :], 

2695 ) 

2696 

2697 if not body: 

2698 # It's the last one 

2699 pkt_frag.pfc_flags += "PFC_LAST_FRAG" 

2700 yield pkt_frag, cur 

2701 else: 

2702 yield pkt, body 

2703 

2704 # [MS-RPCE] sect 3.3.1.5.2.2 

2705 

2706 # The PDU header, PDU body, and sec_trailer MUST be passed in the input message, in 

2707 # this order, to GSS_WrapEx, GSS_UnwrapEx, GSS_GetMICEx, and GSS_VerifyMICEx. For 

2708 # integrity protection the sign flag for that PDU segment MUST be set to TRUE, else 

2709 # it MUST be set to FALSE. For confidentiality protection, the conf_req_flag for 

2710 # that PDU segment MUST be set to TRUE, else it MUST be set to FALSE. 

2711 

2712 # If the authentication level is RPC_C_AUTHN_LEVEL_PKT_PRIVACY, the PDU body will 

2713 # be encrypted. 

2714 # The PDU body from the output message of GSS_UnwrapEx represents the plain text 

2715 # version of the PDU body. The PDU header and sec_trailer output from the output 

2716 # message SHOULD be ignored. 

2717 # Similarly the signature output SHOULD be ignored. 

2718 

2719 def in_pkt(self, pkt): 

2720 # Check for encrypted payloads 

2721 body = None 

2722 if conf.raw_layer in pkt.payload: 

2723 body = bytes(pkt.payload[conf.raw_layer]) 

2724 # If we are doing passive sniffing 

2725 if conf.dcerpc_session_enable and conf.winssps_passive: 

2726 # We have Windows SSPs, and no current context 

2727 if pkt.auth_verifier and pkt.auth_verifier.is_ssp(): 

2728 # This is a bind/alter/auth3 req/resp 

2729 for ssp in self.sniffsspcontexts: 

2730 self.sniffsspcontexts[ssp], status = ssp.GSS_Passive( 

2731 self.sniffsspcontexts[ssp], 

2732 pkt.auth_verifier.auth_value, 

2733 req_flags=GSS_S_FLAGS.GSS_S_ALLOW_MISSING_BINDINGS 

2734 | GSS_C_FLAGS.GSS_C_DCE_STYLE, 

2735 ) 

2736 if status == GSS_S_COMPLETE: 

2737 self.auth_level = DCE_C_AUTHN_LEVEL( 

2738 int(pkt.auth_verifier.auth_level) 

2739 ) 

2740 self.ssp = ssp 

2741 self.sspcontext = self.sniffsspcontexts[ssp] 

2742 self.sniffsspcontexts[ssp] = None 

2743 elif ( 

2744 self.sspcontext 

2745 and pkt.auth_verifier 

2746 and pkt.auth_verifier.is_protected() 

2747 and body 

2748 ): 

2749 # This is a request/response 

2750 if self.sspcontext.passive: 

2751 self.ssp.GSS_Passive_set_Direction( 

2752 self.sspcontext, 

2753 IsAcceptor=DceRpc5Response in pkt, 

2754 ) 

2755 if pkt.auth_verifier and pkt.auth_verifier.is_protected() and body: 

2756 if self.sspcontext is None: 

2757 return pkt 

2758 if self.auth_level in ( 

2759 RPC_C_AUTHN_LEVEL.PKT_INTEGRITY, 

2760 RPC_C_AUTHN_LEVEL.PKT_PRIVACY, 

2761 ): 

2762 # note: 'vt_trailer' is included in the pdu body 

2763 # [MS-RPCE] sect 2.2.2.13 

2764 # "The data structures MUST only appear in a request PDU, and they 

2765 # SHOULD be placed in the PDU immediately after the stub data but 

2766 # before the authentication padding octets. Therefore, for security 

2767 # purposes, the verification trailer is considered part of the PDU 

2768 # body." 

2769 if pkt.vt_trailer: 

2770 body += bytes(pkt.vt_trailer) 

2771 # Account for padding when computing checksum/encryption 

2772 if pkt.auth_padding: 

2773 body += pkt.auth_padding 

2774 

2775 # Build pdu_header and sec_trailer 

2776 pdu_header = pkt.copy() 

2777 sec_trailer = pdu_header.auth_verifier 

2778 # sec_trailer: include the sec_trailer but not the Authentication token 

2779 authval_len = len(sec_trailer.auth_value) 

2780 # Discard everything out of the header 

2781 pdu_header.auth_padding = None 

2782 pdu_header.auth_verifier = None 

2783 pdu_header.payload.payload = NoPayload() 

2784 pdu_header.vt_trailer = None 

2785 

2786 # [MS-RPCE] sect 2.2.2.12 

2787 if self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY: 

2788 _msgs = self.ssp.GSS_UnwrapEx( 

2789 self.sspcontext, 

2790 [ 

2791 # "PDU header" 

2792 SSP.WRAP_MSG( 

2793 conf_req_flag=False, 

2794 sign=self.header_sign, 

2795 data=bytes(pdu_header), 

2796 ), 

2797 # "PDU body" 

2798 SSP.WRAP_MSG( 

2799 conf_req_flag=True, 

2800 sign=True, 

2801 data=body, 

2802 ), 

2803 # "sec_trailer" 

2804 SSP.WRAP_MSG( 

2805 conf_req_flag=False, 

2806 sign=self.header_sign, 

2807 data=bytes(sec_trailer)[:-authval_len], 

2808 ), 

2809 ], 

2810 pkt.auth_verifier.auth_value, 

2811 ) 

2812 body = _msgs[1].data # PDU body 

2813 elif self.auth_level == RPC_C_AUTHN_LEVEL.PKT_INTEGRITY: 

2814 self.ssp.GSS_VerifyMICEx( 

2815 self.sspcontext, 

2816 [ 

2817 # "PDU header" 

2818 SSP.MIC_MSG( 

2819 sign=self.header_sign, 

2820 data=bytes(pdu_header), 

2821 ), 

2822 # "PDU body" 

2823 SSP.MIC_MSG( 

2824 sign=True, 

2825 data=body, 

2826 ), 

2827 # "sec_trailer" 

2828 SSP.MIC_MSG( 

2829 sign=self.header_sign, 

2830 data=bytes(sec_trailer)[:-authval_len], 

2831 ), 

2832 ], 

2833 pkt.auth_verifier.auth_value, 

2834 ) 

2835 # Put padding back into the header 

2836 if pkt.auth_padding: 

2837 padlen = len(pkt.auth_padding) 

2838 body, pkt.auth_padding = body[:-padlen], body[-padlen:] 

2839 # Put back vt_trailer into the header, if present. 

2840 if _SECTRAILER_MAGIC in body: 

2841 body, pkt.vt_trailer = pkt.get_field("vt_trailer").getfield( 

2842 pkt, body 

2843 ) 

2844 # If it's a request / response, could be fragmented 

2845 if isinstance(pkt.payload, (DceRpc5Request, DceRpc5Response)) and body: 

2846 body = self._defragment(pkt, body) 

2847 if not body: 

2848 return 

2849 # Get opnum and options 

2850 opnum, opts = self._up_pkt(pkt) 

2851 # Try to parse the payload 

2852 if opnum is not None and self.rpc_bind_interface: 

2853 # use opnum to parse the payload 

2854 is_response = DceRpc5Response in pkt 

2855 try: 

2856 cls = self.rpc_bind_interface.opnums[opnum][is_response] 

2857 except KeyError: 

2858 log_runtime.warning( 

2859 "Unknown opnum %s for interface %s" 

2860 % (opnum, self.rpc_bind_interface) 

2861 ) 

2862 pkt.payload[conf.raw_layer].load = body 

2863 return pkt 

2864 if body: 

2865 # Dissect payload using class 

2866 try: 

2867 payload = cls( 

2868 body, ndr64=self.ndr64, ndrendian=self.ndrendian, **opts 

2869 ) 

2870 except Exception: 

2871 if conf.debug_dissector: 

2872 log_runtime.error("%s dissector failed", cls.__name__) 

2873 if cls is not None: 

2874 raise 

2875 payload = conf.raw_layer(body, _internal=1) 

2876 pkt.payload[conf.raw_layer].underlayer.remove_payload() 

2877 if conf.padding_layer in payload: 

2878 # Most likely, dissection failed. 

2879 log_runtime.warning( 

2880 "Padding detected when dissecting %s. Looks wrong." % cls 

2881 ) 

2882 pad = payload[conf.padding_layer] 

2883 pad.underlayer.payload = conf.raw_layer(load=pad.load) 

2884 pkt /= payload 

2885 # If a request was encrypted, we need to re-register it once re-parsed. 

2886 if not is_response and self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY: 

2887 self._up_pkt(pkt) 

2888 elif not cls.fields_desc: 

2889 # Request class has no payload 

2890 pkt /= cls(ndr64=self.ndr64, ndrendian=self.ndrendian, **opts) 

2891 elif body: 

2892 pkt.payload[conf.raw_layer].load = body 

2893 return pkt 

2894 

2895 def out_pkt(self, pkt): 

2896 assert DceRpc5 in pkt 

2897 # Register opnum and options 

2898 self._up_pkt(pkt) 

2899 

2900 # If it's a request / response, we can frag it 

2901 if isinstance(pkt.payload, (DceRpc5Request, DceRpc5Response)): 

2902 # The list of packet responses 

2903 pkts = [] 

2904 # Take the body payload, and eventually split it 

2905 body = bytes(pkt.payload.payload) 

2906 

2907 for pkt, body in self._fragment(pkt, body): 

2908 if pkt.auth_verifier is not None: 

2909 # Verifier already set 

2910 pkts.append(pkt) 

2911 continue 

2912 

2913 # Sign / Encrypt 

2914 if self.sspcontext: 

2915 signature = None 

2916 if self.auth_level in ( 

2917 RPC_C_AUTHN_LEVEL.PKT_INTEGRITY, 

2918 RPC_C_AUTHN_LEVEL.PKT_PRIVACY, 

2919 ): 

2920 # Account for padding when computing checksum/encryption 

2921 if pkt.auth_padding is None: 

2922 padlen = (-len(body)) % _COMMON_AUTH_PAD # authdata padding 

2923 pkt.auth_padding = b"\x00" * padlen 

2924 else: 

2925 padlen = len(pkt.auth_padding) 

2926 # Remember that vt_trailer is included in the PDU 

2927 if pkt.vt_trailer: 

2928 body += bytes(pkt.vt_trailer) 

2929 # Remember that padding IS SIGNED & ENCRYPTED 

2930 body += pkt.auth_padding 

2931 # Add the auth_verifier 

2932 pkt.auth_verifier = CommonAuthVerifier( 

2933 auth_type=self.ssp.auth_type, 

2934 auth_level=self.auth_level, 

2935 auth_context_id=self.auth_context_id, 

2936 auth_pad_length=padlen, 

2937 # Note: auth_value should have the correct length because 

2938 # when using PFC_SUPPORT_HEADER_SIGN, auth_len 

2939 # (and frag_len) is included in the token.. but this 

2940 # creates a dependency loop as you'd need to know the token 

2941 # length to compute the token. Windows solves this by 

2942 # setting the 'Maximum Signature Length' (or something 

2943 # similar) beforehand, instead of the real length. 

2944 # See `gensec_sig_size` in samba. 

2945 auth_value=b"\x00" 

2946 * self.ssp.MaximumSignatureLength(self.sspcontext), 

2947 ) 

2948 # Build pdu_header and sec_trailer 

2949 pdu_header = pkt.copy() 

2950 pdu_header.auth_len = len(pdu_header.auth_verifier) - 8 

2951 pdu_header.frag_len = len(pdu_header) 

2952 sec_trailer = pdu_header.auth_verifier 

2953 # sec_trailer: include the sec_trailer but not the 

2954 # Authentication token 

2955 authval_len = len(sec_trailer.auth_value) 

2956 # sec_trailer.auth_value = None 

2957 # Discard everything out of the header 

2958 pdu_header.auth_padding = None 

2959 pdu_header.auth_verifier = None 

2960 pdu_header.payload.payload = NoPayload() 

2961 pdu_header.vt_trailer = None 

2962 signature = None 

2963 # [MS-RPCE] sect 2.2.2.12 

2964 if self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY: 

2965 _msgs, signature = self.ssp.GSS_WrapEx( 

2966 self.sspcontext, 

2967 [ 

2968 # "PDU header" 

2969 SSP.WRAP_MSG( 

2970 conf_req_flag=False, 

2971 sign=self.header_sign, 

2972 data=bytes(pdu_header), 

2973 ), 

2974 # "PDU body" 

2975 SSP.WRAP_MSG( 

2976 conf_req_flag=True, 

2977 sign=True, 

2978 data=body, 

2979 ), 

2980 # "sec_trailer" 

2981 SSP.WRAP_MSG( 

2982 conf_req_flag=False, 

2983 sign=self.header_sign, 

2984 data=bytes(sec_trailer)[:-authval_len], 

2985 ), 

2986 ], 

2987 ) 

2988 s = _msgs[1].data # PDU body 

2989 elif self.auth_level == RPC_C_AUTHN_LEVEL.PKT_INTEGRITY: 

2990 signature = self.ssp.GSS_GetMICEx( 

2991 self.sspcontext, 

2992 [ 

2993 # "PDU header" 

2994 SSP.MIC_MSG( 

2995 sign=self.header_sign, 

2996 data=bytes(pdu_header), 

2997 ), 

2998 # "PDU body" 

2999 SSP.MIC_MSG( 

3000 sign=True, 

3001 data=body, 

3002 ), 

3003 # "sec_trailer" 

3004 SSP.MIC_MSG( 

3005 sign=self.header_sign, 

3006 data=bytes(sec_trailer)[:-authval_len], 

3007 ), 

3008 ], 

3009 pkt.auth_verifier.auth_value, 

3010 ) 

3011 s = body 

3012 else: 

3013 raise ValueError("Impossible") 

3014 # Put padding back in the header 

3015 if padlen: 

3016 s, pkt.auth_padding = s[:-padlen], s[-padlen:] 

3017 # Put back vt_trailer into the header 

3018 if pkt.vt_trailer: 

3019 vtlen = len(pkt.vt_trailer) 

3020 s, pkt.vt_trailer = s[:-vtlen], s[-vtlen:] 

3021 else: 

3022 s = body 

3023 

3024 # now inject the encrypted payload into the packet 

3025 pkt.payload.payload = conf.raw_layer(load=s) 

3026 # and the auth_value 

3027 if signature: 

3028 pkt.auth_verifier.auth_value = signature 

3029 else: 

3030 pkt.auth_verifier = None 

3031 # Add to the list 

3032 pkts.append(pkt) 

3033 return pkts 

3034 else: 

3035 return [pkt] 

3036 

3037 def process(self, pkt: Packet) -> Optional[Packet]: 

3038 """ 

3039 Used when DceRpcSession is used for passive sniffing. 

3040 """ 

3041 pkt = super(DceRpcSession, self).process(pkt) 

3042 if pkt is not None and DceRpc5 in pkt: 

3043 rpkt = self.in_pkt(pkt) 

3044 if rpkt is None: 

3045 # We are passively dissecting a fragmented packet. Return 

3046 # just the header showing that it was indeed, fragmented. 

3047 pkt[DceRpc5].payload.remove_payload() 

3048 return pkt 

3049 return rpkt 

3050 return pkt 

3051 

3052 

3053class DceRpcSocket(StreamSocket): 

3054 """ 

3055 A Wrapper around StreamSocket that uses a DceRpcSession 

3056 """ 

3057 

3058 def __init__(self, *args, **kwargs): 

3059 self.transport = kwargs.pop("transport", None) 

3060 self.session = DceRpcSession( 

3061 ssp=kwargs.pop("ssp", None), 

3062 auth_level=kwargs.pop("auth_level", None), 

3063 auth_context_id=kwargs.pop("auth_context_id", None), 

3064 support_header_signing=kwargs.pop("support_header_signing", True), 

3065 ) 

3066 super(DceRpcSocket, self).__init__(*args, **kwargs) 

3067 

3068 def send(self, x, **kwargs): 

3069 for pkt in self.session.out_pkt(x): 

3070 if self.transport == DCERPC_Transport.NCACN_NP: 

3071 # In this case DceRpcSocket wraps a SMB_RPC_SOCKET, call it directly. 

3072 self.ins.send(pkt, **kwargs) 

3073 else: 

3074 super(DceRpcSocket, self).send(pkt, **kwargs) 

3075 

3076 def recv(self, x=None): 

3077 pkt = super(DceRpcSocket, self).recv(x) 

3078 if pkt is not None: 

3079 return self.session.in_pkt(pkt) 

3080 

3081 

3082# --- TODO cleanup below 

3083 

3084# Heuristically way to find the payload class 

3085# 

3086# To add a possible payload to a DCE/RPC packet, one must first create the 

3087# packet class, then instead of binding layers using bind_layers, he must 

3088# call DceRpcPayload.register_possible_payload() with the payload class as 

3089# parameter. 

3090# 

3091# To be able to decide if the payload class is capable of handling the rest of 

3092# the dissection, the classmethod can_handle() should be implemented in the 

3093# payload class. This method is given the rest of the string to dissect as 

3094# first argument, and the DceRpc packet instance as second argument. Based on 

3095# this information, the method must return True if the class is capable of 

3096# handling the dissection, False otherwise 

3097 

3098 

3099class DceRpc4Payload(Packet): 

3100 """Dummy class which use the dispatch_hook to find the payload class""" 

3101 

3102 _payload_class = [] 

3103 

3104 @classmethod 

3105 def dispatch_hook(cls, _pkt, _underlayer=None, *args, **kargs): 

3106 """dispatch_hook to choose among different registered payloads""" 

3107 for klass in cls._payload_class: 

3108 if hasattr(klass, "can_handle") and klass.can_handle(_pkt, _underlayer): 

3109 return klass 

3110 log_runtime.warning("DCE/RPC payload class not found or undefined (using Raw)") 

3111 return Raw 

3112 

3113 @classmethod 

3114 def register_possible_payload(cls, pay): 

3115 """Method to call from possible DCE/RPC endpoint to register it as 

3116 possible payload""" 

3117 cls._payload_class.append(pay) 

3118 

3119 

3120bind_layers(DceRpc4, DceRpc4Payload)