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

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

1301 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 

24import collections 

25import importlib 

26import inspect 

27import struct 

28 

29from enum import IntEnum 

30from functools import partial 

31from uuid import UUID 

32 

33from scapy.base_classes import Packet_metaclass 

34 

35from scapy.config import conf 

36from scapy.compat import bytes_encode, plain_str 

37from scapy.error import log_runtime 

38from scapy.layers.dns import DNSStrField 

39from scapy.layers.ntlm import ( 

40 NTLM_Header, 

41 NTLMSSP_MESSAGE_SIGNATURE, 

42) 

43from scapy.packet import ( 

44 Packet, 

45 Raw, 

46 bind_bottom_up, 

47 bind_layers, 

48 bind_top_down, 

49 NoPayload, 

50) 

51from scapy.fields import ( 

52 _FieldContainer, 

53 BitEnumField, 

54 BitField, 

55 ByteEnumField, 

56 ByteField, 

57 ConditionalField, 

58 EnumField, 

59 Field, 

60 FieldLenField, 

61 FieldListField, 

62 FlagsField, 

63 IntField, 

64 LEIntEnumField, 

65 LEIntField, 

66 LELongField, 

67 LEShortEnumField, 

68 LEShortField, 

69 LenField, 

70 MultipleTypeField, 

71 PacketField, 

72 PacketLenField, 

73 PacketListField, 

74 PadField, 

75 ReversePadField, 

76 ShortEnumField, 

77 ShortField, 

78 SignedByteField, 

79 StrField, 

80 StrFixedLenField, 

81 StrLenField, 

82 StrLenFieldUtf16, 

83 StrNullField, 

84 StrNullFieldUtf16, 

85 TrailerField, 

86 UUIDEnumField, 

87 UUIDField, 

88 XByteField, 

89 XLEIntField, 

90 XLELongField, 

91 XLEShortField, 

92 XShortField, 

93 XStrFixedLenField, 

94) 

95from scapy.sessions import DefaultSession 

96from scapy.supersocket import StreamSocket 

97 

98from scapy.layers.kerberos import ( 

99 KRB_InnerToken, 

100 Kerberos, 

101) 

102from scapy.layers.gssapi import ( 

103 GSSAPI_BLOB, 

104 GSSAPI_BLOB_SIGNATURE, 

105 GSS_S_COMPLETE, 

106 GSS_S_FLAGS, 

107 GSS_C_FLAGS, 

108 SSP, 

109) 

110from scapy.layers.inet import TCP 

111 

112from scapy.contrib.rtps.common_types import ( 

113 EField, 

114 EPacket, 

115 EPacketField, 

116 EPacketListField, 

117) 

118 

119# Typing imports 

120from typing import ( 

121 Optional, 

122 Union, 

123) 

124 

125# the alignment of auth_pad 

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

127_COMMON_AUTH_PAD = 16 

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

129# ([MS-RPCE] sect 2.2.6.2) 

130_TYPE1_S_PAD = 8 

131 

132# DCE/RPC Packet 

133DCE_RPC_TYPE = { 

134 0: "request", 

135 1: "ping", 

136 2: "response", 

137 3: "fault", 

138 4: "working", 

139 5: "no_call", 

140 6: "reject", 

141 7: "acknowledge", 

142 8: "connectionless_cancel", 

143 9: "frag_ack", 

144 10: "cancel_ack", 

145 11: "bind", 

146 12: "bind_ack", 

147 13: "bind_nak", 

148 14: "alter_context", 

149 15: "alter_context_resp", 

150 16: "auth3", 

151 17: "shutdown", 

152 18: "co_cancel", 

153 19: "orphaned", 

154} 

155_DCE_RPC_4_FLAGS1 = [ 

156 "reserved_01", 

157 "last_frag", 

158 "frag", 

159 "no_frag_ack", 

160 "maybe", 

161 "idempotent", 

162 "broadcast", 

163 "reserved_7", 

164] 

165_DCE_RPC_4_FLAGS2 = [ 

166 "reserved_0", 

167 "cancel_pending", 

168 "reserved_2", 

169 "reserved_3", 

170 "reserved_4", 

171 "reserved_5", 

172 "reserved_6", 

173 "reserved_7", 

174] 

175DCE_RPC_TRANSFER_SYNTAXES = { 

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

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

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

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

180} 

181DCE_RPC_INTERFACES_NAMES = {} 

182DCE_RPC_INTERFACES_NAMES_rev = {} 

183COM_INTERFACES_NAMES = {} 

184COM_INTERFACES_NAMES_rev = {} 

185 

186 

187class DCERPC_Transport(IntEnum): 

188 """ 

189 Protocols identifiers currently supported by Scapy 

190 """ 

191 

192 NCACN_IP_TCP = 0x07 

193 NCACN_NP = 0x0F 

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

195 

196 

197# [C706] Appendix I with names from Appendix B 

198DCE_RPC_PROTOCOL_IDENTIFIERS = { 

199 0x0: "OSI OID", # Special 

200 0x0D: "UUID", # Special 

201 # Transports 

202 # 0x2: "DNA Session Control", 

203 # 0x3: "DNA Session Control V3", 

204 # 0x4: "DNA NSP Transport", 

205 # 0x5: "OSI TP4", 

206 0x06: "NCADG_OSI_CLSN", # [C706] 

207 0x07: "NCACN_IP_TCP", # [C706] 

208 0x08: "NCADG_IP_UDP", # [C706] 

209 0x09: "IP", # [C706] 

210 0x0A: "RPC connectionless protocol", # [C706] 

211 0x0B: "RPC connection-oriented protocol", # [C706] 

212 0x0C: "NCALRPC", 

213 0x0F: "NCACN_NP", # [MS-RPCE] 

214 0x11: "NCACN_NB", # [C706] 

215 0x12: "NCACN_NB_NB", # [MS-RPCE] 

216 0x13: "NCACN_SPX", # [C706] 

217 0x14: "NCADG_IPX", # [C706] 

218 0x16: "NCACN_AT_DSP", # [C706] 

219 0x17: "NCADG_AT_DSP", # [C706] 

220 0x19: "NCADG_NB", # [C706] 

221 0x1A: "NCACN_VNS_SPP", # [C706] 

222 0x1B: "NCADG_VNS_IPC", # [C706] 

223 0x1F: "NCACN_HTTP", # [MS-RPCE] 

224} 

225 

226 

227def _dce_rpc_endianness(pkt): 

228 """ 

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

230 """ 

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

232 return ">" 

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

234 return "<" 

235 else: 

236 return "!" 

237 

238 

239class _EField(EField): 

240 def __init__(self, fld): 

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

242 

243 

244class DceRpc(Packet): 

245 """DCE/RPC packet""" 

246 

247 @classmethod 

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

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

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

251 if ver == 4: 

252 return DceRpc4 

253 elif ver == 5: 

254 return DceRpc5 

255 return DceRpc5 

256 

257 @classmethod 

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

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

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

261 return DceRpc(data) 

262 

263 

264bind_bottom_up(TCP, DceRpc, sport=135) 

265bind_layers(TCP, DceRpc, dport=135) 

266 

267 

268class _DceRpcPayload(Packet): 

269 @property 

270 def endianness(self): 

271 if not self.underlayer: 

272 return "!" 

273 return _dce_rpc_endianness(self.underlayer) 

274 

275 

276# sect 12.5 

277 

278_drep = [ 

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

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

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

282 ByteField("reserved1", 0), 

283] 

284 

285 

286class DceRpc4(DceRpc): 

287 """ 

288 DCE/RPC v4 'connection-less' packet 

289 """ 

290 

291 name = "DCE/RPC v4" 

292 fields_desc = ( 

293 [ 

294 ByteEnumField( 

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

296 ), 

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

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

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

300 ] 

301 + _drep 

302 + [ 

303 XByteField("serial_hi", 0), 

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

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

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

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

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

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

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

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

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

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

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

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

316 XByteField("serial_lo", 0), 

317 ] 

318 ) 

319 

320 

321# Exceptionally, we define those 3 here. 

322 

323 

324class NL_AUTH_MESSAGE(Packet): 

325 # [MS-NRPC] sect 2.2.1.3.1 

326 name = "NL_AUTH_MESSAGE" 

327 fields_desc = [ 

328 LEIntEnumField( 

329 "MessageType", 

330 0x00000000, 

331 { 

332 0x00000000: "Request", 

333 0x00000001: "Response", 

334 }, 

335 ), 

336 FlagsField( 

337 "Flags", 

338 0, 

339 -32, 

340 [ 

341 "NETBIOS_DOMAIN_NAME", 

342 "NETBIOS_COMPUTER_NAME", 

343 "DNS_DOMAIN_NAME", 

344 "DNS_HOST_NAME", 

345 "NETBIOS_COMPUTER_NAME_UTF8", 

346 ], 

347 ), 

348 ConditionalField( 

349 StrNullField("NetbiosDomainName", ""), 

350 lambda pkt: pkt.Flags.NETBIOS_DOMAIN_NAME, 

351 ), 

352 ConditionalField( 

353 StrNullField("NetbiosComputerName", ""), 

354 lambda pkt: pkt.Flags.NETBIOS_COMPUTER_NAME, 

355 ), 

356 ConditionalField( 

357 DNSStrField("DnsDomainName", ""), 

358 lambda pkt: pkt.Flags.DNS_DOMAIN_NAME, 

359 ), 

360 ConditionalField( 

361 DNSStrField("DnsHostName", ""), 

362 lambda pkt: pkt.Flags.DNS_HOST_NAME, 

363 ), 

364 ConditionalField( 

365 # What the fuck? Why are they doing this 

366 # The spec is just wrong 

367 DNSStrField("NetbiosComputerNameUtf8", ""), 

368 lambda pkt: pkt.Flags.NETBIOS_COMPUTER_NAME_UTF8, 

369 ), 

370 ] 

371 

372 

373class NL_AUTH_SIGNATURE(Packet): 

374 # [MS-NRPC] sect 2.2.1.3.2/2.2.1.3.3 

375 name = "NL_AUTH_(SHA2_)SIGNATURE" 

376 fields_desc = [ 

377 LEShortEnumField( 

378 "SignatureAlgorithm", 

379 0x0077, 

380 { 

381 0x0077: "HMAC-MD5", 

382 0x0013: "HMAC-SHA256", 

383 }, 

384 ), 

385 LEShortEnumField( 

386 "SealAlgorithm", 

387 0xFFFF, 

388 { 

389 0xFFFF: "Unencrypted", 

390 0x007A: "RC4", 

391 0x001A: "AES-128", 

392 }, 

393 ), 

394 XLEShortField("Pad", 0xFFFF), 

395 ShortField("Flags", 0), 

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

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

398 ConditionalField( 

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

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

401 ), 

402 MultipleTypeField( 

403 [ 

404 ( 

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

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

407 ), 

408 ], 

409 StrField("Reserved2", b""), 

410 ), 

411 ] 

412 

413 

414# [MS-RPCE] sect 2.2.1.1.7 

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

416# rpcdce.h 

417 

418 

419class RPC_C_AUTHN(IntEnum): 

420 NONE = 0x00 

421 DCE_PRIVATE = 0x01 

422 DCE_PUBLIC = 0x02 

423 DEC_PUBLIC = 0x04 

424 GSS_NEGOTIATE = 0x09 

425 WINNT = 0x0A 

426 GSS_SCHANNEL = 0x0E 

427 GSS_KERBEROS = 0x10 

428 DPA = 0x11 

429 MSN = 0x12 

430 KERNEL = 0x14 

431 DIGEST = 0x15 

432 NEGO_EXTENDED = 0x1E 

433 PKU2U = 0x1F 

434 LIVE_SSP = 0x20 

435 LIVEXP_SSP = 0x23 

436 CLOUD_AP = 0x24 

437 NETLOGON = 0x44 

438 MSONLINE = 0x52 

439 MQ = 0x64 

440 DEFAULT = 0xFFFFFFFF 

441 

442 

443class RPC_C_AUTHN_LEVEL(IntEnum): 

444 DEFAULT = 0x0 

445 NONE = 0x1 

446 CONNECT = 0x2 

447 CALL = 0x3 

448 PKT = 0x4 

449 PKT_INTEGRITY = 0x5 

450 PKT_PRIVACY = 0x6 

451 

452 

453DCE_C_AUTHN_LEVEL = RPC_C_AUTHN_LEVEL # C706 name 

454 

455 

456class RPC_C_IMP_LEVEL(IntEnum): 

457 DEFAULT = 0x0 

458 ANONYMOUS = 0x1 

459 IDENTIFY = 0x2 

460 IMPERSONATE = 0x3 

461 DELEGATE = 0x4 

462 

463 

464# C706 sect 13.2.6.1 

465 

466 

467class CommonAuthVerifier(Packet): 

468 name = "Common Authentication Verifier" 

469 fields_desc = [ 

470 ByteEnumField( 

471 "auth_type", 

472 0, 

473 RPC_C_AUTHN, 

474 ), 

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

476 ByteField("auth_pad_length", None), 

477 ByteField("auth_reserved", 0), 

478 XLEIntField("auth_context_id", 0), 

479 MultipleTypeField( 

480 [ 

481 # SPNEGO 

482 ( 

483 PacketLenField( 

484 "auth_value", 

485 GSSAPI_BLOB(), 

486 GSSAPI_BLOB, 

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

488 ), 

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

490 # Bind/Alter 

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

492 ), 

493 ( 

494 PacketLenField( 

495 "auth_value", 

496 GSSAPI_BLOB_SIGNATURE(), 

497 GSSAPI_BLOB_SIGNATURE, 

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

499 ), 

500 lambda pkt: pkt.auth_type == 0x09 

501 and pkt.parent 

502 and ( 

503 # Other 

504 not pkt.parent 

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

506 ), 

507 ), 

508 # Kerberos 

509 ( 

510 PacketLenField( 

511 "auth_value", 

512 Kerberos(), 

513 Kerberos, 

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

515 ), 

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

517 # Bind/Alter 

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

519 ), 

520 ( 

521 PacketLenField( 

522 "auth_value", 

523 KRB_InnerToken(), 

524 KRB_InnerToken, 

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

526 ), 

527 lambda pkt: pkt.auth_type == 0x10 

528 and pkt.parent 

529 and ( 

530 # Other 

531 not pkt.parent 

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

533 ), 

534 ), 

535 # NTLM 

536 ( 

537 PacketLenField( 

538 "auth_value", 

539 NTLM_Header(), 

540 NTLM_Header, 

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

542 ), 

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

544 # Bind/Alter 

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

546 ), 

547 ( 

548 PacketLenField( 

549 "auth_value", 

550 NTLMSSP_MESSAGE_SIGNATURE(), 

551 NTLMSSP_MESSAGE_SIGNATURE, 

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

553 ), 

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

555 and pkt.parent 

556 and ( 

557 # Other 

558 not pkt.parent 

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

560 ), 

561 ), 

562 # NetLogon 

563 ( 

564 PacketLenField( 

565 "auth_value", 

566 NL_AUTH_MESSAGE(), 

567 NL_AUTH_MESSAGE, 

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

569 ), 

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

571 # Bind/Alter 

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

573 ), 

574 ( 

575 PacketLenField( 

576 "auth_value", 

577 NL_AUTH_SIGNATURE(), 

578 NL_AUTH_SIGNATURE, 

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

580 ), 

581 lambda pkt: pkt.auth_type == 0x44 

582 and ( 

583 # Other 

584 not pkt.parent 

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

586 ), 

587 ), 

588 ], 

589 PacketLenField( 

590 "auth_value", 

591 None, 

592 conf.raw_layer, 

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

594 ), 

595 ), 

596 ] 

597 

598 def is_protected(self): 

599 if not self.auth_value: 

600 return False 

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

602 return False 

603 return True 

604 

605 def is_ssp(self): 

606 if not self.auth_value: 

607 return False 

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

609 return False 

610 return True 

611 

612 def default_payload_class(self, pkt): 

613 return conf.padding_layer 

614 

615 

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

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

618 

619 

620class DceRpcSecVTCommand(Packet): 

621 name = "Verification trailer command" 

622 fields_desc = [ 

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

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

625 BitEnumField( 

626 "Command", 

627 0, 

628 -14, 

629 { 

630 0x0001: "SEC_VT_COMMAND_BITMASK_1", 

631 0x0002: "SEC_VT_COMMAND_PCONTEXT", 

632 0x0003: "SEC_VT_COMMAND_HEADER2", 

633 }, 

634 end_tot_size=-2, 

635 ), 

636 LenField("Length", None, fmt="<H"), 

637 ] 

638 

639 

640# [MS-RPCE] sect 2.2.2.13.2 

641 

642 

643class DceRpcSecVTBitmask(Packet): 

644 name = "rpc_sec_vt_bitmask" 

645 fields_desc = [ 

646 LEIntField("bits", 1), 

647 ] 

648 

649 def default_payload_class(self, pkt): 

650 return conf.padding_layer 

651 

652 

653bind_layers(DceRpcSecVTCommand, DceRpcSecVTBitmask, Command=0x0001) 

654 

655 

656# [MS-RPCE] sect 2.2.2.13.4 

657 

658 

659class DceRpcSecVTPcontext(Packet): 

660 name = "rpc_sec_vt_pcontext" 

661 fields_desc = [ 

662 UUIDEnumField( 

663 "InterfaceId", 

664 None, 

665 ( 

666 DCE_RPC_INTERFACES_NAMES.get, 

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

668 ), 

669 uuid_fmt=UUIDField.FORMAT_LE, 

670 ), 

671 LEIntField("Version", 0), 

672 UUIDEnumField( 

673 "TransferSyntax", 

674 None, 

675 DCE_RPC_TRANSFER_SYNTAXES, 

676 uuid_fmt=UUIDField.FORMAT_LE, 

677 ), 

678 LEIntField("TransferVersion", 0), 

679 ] 

680 

681 def default_payload_class(self, pkt): 

682 return conf.padding_layer 

683 

684 

685bind_layers(DceRpcSecVTCommand, DceRpcSecVTPcontext, Command=0x0002) 

686 

687 

688# [MS-RPCE] sect 2.2.2.13.3 

689 

690 

691class DceRpcSecVTHeader2(Packet): 

692 name = "rpc_sec_vt_header2" 

693 fields_desc = [ 

694 ByteField("PTYPE", 0), 

695 ByteField("Reserved1", 0), 

696 LEShortField("Reserved2", 0), 

697 LEIntField("drep", 0), 

698 LEIntField("call_id", 0), 

699 LEShortField("p_cont_id", 0), 

700 LEShortField("opnum", 0), 

701 ] 

702 

703 def default_payload_class(self, pkt): 

704 return conf.padding_layer 

705 

706 

707bind_layers(DceRpcSecVTCommand, DceRpcSecVTHeader2, Command=0x0003) 

708 

709 

710class DceRpcSecVT(Packet): 

711 name = "Verification trailer" 

712 fields_desc = [ 

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

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

715 ] 

716 

717 

718class _VerifTrailerField(PacketField): 

719 def getfield( 

720 self, 

721 pkt, 

722 s, 

723 ): 

724 if _SECTRAILER_MAGIC in s: 

725 # a bit ugly 

726 ind = s.index(_SECTRAILER_MAGIC) 

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

728 vt_trailer = self.m2i(pkt, sectrailer_bytes) 

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

730 # bad parse 

731 return s, None 

732 return remain, vt_trailer 

733 return s, None 

734 

735 

736# sect 12.6.3 

737 

738 

739_DCE_RPC_5_FLAGS = { 

740 0x01: "PFC_FIRST_FRAG", 

741 0x02: "PFC_LAST_FRAG", 

742 0x04: "PFC_PENDING_CANCEL", 

743 0x08: "PFC_RESERVED_1", 

744 0x10: "PFC_CONC_MPX", 

745 0x20: "PFC_DID_NOT_EXECUTE", 

746 0x40: "PFC_MAYBE", 

747 0x80: "PFC_OBJECT_UUID", 

748} 

749 

750# [MS-RPCE] sect 2.2.2.3 

751 

752_DCE_RPC_5_FLAGS_2 = _DCE_RPC_5_FLAGS.copy() 

753_DCE_RPC_5_FLAGS_2[0x04] = "PFC_SUPPORT_HEADER_SIGN" 

754 

755 

756_DCE_RPC_ERROR_CODES = { 

757 # Win32 

758 0x776: "OR_INVALID_OXID", 

759 0x777: "OR_INVALID_OID", 

760 0x778: "OR_INVALID_SET", 

761 # Appendix N 

762 0x1C010001: "nca_s_comm_failure", 

763 0x1C010002: "nca_s_op_rng_error", 

764 0x1C010003: "nca_s_unk_if", 

765 0x1C010006: "nca_s_wrong_boot_time", 

766 0x1C010009: "nca_s_you_crashed", 

767 0x1C01000B: "nca_s_proto_error", 

768 0x1C010013: "nca_s_out_args_too_big", 

769 0x1C010014: "nca_s_server_too_busy", 

770 0x1C010015: "nca_s_fault_string_too_long", 

771 0x1C010017: "nca_s_unsupported_type", 

772 0x1C000001: "nca_s_fault_int_div_by_zero", 

773 0x1C000002: "nca_s_fault_addr_error", 

774 0x1C000003: "nca_s_fault_fp_div_zero", 

775 0x1C000004: "nca_s_fault_fp_underflow", 

776 0x1C000005: "nca_s_fault_fp_overflow", 

777 0x1C000006: "nca_s_fault_invalid_tag", 

778 0x1C000007: "nca_s_fault_invalid_bound", 

779 0x1C000008: "nca_s_rpc_version_mismatch", 

780 0x1C000009: "nca_s_unspec_reject", 

781 0x1C00000A: "nca_s_bad_actid", 

782 0x1C00000B: "nca_s_who_are_you_failed", 

783 0x1C00000C: "nca_s_manager_not_entered", 

784 0x1C00000D: "nca_s_fault_cancel", 

785 0x1C00000E: "nca_s_fault_ill_inst", 

786 0x1C00000F: "nca_s_fault_fp_error", 

787 0x1C000010: "nca_s_fault_int_overflow", 

788 0x1C000012: "nca_s_fault_unspec", 

789 0x1C000013: "nca_s_fault_remote_comm_failure", 

790 0x1C000014: "nca_s_fault_pipe_empty", 

791 0x1C000015: "nca_s_fault_pipe_closed", 

792 0x1C000016: "nca_s_fault_pipe_order", 

793 0x1C000017: "nca_s_fault_pipe_discipline", 

794 0x1C000018: "nca_s_fault_pipe_comm_error", 

795 0x1C000019: "nca_s_fault_pipe_memory", 

796 0x1C00001A: "nca_s_fault_context_mismatch", 

797 0x1C00001B: "nca_s_fault_remote_no_memory", 

798 0x1C00001C: "nca_s_invalid_pres_context_id", 

799 0x1C00001D: "nca_s_unsupported_authn_level", 

800 0x1C00001F: "nca_s_invalid_checksum", 

801 0x1C000020: "nca_s_invalid_crc", 

802 0x1C000021: "nca_s_fault_user_defined", 

803 0x1C000022: "nca_s_fault_tx_open_failed", 

804 0x1C000023: "nca_s_fault_codeset_conv_error", 

805 0x1C000024: "nca_s_fault_object_not_found", 

806 0x1C000025: "nca_s_fault_no_client_stub", 

807 # [MS-ERREF] 

808 0x000006D3: "RPC_S_UNKNOWN_AUTHN_SERVICE", 

809 0x000006D8: "EPT_S_CANT_PERFORM_OP", 

810 0x000006F7: "RPC_X_BAD_STUB_DATA", 

811 0x00000719: "RPC_S_NO_INTERFACES", 

812 0x0000071A: "RPC_S_CALL_CANCELLED", 

813 0x0000071B: "RPC_S_BINDING_INCOMPLETE", 

814 0x0000071C: "RPC_S_COMM_FAILURE", 

815 0x0000071D: "RPC_S_UNSUPPORTED_AUTHN_LEVEL", 

816 0x0000071E: "RPC_S_NO_PRINC_NAME", 

817 0x0000071F: "RPC_S_NOT_RPC_ERROR", 

818 0x00000720: "RPC_S_UUID_LOCAL_ONLY", 

819 0x00000721: "RPC_S_SEC_PKG_ERROR", 

820 0x00000722: "RPC_S_NOT_CANCELLED", 

821 0x0000076A: "RPC_S_GROUP_MEMBER_NOT_FOUND", 

822 0x0000076C: "RPC_S_INVALID_OBJECT", 

823 0x80004002: "E_NOINTERFACE", 

824 0x80010107: "RPC_E_INVALIDMETHOD", 

825 0x80010108: "RPC_E_DISCONNECTED", 

826 0x80010109: "RPC_E_RETRY", 

827 0x80040153: "REGDB_E_INVALIDVALUE", 

828 0x80040154: "REGDB_E_CLASSNOTREG", 

829 0x80040155: "REGDB_E_IIDNOTREG", 

830 0x800706F7: "COM_X_BAD_STUB_DATA", 

831} 

832 

833_DCE_RPC_REJECTION_REASONS = { 

834 0: "REASON_NOT_SPECIFIED", 

835 1: "TEMPORARY_CONGESTION", 

836 2: "LOCAL_LIMIT_EXCEEDED", 

837 3: "CALLED_PADDR_UNKNOWN", 

838 4: "PROTOCOL_VERSION_NOT_SUPPORTED", 

839 5: "DEFAULT_CONTEXT_NOT_SUPPORTED", 

840 6: "USER_DATA_NOT_READABLE", 

841 7: "NO_PSAP_AVAILABLE", 

842 8: "AUTHENTICATION_TYPE_NOT_RECOGNIZED", 

843 9: "INVALID_CHECKSUM", 

844} 

845 

846 

847class DceRpc5(DceRpc): 

848 """ 

849 DCE/RPC v5 'connection-oriented' packet 

850 """ 

851 

852 name = "DCE/RPC v5" 

853 fields_desc = ( 

854 [ 

855 ByteEnumField( 

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

857 ), 

858 ByteField("rpc_vers_minor", 0), 

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

860 MultipleTypeField( 

861 # [MS-RPCE] sect 2.2.2.3 

862 [ 

863 ( 

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

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

866 ) 

867 ], 

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

869 ), 

870 ] 

871 + _drep 

872 + [ 

873 ByteField("reserved2", 0), 

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

875 _EField( 

876 FieldLenField( 

877 "auth_len", 

878 None, 

879 fmt="H", 

880 length_of="auth_verifier", 

881 adjust=lambda _, x: 0 if not x else (x - 8), 

882 ) 

883 ), 

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

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

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

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

888 # - auth_verifier includes sec_trailer + the authentication token 

889 # - auth_padding is the authentication padding 

890 # - vt_trailer is the verification trailer 

891 ConditionalField( 

892 TrailerField( 

893 PacketLenField( 

894 "auth_verifier", 

895 None, 

896 CommonAuthVerifier, 

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

898 ) 

899 ), 

900 lambda pkt: pkt.auth_len != 0, 

901 ), 

902 ConditionalField( 

903 TrailerField( 

904 StrLenField( 

905 "auth_padding", 

906 None, 

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

908 ) 

909 ), 

910 lambda pkt: pkt.auth_len != 0, 

911 ), 

912 TrailerField( 

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

914 ), 

915 ] 

916 ) 

917 

918 def do_dissect(self, s): 

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

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

921 # packets are concatenated 

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

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

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

925 

926 def extract_padding(self, s): 

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

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

929 # creating the next fragment, etc. 

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

931 return s[:pay_len], s[pay_len:] 

932 

933 def post_build(self, pkt, pay): 

934 if ( 

935 self.auth_verifier 

936 and self.auth_padding is None 

937 and self.auth_verifier.auth_pad_length is None 

938 ): 

939 # Compute auth_len and add padding 

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

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

942 pdu_len = len(pay) 

943 if self.payload: 

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

945 padlen = (-pdu_len) % _COMMON_AUTH_PAD 

946 auth_verifier = ( 

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

948 ) 

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

950 if self.frag_len is None: 

951 # Compute frag_len 

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

953 pkt = ( 

954 pkt[:8] 

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

956 + pkt[10:] 

957 ) 

958 return pkt + pay 

959 

960 def answers(self, pkt): 

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

962 

963 @classmethod 

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

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

966 return 

967 if len(data) < 10: 

968 return 

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

970 if endian not in [0, 1]: 

971 return 

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

973 if len(data) >= length: 

974 if conf.dcerpc_session_enable: 

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

976 if "dcerpcsess" not in session: 

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

978 else: 

979 dcerpcsess = session["dcerpcsess"] 

980 return dcerpcsess.process(DceRpc5(data)) 

981 return DceRpc5(data) 

982 

983 

984# sec 12.6.3.1 

985 

986 

987class DceRpc5AbstractSyntax(EPacket): 

988 name = "Presentation Syntax (p_syntax_id_t)" 

989 fields_desc = [ 

990 _EField( 

991 UUIDEnumField( 

992 "if_uuid", 

993 None, 

994 ( 

995 # Those are dynamic 

996 DCE_RPC_INTERFACES_NAMES.get, 

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

998 ), 

999 ) 

1000 ), 

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

1002 ] 

1003 

1004 

1005class DceRpc5TransferSyntax(EPacket): 

1006 name = "Presentation Transfer Syntax (p_syntax_id_t)" 

1007 fields_desc = [ 

1008 _EField( 

1009 UUIDEnumField( 

1010 "if_uuid", 

1011 None, 

1012 DCE_RPC_TRANSFER_SYNTAXES, 

1013 ) 

1014 ), 

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

1016 ] 

1017 

1018 

1019class DceRpc5Context(EPacket): 

1020 name = "Presentation Context (p_cont_elem_t)" 

1021 fields_desc = [ 

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

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

1024 ByteField("reserved", 0), 

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

1026 EPacketListField( 

1027 "transfer_syntaxes", 

1028 None, 

1029 DceRpc5TransferSyntax, 

1030 count_from=lambda pkt: pkt.n_transfer_syn, 

1031 endianness_from=_dce_rpc_endianness, 

1032 ), 

1033 ] 

1034 

1035 

1036class DceRpc5Result(EPacket): 

1037 name = "Context negotiation Result" 

1038 fields_desc = [ 

1039 _EField( 

1040 ShortEnumField( 

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

1042 ) 

1043 ), 

1044 _EField( 

1045 ShortEnumField( 

1046 "reason", 

1047 0, 

1048 _DCE_RPC_REJECTION_REASONS, 

1049 ) 

1050 ), 

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

1052 ] 

1053 

1054 

1055class DceRpc5PortAny(EPacket): 

1056 name = "Port Any (port_any_t)" 

1057 fields_desc = [ 

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

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

1060 ] 

1061 

1062 

1063# sec 12.6.4.3 

1064 

1065 

1066class DceRpc5Bind(_DceRpcPayload): 

1067 name = "DCE/RPC v5 - Bind" 

1068 fields_desc = [ 

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

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

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

1072 # p_cont_list_t 

1073 _EField( 

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

1075 ), 

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

1077 EPacketListField( 

1078 "context_elem", 

1079 [], 

1080 DceRpc5Context, 

1081 endianness_from=_dce_rpc_endianness, 

1082 count_from=lambda pkt: pkt.n_context_elem, 

1083 ), 

1084 ] 

1085 

1086 

1087bind_layers(DceRpc5, DceRpc5Bind, ptype=11) 

1088 

1089# sec 12.6.4.4 

1090 

1091 

1092class DceRpc5BindAck(_DceRpcPayload): 

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

1094 fields_desc = [ 

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

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

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

1098 PadField( 

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

1100 align=4, 

1101 ), 

1102 # p_result_list_t 

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

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

1105 EPacketListField( 

1106 "results", 

1107 [], 

1108 DceRpc5Result, 

1109 endianness_from=_dce_rpc_endianness, 

1110 count_from=lambda pkt: pkt.n_results, 

1111 ), 

1112 ] 

1113 

1114 

1115bind_layers(DceRpc5, DceRpc5BindAck, ptype=12) 

1116 

1117# sec 12.6.4.5 

1118 

1119 

1120class DceRpc5Version(EPacket): 

1121 name = "version_t" 

1122 fields_desc = [ 

1123 ByteField("major", 0), 

1124 ByteField("minor", 0), 

1125 ] 

1126 

1127 

1128class DceRpc5BindNak(_DceRpcPayload): 

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

1130 fields_desc = [ 

1131 _EField( 

1132 ShortEnumField("provider_reject_reason", 0, _DCE_RPC_REJECTION_REASONS) 

1133 ), 

1134 # p_rt_versions_supported_t 

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

1136 EPacketListField( 

1137 "protocols", 

1138 [], 

1139 DceRpc5Version, 

1140 count_from=lambda pkt: pkt.n_protocols, 

1141 endianness_from=_dce_rpc_endianness, 

1142 ), 

1143 # [MS-RPCE] sect 2.2.2.9 

1144 ConditionalField( 

1145 ReversePadField( 

1146 _EField( 

1147 UUIDEnumField( 

1148 "signature", 

1149 None, 

1150 { 

1151 UUID( 

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

1153 ): "Extended Error", 

1154 }, 

1155 ) 

1156 ), 

1157 align=8, 

1158 ), 

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

1160 or ( 

1161 pkt.underlayer 

1162 and pkt.n_protocols 

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

1164 ), 

1165 ), 

1166 ] 

1167 

1168 

1169bind_layers(DceRpc5, DceRpc5BindNak, ptype=13) 

1170 

1171 

1172# sec 12.6.4.1 

1173 

1174 

1175class DceRpc5AlterContext(_DceRpcPayload): 

1176 name = "DCE/RPC v5 - AlterContext" 

1177 fields_desc = DceRpc5Bind.fields_desc 

1178 

1179 

1180bind_layers(DceRpc5, DceRpc5AlterContext, ptype=14) 

1181 

1182 

1183# sec 12.6.4.2 

1184 

1185 

1186class DceRpc5AlterContextResp(_DceRpcPayload): 

1187 name = "DCE/RPC v5 - AlterContextResp" 

1188 fields_desc = DceRpc5BindAck.fields_desc 

1189 

1190 

1191bind_layers(DceRpc5, DceRpc5AlterContextResp, ptype=15) 

1192 

1193# [MS-RPCE] sect 2.2.2.10 - rpc_auth_3 

1194 

1195 

1196class DceRpc5Auth3(Packet): 

1197 name = "DCE/RPC v5 - Auth3" 

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

1199 

1200 

1201bind_layers(DceRpc5, DceRpc5Auth3, ptype=16) 

1202 

1203# sec 12.6.4.7 

1204 

1205 

1206class DceRpc5Fault(_DceRpcPayload): 

1207 name = "DCE/RPC v5 - Fault" 

1208 fields_desc = [ 

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

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

1211 ByteField("cancel_count", 0), 

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

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

1214 IntField("reserved2", 0), 

1215 ] 

1216 

1217 

1218bind_layers(DceRpc5, DceRpc5Fault, ptype=3) 

1219 

1220 

1221# sec 12.6.4.9 

1222 

1223 

1224class DceRpc5Request(_DceRpcPayload): 

1225 name = "DCE/RPC v5 - Request" 

1226 fields_desc = [ 

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

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

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

1230 ConditionalField( 

1231 PadField( 

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

1233 align=8, 

1234 ), 

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

1236 ), 

1237 ] 

1238 

1239 

1240bind_layers(DceRpc5, DceRpc5Request, ptype=0) 

1241 

1242# sec 12.6.4.10 

1243 

1244 

1245class DceRpc5Response(_DceRpcPayload): 

1246 name = "DCE/RPC v5 - Response" 

1247 fields_desc = [ 

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

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

1250 ByteField("cancel_count", 0), 

1251 ByteField("reserved", 0), 

1252 ] 

1253 

1254 

1255bind_layers(DceRpc5, DceRpc5Response, ptype=2) 

1256 

1257# --- API 

1258 

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

1260DCE_RPC_INTERFACES = {} 

1261 

1262 

1263class DceRpcInterface: 

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

1265 self.name = name 

1266 self.uuid = uuid 

1267 self.major_version, self.minor_version = version_tuple 

1268 self.if_version = if_version 

1269 self.opnums = opnums 

1270 

1271 def __repr__(self): 

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

1273 self.name, 

1274 self.major_version, 

1275 self.minor_version, 

1276 ) 

1277 

1278 

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

1280 """ 

1281 Register a DCE/RPC interface 

1282 """ 

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

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

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

1286 if (uuid, if_version) in DCE_RPC_INTERFACES: 

1287 # Interface is already registered. 

1288 interface = DCE_RPC_INTERFACES[(uuid, if_version)] 

1289 if interface.name == name: 

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

1291 # Interface is an extension of a previous interface 

1292 interface.opnums.update(opnums) 

1293 else: 

1294 log_runtime.warning( 

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

1296 ) 

1297 return 

1298 else: 

1299 raise ValueError( 

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

1301 ) 

1302 else: 

1303 # New interface 

1304 DCE_RPC_INTERFACES_NAMES[uuid] = name 

1305 DCE_RPC_INTERFACES_NAMES_rev[name.lower()] = uuid 

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

1307 name, 

1308 uuid, 

1309 version_tuple, 

1310 if_version, 

1311 opnums, 

1312 ) 

1313 

1314 # bind for build 

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

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

1317 operations.request.opnum = opnum 

1318 operations.request.intf = uuid 

1319 

1320 

1321def find_dcerpc_interface(name) -> DceRpcInterface: 

1322 """ 

1323 Find an interface object through the name in the IDL 

1324 """ 

1325 try: 

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

1327 except StopIteration: 

1328 raise AttributeError("Unknown interface !") 

1329 

1330 

1331COM_INTERFACES = {} 

1332 

1333 

1334class ComInterface: 

1335 if_version = 0 

1336 

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

1338 self.name = name 

1339 self.uuid = uuid 

1340 self.opnums = opnums 

1341 

1342 def __repr__(self): 

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

1344 

1345 

1346def register_com_interface(name, uuid, opnums): 

1347 """ 

1348 Register a COM interface 

1349 """ 

1350 COM_INTERFACES[uuid] = ComInterface( 

1351 name, 

1352 uuid, 

1353 opnums, 

1354 ) 

1355 # bind for build 

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

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

1358 COM_INTERFACES_NAMES[uuid] = name 

1359 COM_INTERFACES_NAMES_rev[name.lower()] = uuid 

1360 

1361 

1362def find_com_interface(name) -> ComInterface: 

1363 """ 

1364 Find an interface object through the name in the IDL 

1365 """ 

1366 try: 

1367 return next(x for x in COM_INTERFACES.values() if x.name == name) 

1368 except StopIteration: 

1369 raise AttributeError("Unknown interface !") 

1370 

1371 

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

1373 

1374 

1375def _set_ctx_on(f, obj): 

1376 if isinstance(f, _NDRPacket): 

1377 f.ndr64 = obj.ndr64 

1378 f.ndrendian = obj.ndrendian 

1379 if isinstance(f, list): 

1380 for x in f: 

1381 if isinstance(x, _NDRPacket): 

1382 x.ndr64 = obj.ndr64 

1383 x.ndrendian = obj.ndrendian 

1384 

1385 

1386def _e(ndrendian): 

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

1388 

1389 

1390class _NDRPacket(Packet): 

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

1392 

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

1394 self.ndr64 = kwargs.pop("ndr64", conf.ndr64) 

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

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

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

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

1399 self.deferred_pointers = [] 

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

1401 

1402 def do_dissect(self, s): 

1403 _up = self.parent or self.underlayer 

1404 if _up and isinstance(_up, _NDRPacket): 

1405 self.ndr64 = _up.ndr64 

1406 self.ndrendian = _up.ndrendian 

1407 else: 

1408 # See comment above NDRConstructedType 

1409 return NDRConstructedType([]).read_deferred_pointers( 

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

1411 ) 

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

1413 

1414 def post_dissect(self, s): 

1415 if self.deferred_pointers: 

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

1417 self.raw_packet_cache = None 

1418 return s 

1419 

1420 def do_build(self): 

1421 _up = self.parent or self.underlayer 

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

1423 _set_ctx_on(f, self) 

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

1425 # See comment above NDRConstructedType 

1426 return NDRConstructedType([]).add_deferred_pointers( 

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

1428 ) 

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

1430 

1431 def default_payload_class(self, pkt): 

1432 return conf.padding_layer 

1433 

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

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

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

1437 # on build. 

1438 pkt.deferred_pointers = self.deferred_pointers 

1439 pkt.ndr64 = self.ndr64 

1440 pkt.ndrendian = self.ndrendian 

1441 return pkt 

1442 

1443 def copy(self): 

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

1445 pkt.deferred_pointers = self.deferred_pointers 

1446 pkt.ndr64 = self.ndr64 

1447 pkt.ndrendian = self.ndrendian 

1448 return pkt 

1449 

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

1451 return self.__class__( 

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

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

1454 

1455 def getfield_and_val(self, attr): 

1456 try: 

1457 return Packet.getfield_and_val(self, attr) 

1458 except ValueError: 

1459 if self.request_packet: 

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

1461 try: 

1462 return self.request_packet.getfield_and_val(attr) 

1463 except AttributeError: 

1464 pass 

1465 raise 

1466 

1467 def valueof(self, request: str): 

1468 """ 

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

1470 """ 

1471 val = self 

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

1473 fld, fval = val.getfield_and_val(ndr_field) 

1474 val = fld.valueof(val, fval) 

1475 return val 

1476 

1477 

1478class _NDRAlign: 

1479 def padlen(self, flen, pkt): 

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

1481 

1482 def original_length(self, pkt): 

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

1484 while pkt: 

1485 par = pkt.parent or pkt.underlayer 

1486 if par and isinstance(par, _NDRPacket): 

1487 pkt = par 

1488 else: 

1489 break 

1490 return len(pkt.original) 

1491 

1492 

1493class NDRAlign(_NDRAlign, ReversePadField): 

1494 """ 

1495 ReversePadField modified to fit NDR. 

1496 

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

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

1499 """ 

1500 

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

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

1503 

1504 

1505class _VirtualField(Field): 

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

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

1508 return s 

1509 

1510 def getfield(self, pkt, s): 

1511 return s, None 

1512 

1513 

1514class _NDRPacketMetaclass(Packet_metaclass): 

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

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

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

1518 if conformants: 

1519 amount = len(conformants) 

1520 if amount == 1: 

1521 newcls.fields_desc.insert( 

1522 0, 

1523 _VirtualField("max_count", None), 

1524 ) 

1525 else: 

1526 newcls.fields_desc.insert( 

1527 0, 

1528 FieldListField( 

1529 "max_counts", 

1530 [], 

1531 _VirtualField("", 0), 

1532 count_from=lambda _: amount, 

1533 ), 

1534 ) 

1535 return newcls # type: ignore 

1536 

1537 

1538class NDRPacket(_NDRPacket, metaclass=_NDRPacketMetaclass): 

1539 """ 

1540 A NDR Packet. Handles pointer size & endianness 

1541 """ 

1542 

1543 __slots__ = ["_align"] 

1544 

1545 # NDR64 pad structures 

1546 # [MS-RPCE] 2.2.5.3.4.1 

1547 ALIGNMENT = (1, 1) 

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

1549 DEPORTED_CONFORMANTS = [] 

1550 

1551 

1552# Primitive types 

1553 

1554 

1555class _NDRValueOf: 

1556 def valueof(self, pkt, x): 

1557 return x 

1558 

1559 

1560class _NDRLenField(_NDRValueOf, Field): 

1561 """ 

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

1563 and take the value of a size on build. 

1564 """ 

1565 

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

1567 

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

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

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

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

1572 

1573 def i2m(self, pkt, x): 

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

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

1576 f = fld.i2len(pkt, fval) 

1577 x = self.adjust(pkt, f) 

1578 elif x is None: 

1579 x = 0 

1580 return x 

1581 

1582 

1583class NDRByteField(_NDRLenField, ByteField): 

1584 pass 

1585 

1586 

1587class NDRSignedByteField(_NDRLenField, SignedByteField): 

1588 pass 

1589 

1590 

1591class _NDRField(_NDRLenField): 

1592 FMT = "" 

1593 ALIGN = (0, 0) 

1594 

1595 def getfield(self, pkt, s): 

1596 return NDRAlign( 

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

1598 ).getfield(pkt, s) 

1599 

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

1601 return NDRAlign( 

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

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

1604 

1605 

1606class NDRShortField(_NDRField): 

1607 FMT = "H" 

1608 ALIGN = (2, 2) 

1609 

1610 

1611class NDRSignedShortField(_NDRField): 

1612 FMT = "h" 

1613 ALIGN = (2, 2) 

1614 

1615 

1616class NDRIntField(_NDRField): 

1617 FMT = "I" 

1618 ALIGN = (4, 4) 

1619 

1620 

1621class NDRSignedIntField(_NDRField): 

1622 FMT = "i" 

1623 ALIGN = (4, 4) 

1624 

1625 

1626class NDRLongField(_NDRField): 

1627 FMT = "Q" 

1628 ALIGN = (8, 8) 

1629 

1630 

1631class NDRSignedLongField(_NDRField): 

1632 FMT = "q" 

1633 ALIGN = (8, 8) 

1634 

1635 

1636class NDRIEEEFloatField(_NDRField): 

1637 FMT = "f" 

1638 ALIGN = (4, 4) 

1639 

1640 

1641class NDRIEEEDoubleField(_NDRField): 

1642 FMT = "d" 

1643 ALIGN = (8, 8) 

1644 

1645 

1646# Enum types 

1647 

1648 

1649class _NDREnumField(_NDRValueOf, EnumField): 

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

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

1652 

1653 def getfield(self, pkt, s): 

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

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

1656 

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

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

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

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

1661 ) 

1662 

1663 

1664class NDRInt3264EnumField(NDRAlign): 

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

1666 super(NDRInt3264EnumField, self).__init__( 

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

1668 ) 

1669 

1670 

1671class NDRIntEnumField(_NDRValueOf, NDRAlign): 

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

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

1674 super(NDRIntEnumField, self).__init__( 

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

1676 ) 

1677 

1678 

1679# Special types 

1680 

1681 

1682class NDRInt3264Field(_NDRLenField): 

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

1684 

1685 def getfield(self, pkt, s): 

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

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

1688 

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

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

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

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

1693 ) 

1694 

1695 

1696class NDRSignedInt3264Field(NDRInt3264Field): 

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

1698 

1699 

1700# Pointer types 

1701 

1702 

1703class NDRPointer(_NDRPacket): 

1704 fields_desc = [ 

1705 MultipleTypeField( 

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

1707 XLEIntField("referent_id", 1), 

1708 ), 

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

1710 ] 

1711 

1712 

1713class NDRFullPointerField(_FieldContainer): 

1714 """ 

1715 A NDR Full/Unique pointer field encapsulation. 

1716 

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

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

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

1720 """ 

1721 

1722 EMBEDDED = False 

1723 EMBEDDED_REF = False 

1724 

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

1726 self.fld = fld 

1727 self.ref = ref 

1728 self.default = None 

1729 

1730 def getfield(self, pkt, s): 

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

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

1733 pkt, s 

1734 ) 

1735 

1736 # No value 

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

1738 return remain, None 

1739 

1740 # With value 

1741 if self.EMBEDDED: 

1742 # deferred 

1743 ptr = NDRPointer( 

1744 ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, referent_id=referent_id 

1745 ) 

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

1747 return remain, ptr 

1748 

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

1750 return remain, NDRPointer( 

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

1752 ) 

1753 

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

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

1756 raise ValueError( 

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

1758 ) 

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

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

1761 

1762 # No value 

1763 if val is None and not self.EMBEDDED_REF: 

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

1765 

1766 # With value 

1767 _set_ctx_on(val.value, pkt) 

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

1769 if self.EMBEDDED: 

1770 # deferred 

1771 pkt.deferred_pointers.append( 

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

1773 ) 

1774 return s 

1775 

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

1777 

1778 def any2i(self, pkt, x): 

1779 # User-friendly helper 

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

1781 return NDRPointer( 

1782 referent_id=0x20000, 

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

1784 ) 

1785 return x 

1786 

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

1788 def i2repr(self, pkt, val): 

1789 return repr(val) 

1790 

1791 def i2h(self, pkt, x): 

1792 return x 

1793 

1794 def h2i(self, pkt, x): 

1795 return x 

1796 

1797 def i2len(self, pkt, x): 

1798 if x is None: 

1799 return 0 

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

1801 

1802 def valueof(self, pkt, x): 

1803 if x is None: 

1804 return x 

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

1806 

1807 

1808class NDRFullEmbPointerField(NDRFullPointerField): 

1809 """ 

1810 A NDR Embedded Full pointer. 

1811 

1812 Same as NDRFullPointerField with EMBEDDED = True. 

1813 """ 

1814 

1815 EMBEDDED = True 

1816 

1817 

1818class NDRRefEmbPointerField(NDRFullPointerField): 

1819 """ 

1820 A NDR Embedded Reference pointer. 

1821 

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

1823 """ 

1824 

1825 EMBEDDED = True 

1826 EMBEDDED_REF = True 

1827 

1828 

1829# Constructed types 

1830 

1831 

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

1833 

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

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

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

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

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

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

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

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

1842 

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

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

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

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

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

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

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

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

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

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

1853 

1854 

1855class NDRConstructedType(object): 

1856 def __init__(self, fields): 

1857 self.handles_deferred = False 

1858 self.ndr_fields = fields 

1859 self.rec_check_deferral() 

1860 

1861 def rec_check_deferral(self): 

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

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

1864 # and make all sub-constructed types not. 

1865 for f in self.ndr_fields: 

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

1867 self.handles_deferred = True 

1868 if isinstance(f, NDRConstructedType): 

1869 f.rec_check_deferral() 

1870 if f.handles_deferred: 

1871 self.handles_deferred = True 

1872 f.handles_deferred = False 

1873 

1874 def getfield(self, pkt, s): 

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

1876 if isinstance(fval, _NDRPacket): 

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

1878 # pass it to parent packet to propagate. 

1879 pkt.deferred_pointers.extend(fval.deferred_pointers) 

1880 del fval.deferred_pointers[:] 

1881 if self.handles_deferred: 

1882 # This field handles deferral ! 

1883 s = self.read_deferred_pointers(pkt, s) 

1884 return s, fval 

1885 

1886 def read_deferred_pointers(self, pkt, s): 

1887 # Now read content of the pointers that were deferred 

1888 q = collections.deque() 

1889 q.extend(pkt.deferred_pointers) 

1890 del pkt.deferred_pointers[:] 

1891 while q: 

1892 # Recursively resolve pointers that were deferred 

1893 ptr, getfld = q.popleft() 

1894 s, val = getfld(s) 

1895 ptr.value = val 

1896 if isinstance(val, _NDRPacket): 

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

1898 q.extend(val.deferred_pointers) 

1899 del val.deferred_pointers[:] 

1900 return s 

1901 

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

1903 try: 

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

1905 except Exception as ex: 

1906 try: 

1907 ex.args = ( 

1908 "While building field '%s': " % self.name + ex.args[0], 

1909 ) + ex.args[1:] 

1910 except (AttributeError, IndexError): 

1911 pass 

1912 raise ex 

1913 if isinstance(val, _NDRPacket): 

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

1915 # pass it to parent packet to propagate. 

1916 pkt.deferred_pointers.extend(val.deferred_pointers) 

1917 del val.deferred_pointers[:] 

1918 if self.handles_deferred: 

1919 # This field handles deferral ! 

1920 s = self.add_deferred_pointers(pkt, s) 

1921 return s 

1922 

1923 def add_deferred_pointers(self, pkt, s): 

1924 # Now add content of pointers that were deferred 

1925 q = collections.deque() 

1926 q.extend(pkt.deferred_pointers) 

1927 del pkt.deferred_pointers[:] 

1928 while q: 

1929 addfld, fval = q.popleft() 

1930 s = addfld(s) 

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

1932 q.extend(fval.value.deferred_pointers) 

1933 del fval.value.deferred_pointers[:] 

1934 return s 

1935 

1936 

1937class _NDRPacketField(_NDRValueOf, PacketField): 

1938 def m2i(self, pkt, m): 

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

1940 

1941 

1942class _NDRPacketPadField(PadField): 

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

1944 # Structures have extra alignment/padding in NDR64. 

1945 def padlen(self, flen, pkt): 

1946 if pkt.ndr64: 

1947 return -flen % self._align[1] 

1948 else: 

1949 return 0 

1950 

1951 

1952class NDRPacketField(NDRConstructedType, NDRAlign): 

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

1954 self.DEPORTED_CONFORMANTS = pkt_cls.DEPORTED_CONFORMANTS 

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

1956 

1957 # The inner _NDRPacketPadField handles NDR64's trailing gap in 

1958 # the case where there a no inner conformants (see [MS-RPCE] 2.2.5.3.4.1) 

1959 if self.DEPORTED_CONFORMANTS: 

1960 innerfld = self.fld 

1961 else: 

1962 innerfld = _NDRPacketPadField(self.fld, align=pkt_cls.ALIGNMENT) 

1963 

1964 # C706 14.3.2 Alignment of Constructed Types is handled by the 

1965 # NDRAlign below. 

1966 NDRAlign.__init__( 

1967 self, 

1968 innerfld, 

1969 align=pkt_cls.ALIGNMENT, 

1970 ) 

1971 NDRConstructedType.__init__(self, pkt_cls.fields_desc) 

1972 

1973 def getfield(self, pkt, x): 

1974 # Handle deformed conformants max_count here 

1975 if self.DEPORTED_CONFORMANTS: 

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

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

1978 fld = NDRInt3264Field("", 0) 

1979 max_counts = [] 

1980 for _ in self.DEPORTED_CONFORMANTS: 

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

1982 max_counts.append(max_count) 

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

1984 if len(max_counts) == 1: 

1985 val.max_count = max_counts[0] 

1986 else: 

1987 val.max_counts = max_counts 

1988 return res, val 

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

1990 

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

1992 # Handle deformed conformants max_count here 

1993 if self.DEPORTED_CONFORMANTS: 

1994 mcfld = NDRInt3264Field("", 0) 

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

1996 max_counts = [x.max_count] 

1997 else: 

1998 max_counts = x.max_counts 

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

2000 if max_count is None: 

2001 fld, val = x.getfield_and_val(fldname) 

2002 max_count = fld.i2len(x, val) 

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

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

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

2006 

2007 

2008# Array types 

2009 

2010 

2011class _NDRPacketListField(NDRConstructedType, PacketListField): 

2012 """ 

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

2014 """ 

2015 

2016 islist = 1 

2017 holds_packets = 1 

2018 

2019 __slots__ = ["ptr_lvl", "fld"] 

2020 

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

2022 self.ptr_lvl = kwargs.pop("ptr_lvl", False) 

2023 if self.ptr_lvl: 

2024 # TODO: support more than 1 level ? 

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

2026 else: 

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

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

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

2030 

2031 def m2i(self, pkt, s): 

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

2033 if val is None: 

2034 val = NDRNone() 

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

2036 # which breaks pointer defferal. Same applies elsewhere 

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

2038 return val 

2039 

2040 def any2i(self, pkt, x): 

2041 # User-friendly helper 

2042 if isinstance(x, list): 

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

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

2045 

2046 def i2m(self, pkt, val): 

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

2048 

2049 def i2len(self, pkt, x): 

2050 return len(x) 

2051 

2052 def valueof(self, pkt, x): 

2053 return [ 

2054 self.fld.valueof(pkt, y if not isinstance(y, NDRNone) else None) for y in x 

2055 ] 

2056 

2057 

2058class NDRFieldListField(NDRConstructedType, FieldListField): 

2059 """ 

2060 A FieldListField for NDR 

2061 """ 

2062 

2063 islist = 1 

2064 

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

2066 if "length_is" in kwargs: 

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

2068 elif "size_is" in kwargs: 

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

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

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

2072 

2073 def i2len(self, pkt, x): 

2074 return len(x) 

2075 

2076 def valueof(self, pkt, x): 

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

2078 

2079 

2080class NDRVaryingArray(_NDRPacket): 

2081 fields_desc = [ 

2082 MultipleTypeField( 

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

2084 LEIntField("offset", 0), 

2085 ), 

2086 MultipleTypeField( 

2087 [ 

2088 ( 

2089 LELongField("actual_count", None), 

2090 lambda pkt: pkt and pkt.ndr64, 

2091 ) 

2092 ], 

2093 LEIntField("actual_count", None), 

2094 ), 

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

2096 ] 

2097 

2098 

2099class _NDRVarField: 

2100 """ 

2101 NDR Varying Array / String field 

2102 """ 

2103 

2104 LENGTH_FROM = False 

2105 COUNT_FROM = False 

2106 

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

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

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

2110 # length_is field. 

2111 if "length_is" in kwargs: 

2112 _length_is = kwargs.pop("length_is") 

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

2114 else: 

2115 length_is = lambda pkt: pkt.actual_count 

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

2117 if self.LENGTH_FROM: 

2118 kwargs["length_from"] = length_is 

2119 elif self.COUNT_FROM: 

2120 kwargs["count_from"] = length_is 

2121 # TODO: For now, we do nothing with max_is 

2122 if "max_is" in kwargs: 

2123 kwargs.pop("max_is") 

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

2125 

2126 def getfield(self, pkt, s): 

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

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

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

2130 pkt, remain 

2131 ) 

2132 final = NDRVaryingArray( 

2133 ndr64=pkt.ndr64, 

2134 ndrendian=pkt.ndrendian, 

2135 offset=offset, 

2136 actual_count=actual_count, 

2137 _underlayer=pkt, 

2138 ) 

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

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

2141 return remain, final 

2142 

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

2144 if not isinstance(val, NDRVaryingArray): 

2145 raise ValueError( 

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

2147 ) 

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

2149 _set_ctx_on(val.value, pkt) 

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

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

2152 pkt, 

2153 s, 

2154 val.actual_count is None 

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

2156 or val.actual_count, 

2157 ) 

2158 return super(_NDRVarField, self).addfield( 

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

2160 ) 

2161 

2162 def i2len(self, pkt, x): 

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

2164 

2165 def any2i(self, pkt, x): 

2166 # User-friendly helper 

2167 if not isinstance(x, NDRVaryingArray): 

2168 return NDRVaryingArray( 

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

2170 ) 

2171 return x 

2172 

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

2174 def i2repr(self, pkt, val): 

2175 return repr(val) 

2176 

2177 def i2h(self, pkt, x): 

2178 return x 

2179 

2180 def h2i(self, pkt, x): 

2181 return x 

2182 

2183 def valueof(self, pkt, x): 

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

2185 

2186 

2187class NDRConformantArray(_NDRPacket): 

2188 fields_desc = [ 

2189 MultipleTypeField( 

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

2191 LEIntField("max_count", None), 

2192 ), 

2193 MultipleTypeField( 

2194 [ 

2195 ( 

2196 PacketListField( 

2197 "value", 

2198 [], 

2199 conf.raw_layer, 

2200 count_from=lambda pkt: pkt.max_count, 

2201 ), 

2202 ( 

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

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

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

2206 ), 

2207 ) 

2208 ], 

2209 FieldListField( 

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

2211 ), 

2212 ), 

2213 ] 

2214 

2215 

2216class NDRConformantString(_NDRPacket): 

2217 fields_desc = [ 

2218 MultipleTypeField( 

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

2220 LEIntField("max_count", None), 

2221 ), 

2222 StrField("value", ""), 

2223 ] 

2224 

2225 

2226class _NDRConfField: 

2227 """ 

2228 NDR Conformant Array / String field 

2229 """ 

2230 

2231 CONFORMANT_STRING = False 

2232 LENGTH_FROM = False 

2233 COUNT_FROM = False 

2234 

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

2236 # when conformant_in_struct is True, we remove the level of abstraction 

2237 # provided by NDRConformantString / NDRConformantArray because max_count 

2238 # is a proper field in the parent packet. 

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

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

2241 if "size_is" in kwargs: 

2242 size_is = kwargs.pop("size_is") 

2243 if self.LENGTH_FROM: 

2244 kwargs["length_from"] = size_is 

2245 elif self.COUNT_FROM: 

2246 kwargs["count_from"] = size_is 

2247 # TODO: For now, we do nothing with max_is 

2248 if "max_is" in kwargs: 

2249 kwargs.pop("max_is") 

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

2251 

2252 def getfield(self, pkt, s): 

2253 # [C706] - 14.3.7 Structures Containing Arrays 

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

2255 if self.conformant_in_struct: 

2256 # [MS-RPCE] 2.2.5.3.4.2 Structure Containing a Conformant Array 

2257 # Padding is here: just before the Conformant content 

2258 return NDRAlign( 

2259 super(_NDRConfField, self), 

2260 align=pkt.ALIGNMENT, 

2261 ).getfield(pkt, s) 

2262 

2263 # The max count is aligned as a primitive type 

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

2265 pkt, s 

2266 ) 

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

2268 return remain, ( 

2269 NDRConformantString if self.CONFORMANT_STRING else NDRConformantArray 

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

2271 

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

2273 if self.conformant_in_struct: 

2274 # [MS-RPCE] 2.2.5.3.4.2 Structure Containing a Conformant Array 

2275 # Padding is here: just before the Conformant content 

2276 return NDRAlign(super(_NDRConfField, self), align=pkt.ALIGNMENT).addfield( 

2277 pkt, s, val 

2278 ) 

2279 

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

2281 raise ValueError( 

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

2283 % self.name 

2284 ) 

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

2286 raise ValueError( 

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

2288 ) 

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

2290 _set_ctx_on(val.value, pkt) 

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

2292 value = val.value[0] 

2293 else: 

2294 value = val.value 

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

2296 pkt, 

2297 s, 

2298 val.max_count is None 

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

2300 or val.max_count, 

2301 ) 

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

2303 

2304 def _subval(self, x): 

2305 if self.conformant_in_struct: 

2306 value = x 

2307 elif ( 

2308 not self.CONFORMANT_STRING 

2309 and x.value 

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

2311 ): 

2312 value = x.value[0] 

2313 else: 

2314 value = x.value 

2315 return value 

2316 

2317 def i2len(self, pkt, x): 

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

2319 

2320 def any2i(self, pkt, x): 

2321 # User-friendly helper 

2322 if self.conformant_in_struct: 

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

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

2325 return NDRConformantString( 

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

2327 ) 

2328 elif not isinstance(x, NDRConformantArray): 

2329 return NDRConformantArray( 

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

2331 ) 

2332 return x 

2333 

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

2335 def i2repr(self, pkt, val): 

2336 return repr(val) 

2337 

2338 def i2h(self, pkt, x): 

2339 return x 

2340 

2341 def h2i(self, pkt, x): 

2342 return x 

2343 

2344 def valueof(self, pkt, x): 

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

2346 

2347 

2348class NDRVarPacketListField(_NDRVarField, _NDRPacketListField): 

2349 """ 

2350 NDR Varying PacketListField. Unused 

2351 """ 

2352 

2353 COUNT_FROM = True 

2354 

2355 

2356class NDRConfPacketListField(_NDRConfField, _NDRPacketListField): 

2357 """ 

2358 NDR Conformant PacketListField 

2359 """ 

2360 

2361 COUNT_FROM = True 

2362 

2363 

2364class NDRConfVarPacketListField(_NDRConfField, _NDRVarField, _NDRPacketListField): 

2365 """ 

2366 NDR Conformant Varying PacketListField 

2367 """ 

2368 

2369 COUNT_FROM = True 

2370 

2371 

2372class NDRConfFieldListField(_NDRConfField, NDRFieldListField): 

2373 """ 

2374 NDR Conformant FieldListField 

2375 """ 

2376 

2377 COUNT_FROM = True 

2378 

2379 

2380class NDRConfVarFieldListField(_NDRConfField, _NDRVarField, NDRFieldListField): 

2381 """ 

2382 NDR Conformant Varying FieldListField 

2383 """ 

2384 

2385 COUNT_FROM = True 

2386 

2387 

2388# NDR String fields 

2389 

2390 

2391class _NDRUtf16(Field): 

2392 def h2i(self, pkt, x): 

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

2394 return plain_str(x).encode(encoding) 

2395 

2396 def i2h(self, pkt, x): 

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

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

2399 

2400 

2401class NDRConfStrLenField(_NDRConfField, _NDRValueOf, StrLenField): 

2402 """ 

2403 NDR Conformant StrLenField. 

2404 

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

2406 (e.g. tower_octet_string). For ease of use, we implicitly convert 

2407 it in specific cases. 

2408 """ 

2409 

2410 CONFORMANT_STRING = True 

2411 LENGTH_FROM = True 

2412 

2413 

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

2415 """ 

2416 NDR Conformant StrLenFieldUtf16. 

2417 

2418 See NDRConfStrLenField for comment. 

2419 """ 

2420 

2421 CONFORMANT_STRING = True 

2422 ON_WIRE_SIZE_UTF16 = False 

2423 LENGTH_FROM = True 

2424 

2425 

2426class NDRVarStrNullField(_NDRVarField, _NDRValueOf, StrNullField): 

2427 """ 

2428 NDR Varying StrNullField 

2429 """ 

2430 

2431 NULLFIELD = True 

2432 

2433 

2434class NDRVarStrNullFieldUtf16(_NDRVarField, _NDRValueOf, StrNullFieldUtf16, _NDRUtf16): 

2435 """ 

2436 NDR Varying StrNullFieldUtf16 

2437 """ 

2438 

2439 NULLFIELD = True 

2440 

2441 

2442class NDRVarStrLenField(_NDRVarField, StrLenField): 

2443 """ 

2444 NDR Varying StrLenField 

2445 """ 

2446 

2447 LENGTH_FROM = True 

2448 

2449 

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

2451 """ 

2452 NDR Varying StrLenFieldUtf16 

2453 """ 

2454 

2455 ON_WIRE_SIZE_UTF16 = False 

2456 LENGTH_FROM = True 

2457 

2458 

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

2460 """ 

2461 NDR Conformant Varying StrLenField 

2462 """ 

2463 

2464 LENGTH_FROM = True 

2465 

2466 

2467class NDRConfVarStrLenFieldUtf16( 

2468 _NDRConfField, _NDRVarField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16 

2469): 

2470 """ 

2471 NDR Conformant Varying StrLenFieldUtf16 

2472 """ 

2473 

2474 ON_WIRE_SIZE_UTF16 = False 

2475 LENGTH_FROM = True 

2476 

2477 

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

2479 """ 

2480 NDR Conformant Varying StrNullField 

2481 """ 

2482 

2483 NULLFIELD = True 

2484 

2485 

2486class NDRConfVarStrNullFieldUtf16( 

2487 _NDRConfField, _NDRVarField, _NDRValueOf, StrNullFieldUtf16, _NDRUtf16 

2488): 

2489 """ 

2490 NDR Conformant Varying StrNullFieldUtf16 

2491 """ 

2492 

2493 ON_WIRE_SIZE_UTF16 = False 

2494 NULLFIELD = True 

2495 

2496 

2497# Union type 

2498 

2499 

2500class NDRUnion(_NDRPacket): 

2501 fields_desc = [ 

2502 IntField("tag", 0), 

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

2504 ] 

2505 

2506 

2507class _NDRUnionField(MultipleTypeField): 

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

2509 

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

2511 self.switch_fmt = switch_fmt 

2512 self.align = align 

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

2514 

2515 def getfield(self, pkt, s): 

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

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

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

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

2520 return remain, NDRUnion( 

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

2522 ) 

2523 

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

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

2526 if not isinstance(val, NDRUnion): 

2527 raise ValueError( 

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

2529 ) 

2530 _set_ctx_on(val.value, pkt) 

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

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

2533 # Then, compute the subfield with its own alignment 

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

2535 

2536 def _find_fld_pkt_val(self, pkt, val): 

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

2538 return fld, val.value 

2539 

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

2541 def i2repr(self, pkt, val): 

2542 return repr(val) 

2543 

2544 def i2h(self, pkt, x): 

2545 return x 

2546 

2547 def h2i(self, pkt, x): 

2548 return x 

2549 

2550 def valueof(self, pkt, x): 

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

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

2553 

2554 

2555class NDRUnionField(NDRConstructedType, _NDRUnionField): 

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

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

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

2559 

2560 def any2i(self, pkt, x): 

2561 # User-friendly helper 

2562 if x: 

2563 if not isinstance(x, NDRUnion): 

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

2565 else: 

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

2567 return x 

2568 

2569 

2570# Misc 

2571 

2572 

2573class _ProxyArray: 

2574 # Hack for recursive fields DEPORTED_CONFORMANTS field 

2575 __slots__ = ["getfld"] 

2576 

2577 def __init__(self, getfld): 

2578 self.getfld = getfld 

2579 

2580 def __len__(self): 

2581 try: 

2582 return len(self.getfld()) 

2583 except AttributeError: 

2584 return 0 

2585 

2586 def __iter__(self): 

2587 try: 

2588 return iter(self.getfld()) 

2589 except AttributeError: 

2590 return iter([]) 

2591 

2592 

2593class _ProxyTuple: 

2594 # Hack for recursive fields ALIGNMENT field 

2595 __slots__ = ["getfld"] 

2596 

2597 def __init__(self, getfld): 

2598 self.getfld = getfld 

2599 

2600 def __getitem__(self, name): 

2601 try: 

2602 return self.getfld()[name] 

2603 except AttributeError: 

2604 raise KeyError 

2605 

2606 

2607def NDRRecursiveClass(clsname): 

2608 """ 

2609 Return a special class that is used for pointer recursion 

2610 """ 

2611 # Get module where this is called 

2612 frame = inspect.currentframe().f_back 

2613 mod = frame.f_globals["__loader__"].name 

2614 getcls = lambda: getattr(importlib.import_module(mod), clsname) 

2615 

2616 class _REC(NDRPacket): 

2617 ALIGNMENT = _ProxyTuple(lambda: getattr(getcls(), "ALIGNMENT")) 

2618 DEPORTED_CONFORMANTS = _ProxyArray( 

2619 lambda: getattr(getcls(), "DEPORTED_CONFORMANTS") 

2620 ) 

2621 

2622 @classmethod 

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

2624 return getcls() 

2625 

2626 return _REC 

2627 

2628 

2629# The very few NDR-specific structures 

2630 

2631 

2632class NDRContextHandle(NDRPacket): 

2633 ALIGNMENT = (4, 4) 

2634 fields_desc = [ 

2635 LEIntField("attributes", 0), 

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

2637 ] 

2638 

2639 def guess_payload_class(self, payload): 

2640 return conf.padding_layer 

2641 

2642 

2643class NDRNone(NDRPacket): 

2644 # This is only used in NDRPacketListField to act as a "None" pointer, and is 

2645 # a workaround because the field doesn't support None as a value in the list. 

2646 name = "None" 

2647 ALIGNMENT = (4, 8) 

2648 fields_desc = [ 

2649 NDRInt3264Field("ptr", 0), 

2650 ] 

2651 

2652 

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

2654 

2655 

2656def _get_ndrtype1_endian(pkt): 

2657 if pkt.underlayer is None: 

2658 return "<" 

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

2660 

2661 

2662class NDRSerialization1Header(Packet): 

2663 fields_desc = [ 

2664 ByteField("Version", 1), 

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

2666 LEShortField("CommonHeaderLength", 8), 

2667 XLEIntField("Filler", 0xCCCCCCCC), 

2668 ] 

2669 

2670 # Add a bit of goo so that valueof() goes through the header 

2671 

2672 def _ndrlayer(self): 

2673 cur = self 

2674 while cur and not isinstance(cur, _NDRPacket) and cur.payload: 

2675 cur = cur.payload 

2676 if isinstance(cur, NDRPointer): 

2677 cur = cur.value 

2678 return cur 

2679 

2680 def getfield_and_val(self, attr): 

2681 try: 

2682 return Packet.getfield_and_val(self, attr) 

2683 except ValueError: 

2684 return self._ndrlayer().getfield_and_val(attr) 

2685 

2686 def valueof(self, name): 

2687 return self._ndrlayer().valueof(name) 

2688 

2689 

2690class NDRSerialization1PrivateHeader(Packet): 

2691 fields_desc = [ 

2692 EField( 

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

2694 ), 

2695 XLEIntField("Filler", 0), 

2696 ] 

2697 

2698 

2699def ndr_deserialize1(b, cls, ptr_pack=False): 

2700 """ 

2701 Deserialize Type Serialization Version 1 

2702 [MS-RPCE] sect 2.2.6 

2703 

2704 :param ptr_pack: pack in a pointer to the structure. 

2705 """ 

2706 if issubclass(cls, NDRPacket): 

2707 # We use an intermediary class because it uses NDRPacketField which handles 

2708 # deported conformant fields 

2709 if ptr_pack: 

2710 hdrlen = 20 

2711 

2712 class _cls(NDRPacket): 

2713 fields_desc = [NDRFullPointerField(NDRPacketField("pkt", None, cls))] 

2714 

2715 else: 

2716 hdrlen = 16 

2717 

2718 class _cls(NDRPacket): 

2719 fields_desc = [NDRPacketField("pkt", None, cls)] 

2720 

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

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

2723 padlen = (-hdr.ObjectBufferLength) % _TYPE1_S_PAD 

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

2725 # implement apparently misread the spec 

2726 return ( 

2727 hdr 

2728 / _cls( 

2729 b[16 : hdrlen + hdr.ObjectBufferLength], 

2730 ndr64=False, # Only NDR32 is supported in Type 1 

2731 ndrendian=endian, 

2732 ).pkt 

2733 / conf.padding_layer(b[hdrlen + padlen + hdr.ObjectBufferLength :]) 

2734 ) 

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

2736 

2737 

2738def ndr_serialize1(pkt, ptr_pack=False): 

2739 """ 

2740 Serialize Type Serialization Version 1 

2741 [MS-RPCE] sect 2.2.6 

2742 

2743 :param ptr_pack: pack in a pointer to the structure. 

2744 """ 

2745 pkt = pkt.copy() 

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

2747 if not isinstance(pkt, NDRSerialization1Header): 

2748 if not isinstance(pkt, NDRPacket): 

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

2750 if isinstance(pkt, NDRPointer): 

2751 cls = pkt.value.__class__ 

2752 else: 

2753 cls = pkt.__class__ 

2754 val = pkt 

2755 pkt_len = len(pkt) 

2756 # ObjectBufferLength: 

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

2758 pkt = NDRSerialization1Header( 

2759 Endianness=endian 

2760 ) / NDRSerialization1PrivateHeader( 

2761 ObjectBufferLength=pkt_len + (-pkt_len) % _TYPE1_S_PAD 

2762 ) 

2763 else: 

2764 cls = pkt.value.__class__ 

2765 val = pkt.payload.payload 

2766 pkt.payload.remove_payload() 

2767 

2768 # See above about why we need an intermediary class 

2769 if ptr_pack: 

2770 

2771 class _cls(NDRPacket): 

2772 fields_desc = [NDRFullPointerField(NDRPacketField("pkt", None, cls))] 

2773 

2774 else: 

2775 

2776 class _cls(NDRPacket): 

2777 fields_desc = [NDRPacketField("pkt", None, cls)] 

2778 

2779 ret = bytes(pkt / _cls(pkt=val, ndr64=False, ndrendian=endian)) 

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

2781 

2782 

2783class _NDRSerializeType1: 

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

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

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

2787 

2788 def i2m(self, pkt, val): 

2789 return ndr_serialize1(val, ptr_pack=self.ptr_pack) 

2790 

2791 def m2i(self, pkt, s): 

2792 return ndr_deserialize1(s, self.cls, ptr_pack=self.ptr_pack) 

2793 

2794 def i2len(self, pkt, val): 

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

2796 

2797 

2798class NDRSerializeType1PacketField(_NDRSerializeType1, PacketField): 

2799 __slots__ = ["ptr_pack"] 

2800 

2801 

2802class NDRSerializeType1PacketLenField(_NDRSerializeType1, PacketLenField): 

2803 __slots__ = ["ptr_pack"] 

2804 

2805 

2806class NDRSerializeType1PacketListField(_NDRSerializeType1, PacketListField): 

2807 __slots__ = ["ptr_pack"] 

2808 

2809 def i2len(self, pkt, val): 

2810 return sum(len(self.i2m(pkt, p)) for p in val) 

2811 

2812 

2813# --- DCE/RPC session 

2814 

2815 

2816class DceRpcSession(DefaultSession): 

2817 """ 

2818 A DCE/RPC session within a TCP socket. 

2819 """ 

2820 

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

2822 self.rpc_bind_interface: Union[DceRpcInterface, ComInterface] = None 

2823 self.rpc_bind_is_com: bool = False 

2824 self.rpc_bind_interface_commit: Union[DceRpcInterface, ComInterface] = None 

2825 self.rpc_bind_is_com_commit: bool = False 

2826 self.ndr64 = False 

2827 self.ndrendian = "little" 

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

2829 self.header_sign = conf.dcerpc_force_header_signing 

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

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

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

2833 self.sent_cont_ids = [] 

2834 self.cont_id = 0 # Currently selected context 

2835 self.auth_context_id = 0 # Currently selected authentication context 

2836 self.assoc_group_id = 0 # Currently selected association group 

2837 self.map_callid_opnum = {} 

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

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

2840 if conf.dcerpc_session_enable and conf.winssps_passive: 

2841 for ssp in conf.winssps_passive: 

2842 self.sniffsspcontexts[ssp] = None 

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

2844 

2845 def commit_rpc_interface(self): 

2846 """ 

2847 Called by the client/server when the current context is accepted 

2848 """ 

2849 self.rpc_bind_interface = self.rpc_bind_interface_commit 

2850 self.rpc_bind_is_com = self.rpc_bind_is_com_commit 

2851 self.rpc_bind_interface_commit = None 

2852 self.rpc_bind_is_com_commit = False 

2853 

2854 def _up_pkt(self, pkt, commit=True): 

2855 """ 

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

2857 opnums, etc. 

2858 

2859 :param commit: whether to commit bind information immediately, or wait for the 

2860 parent to call commit_rpc_interface(). 

2861 """ 

2862 opnum = None 

2863 opts = {} 

2864 if DceRpc5Bind in pkt or DceRpc5AlterContext in pkt: 

2865 # bind => get which RPC interface 

2866 self.sent_cont_ids = [x.cont_id for x in pkt.context_elem] 

2867 for ctx in pkt.context_elem: 

2868 if_uuid = ctx.abstract_syntax.if_uuid 

2869 if_version = ctx.abstract_syntax.if_version 

2870 try: 

2871 self.rpc_bind_interface_commit = DCE_RPC_INTERFACES[ 

2872 (if_uuid, if_version) 

2873 ] 

2874 self.rpc_bind_is_com_commit = False 

2875 except KeyError: 

2876 try: 

2877 self.rpc_bind_interface_commit = COM_INTERFACES[if_uuid] 

2878 self.rpc_bind_is_com_commit = True 

2879 except KeyError: 

2880 self.rpc_bind_interface_commit = None 

2881 log_runtime.warning( 

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

2883 ) 

2884 if commit: 

2885 self.commit_rpc_interface() 

2886 elif DceRpc5BindAck in pkt or DceRpc5AlterContextResp in pkt: 

2887 # bind ack => is it NDR64 

2888 for i, res in enumerate(pkt.results): 

2889 if res.result == 0: # Accepted 

2890 # Context 

2891 try: 

2892 self.cont_id = self.sent_cont_ids[i] 

2893 except IndexError: 

2894 self.cont_id = 0 

2895 finally: 

2896 self.sent_cont_ids = [] 

2897 

2898 self.assoc_group_id = pkt.assoc_group_id 

2899 

2900 # Endianness 

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

2902 

2903 # Transfer syntax 

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

2905 self.ndr64 = True 

2906 elif DceRpc5Request in pkt: 

2907 # request => match opnum with callID 

2908 opnum = pkt.opnum 

2909 uid = (self.assoc_group_id, pkt.call_id) 

2910 if self.rpc_bind_is_com: 

2911 self.map_callid_opnum[uid] = ( 

2912 opnum, 

2913 pkt[DceRpc5Request].payload.payload, 

2914 ) 

2915 else: 

2916 self.map_callid_opnum[uid] = opnum, pkt[DceRpc5Request].payload 

2917 elif DceRpc5Response in pkt: 

2918 # response => get opnum from table 

2919 uid = (self.assoc_group_id, pkt.call_id) 

2920 try: 

2921 opnum, opts["request_packet"] = self.map_callid_opnum[uid] 

2922 del self.map_callid_opnum[uid] 

2923 except KeyError: 

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

2925 # Bind / Alter request/response specific 

2926 if ( 

2927 DceRpc5Bind in pkt 

2928 or DceRpc5AlterContext in pkt 

2929 or DceRpc5BindAck in pkt 

2930 or DceRpc5AlterContextResp in pkt 

2931 ): 

2932 # Detect if "Header Signing" is in use 

2933 if pkt.pfc_flags & 0x04: # PFC_SUPPORT_HEADER_SIGN 

2934 self.header_sign = True 

2935 return opnum, opts 

2936 

2937 # [C706] sect 12.6.2 - Fragmentation and Reassembly 

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

2939 # will always receive the fragments in order. 

2940 

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

2942 """ 

2943 Function to defragment DCE/RPC packets. 

2944 """ 

2945 uid = (self.assoc_group_id, pkt.call_id) 

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

2947 # Not fragmented 

2948 return body 

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

2950 # Packet is fragmented 

2951 if body is None: 

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

2953 self.frags[uid] += body 

2954 if pkt.pfc_flags.PFC_LAST_FRAG: 

2955 return self.frags[uid] 

2956 else: 

2957 # Not fragmented 

2958 return body 

2959 

2960 # C706 sect 12.5.2.15 - PDU Body Length 

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

2962 MAX_PDU_BODY_SIZE = 4176 

2963 

2964 def _fragment(self, pkt, body): 

2965 """ 

2966 Function to fragment DCE/RPC packets. 

2967 """ 

2968 if len(body) > self.MAX_PDU_BODY_SIZE: 

2969 # Clear any PFC_*_FRAG flag 

2970 pkt.pfc_flags &= 0xFC 

2971 

2972 # Iterate through fragments 

2973 cur = None 

2974 while body: 

2975 # Create a fragment 

2976 pkt_frag = pkt.copy() 

2977 

2978 if cur is None: 

2979 # It's the first one 

2980 pkt_frag.pfc_flags += "PFC_FIRST_FRAG" 

2981 

2982 # Split 

2983 cur, body = ( 

2984 body[: self.MAX_PDU_BODY_SIZE], 

2985 body[self.MAX_PDU_BODY_SIZE :], 

2986 ) 

2987 

2988 if not body: 

2989 # It's the last one 

2990 pkt_frag.pfc_flags += "PFC_LAST_FRAG" 

2991 else: 

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

2993 # "only the last PDU of the request MUST have a verification 

2994 # trailer" 

2995 pkt_frag.vt_trailer = None 

2996 

2997 # Update payload for frag_len calculation 

2998 pkt_frag.payload.payload = conf.raw_layer(load=b"\x00" * len(cur)) 

2999 

3000 yield pkt_frag, cur 

3001 else: 

3002 yield pkt, body 

3003 

3004 # [MS-RPCE] sect 3.3.1.5.2.2 

3005 

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

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

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

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

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

3011 

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

3013 # be encrypted. 

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

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

3016 # message SHOULD be ignored. 

3017 # Similarly the signature output SHOULD be ignored. 

3018 

3019 def in_pkt(self, pkt, commit=True): 

3020 # Check for encrypted payloads 

3021 body = None 

3022 if conf.raw_layer in pkt.payload: 

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

3024 if ( 

3025 self.sspcontext is not None 

3026 and self.auth_level 

3027 in ( 

3028 RPC_C_AUTHN_LEVEL.PKT_INTEGRITY, 

3029 RPC_C_AUTHN_LEVEL.PKT_PRIVACY, 

3030 ) 

3031 and isinstance(pkt.payload, (DceRpc5Request, DceRpc5Response)) 

3032 and not (pkt.auth_verifier and pkt.auth_verifier.is_protected()) 

3033 ): 

3034 raise ValueError("DCE/RPC packet protection is required !") 

3035 # If we are doing passive sniffing 

3036 if conf.dcerpc_session_enable and conf.winssps_passive: 

3037 # We have Windows SSPs, and no current context 

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

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

3040 for ssp in self.sniffsspcontexts: 

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

3042 self.sniffsspcontexts[ssp], 

3043 pkt.auth_verifier.auth_value, 

3044 req_flags=GSS_S_FLAGS.GSS_S_ALLOW_MISSING_BINDINGS 

3045 | GSS_C_FLAGS.GSS_C_DCE_STYLE, 

3046 ) 

3047 if status == GSS_S_COMPLETE: 

3048 self.auth_level = DCE_C_AUTHN_LEVEL( 

3049 int(pkt.auth_verifier.auth_level) 

3050 ) 

3051 self.ssp = ssp 

3052 self.sspcontext = self.sniffsspcontexts[ssp] 

3053 self.sniffsspcontexts[ssp] = None 

3054 elif ( 

3055 self.sspcontext 

3056 and pkt.auth_verifier 

3057 and pkt.auth_verifier.is_protected() 

3058 and body 

3059 ): 

3060 # This is a request/response 

3061 if self.sspcontext.passive: 

3062 self.ssp.GSS_Passive_set_Direction( 

3063 self.sspcontext, 

3064 IsAcceptor=DceRpc5Response in pkt, 

3065 ) 

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

3067 if self.sspcontext is None: 

3068 return pkt 

3069 if self.auth_level in ( 

3070 RPC_C_AUTHN_LEVEL.PKT_INTEGRITY, 

3071 RPC_C_AUTHN_LEVEL.PKT_PRIVACY, 

3072 ): 

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

3074 # [MS-RPCE] sect 2.2.2.13 

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

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

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

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

3079 # body." 

3080 if pkt.vt_trailer: 

3081 body += bytes(pkt.vt_trailer) 

3082 # Account for padding when computing checksum/encryption 

3083 if pkt.auth_padding: 

3084 body += pkt.auth_padding 

3085 

3086 # Build pdu_header and sec_trailer 

3087 pdu_header = pkt.copy() 

3088 sec_trailer = pdu_header.auth_verifier 

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

3090 authval_len = len(sec_trailer.auth_value) 

3091 # Discard everything out of the header 

3092 pdu_header.auth_padding = None 

3093 pdu_header.auth_verifier = None 

3094 pdu_header.payload.payload = NoPayload() 

3095 pdu_header.vt_trailer = None 

3096 

3097 # [MS-RPCE] sect 2.2.2.12 

3098 if self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY: 

3099 _msgs = self.ssp.GSS_UnwrapEx( 

3100 self.sspcontext, 

3101 [ 

3102 # "PDU header" 

3103 SSP.WRAP_MSG( 

3104 conf_req_flag=False, 

3105 sign=self.header_sign, 

3106 data=bytes(pdu_header), 

3107 ), 

3108 # "PDU body" 

3109 SSP.WRAP_MSG( 

3110 conf_req_flag=True, 

3111 sign=True, 

3112 data=body, 

3113 ), 

3114 # "sec_trailer" 

3115 SSP.WRAP_MSG( 

3116 conf_req_flag=False, 

3117 sign=self.header_sign, 

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

3119 ), 

3120 ], 

3121 pkt.auth_verifier.auth_value, 

3122 ) 

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

3124 elif self.auth_level == RPC_C_AUTHN_LEVEL.PKT_INTEGRITY: 

3125 self.ssp.GSS_VerifyMICEx( 

3126 self.sspcontext, 

3127 [ 

3128 # "PDU header" 

3129 SSP.MIC_MSG( 

3130 sign=self.header_sign, 

3131 data=bytes(pdu_header), 

3132 ), 

3133 # "PDU body" 

3134 SSP.MIC_MSG( 

3135 sign=True, 

3136 data=body, 

3137 ), 

3138 # "sec_trailer" 

3139 SSP.MIC_MSG( 

3140 sign=self.header_sign, 

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

3142 ), 

3143 ], 

3144 pkt.auth_verifier.auth_value, 

3145 ) 

3146 # Put padding back into the header 

3147 if pkt.auth_padding: 

3148 padlen = len(pkt.auth_padding) 

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

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

3151 if _SECTRAILER_MAGIC in body: 

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

3153 pkt, body 

3154 ) 

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

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

3157 body = self._defragment(pkt, body) 

3158 if not body: 

3159 return 

3160 # Get opnum and options 

3161 opnum, opts = self._up_pkt(pkt, commit=commit) 

3162 # Try to parse the payload 

3163 if opnum is not None and self.rpc_bind_interface: 

3164 # use opnum to parse the payload 

3165 is_response = DceRpc5Response in pkt 

3166 try: 

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

3168 except KeyError: 

3169 log_runtime.warning( 

3170 "Unknown opnum %s for interface %s" 

3171 % (opnum, self.rpc_bind_interface) 

3172 ) 

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

3174 return pkt 

3175 if body: 

3176 orpc = None 

3177 if self.rpc_bind_is_com: 

3178 # If interface is a COM interface, start off by dissecting the 

3179 # ORPCTHIS / ORPCTHAT argument 

3180 from scapy.layers.msrpce.raw.ms_dcom import ORPCTHAT, ORPCTHIS 

3181 

3182 # [MS-DCOM] sect 2.2.13 

3183 # "ORPCTHIS and ORPCTHAT structures MUST be marshaled using 

3184 # the NDR (32) Transfer Syntax" 

3185 if is_response: 

3186 orpc = ORPCTHAT(body, ndr64=False) 

3187 else: 

3188 orpc = ORPCTHIS(body, ndr64=False) 

3189 body = orpc.load 

3190 orpc.remove_payload() 

3191 # Dissect payload using class 

3192 try: 

3193 payload = cls( 

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

3195 ) 

3196 except Exception: 

3197 if conf.debug_dissector: 

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

3199 if cls is not None: 

3200 raise 

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

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

3203 if conf.padding_layer in payload: 

3204 # Most likely, dissection failed. 

3205 log_runtime.warning( 

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

3207 ) 

3208 pad = payload[conf.padding_layer] 

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

3210 if orpc is not None: 

3211 pkt /= orpc 

3212 pkt /= payload 

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

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

3215 self._up_pkt(pkt, commit=commit) 

3216 elif not cls.fields_desc: 

3217 # Request class has no payload 

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

3219 elif body: 

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

3221 return pkt 

3222 

3223 def out_pkt(self, pkt): 

3224 assert DceRpc5 in pkt 

3225 # Register opnum and options 

3226 self._up_pkt(pkt) 

3227 

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

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

3230 # The list of packet responses 

3231 pkts = [] 

3232 # Take the body payload, and eventually split it 

3233 body = bytes(pkt.payload.payload) 

3234 

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

3236 if pkt.auth_verifier is not None: 

3237 # Verifier already set 

3238 pkts.append(pkt) 

3239 continue 

3240 

3241 # Sign / Encrypt 

3242 if self.sspcontext: 

3243 signature = None 

3244 if self.auth_level in ( 

3245 RPC_C_AUTHN_LEVEL.PKT_INTEGRITY, 

3246 RPC_C_AUTHN_LEVEL.PKT_PRIVACY, 

3247 ): 

3248 # Remember that vt_trailer is included in the PDU 

3249 if pkt.vt_trailer: 

3250 body += bytes(pkt.vt_trailer) 

3251 # Account for padding when computing checksum/encryption 

3252 if pkt.auth_padding is None: 

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

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

3255 else: 

3256 padlen = len(pkt.auth_padding) 

3257 # Remember that padding IS SIGNED & ENCRYPTED 

3258 body += pkt.auth_padding 

3259 # Add the auth_verifier 

3260 pkt.auth_verifier = CommonAuthVerifier( 

3261 auth_type=self.ssp.auth_type, 

3262 auth_level=self.auth_level, 

3263 auth_context_id=self.auth_context_id, 

3264 auth_pad_length=padlen, 

3265 # Note: auth_value should have the correct length because 

3266 # when using PFC_SUPPORT_HEADER_SIGN, auth_len 

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

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

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

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

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

3272 # See `gensec_sig_size` in samba. 

3273 auth_value=b"\x00" 

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

3275 ) 

3276 # Build pdu_header and sec_trailer 

3277 pdu_header = pkt.copy() 

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

3279 pdu_header.frag_len = len(pdu_header) 

3280 sec_trailer = pdu_header.auth_verifier 

3281 # sec_trailer: include the sec_trailer but not the 

3282 # Authentication token 

3283 authval_len = len(sec_trailer.auth_value) 

3284 # sec_trailer.auth_value = None 

3285 # Discard everything out of the header 

3286 pdu_header.auth_padding = None 

3287 pdu_header.auth_verifier = None 

3288 pdu_header.payload.payload = NoPayload() 

3289 pdu_header.vt_trailer = None 

3290 signature = None 

3291 # [MS-RPCE] sect 2.2.2.12 

3292 if self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY: 

3293 _msgs, signature = self.ssp.GSS_WrapEx( 

3294 self.sspcontext, 

3295 [ 

3296 # "PDU header" 

3297 SSP.WRAP_MSG( 

3298 conf_req_flag=False, 

3299 sign=self.header_sign, 

3300 data=bytes(pdu_header), 

3301 ), 

3302 # "PDU body" 

3303 SSP.WRAP_MSG( 

3304 conf_req_flag=True, 

3305 sign=True, 

3306 data=body, 

3307 ), 

3308 # "sec_trailer" 

3309 SSP.WRAP_MSG( 

3310 conf_req_flag=False, 

3311 sign=self.header_sign, 

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

3313 ), 

3314 ], 

3315 ) 

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

3317 elif self.auth_level == RPC_C_AUTHN_LEVEL.PKT_INTEGRITY: 

3318 signature = self.ssp.GSS_GetMICEx( 

3319 self.sspcontext, 

3320 [ 

3321 # "PDU header" 

3322 SSP.MIC_MSG( 

3323 sign=self.header_sign, 

3324 data=bytes(pdu_header), 

3325 ), 

3326 # "PDU body" 

3327 SSP.MIC_MSG( 

3328 sign=True, 

3329 data=body, 

3330 ), 

3331 # "sec_trailer" 

3332 SSP.MIC_MSG( 

3333 sign=self.header_sign, 

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

3335 ), 

3336 ], 

3337 pkt.auth_verifier.auth_value, 

3338 ) 

3339 s = body 

3340 else: 

3341 raise ValueError("Impossible") 

3342 # Put padding back in the header 

3343 if padlen: 

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

3345 # Put back vt_trailer into the header 

3346 if pkt.vt_trailer: 

3347 vtlen = len(pkt.vt_trailer) 

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

3349 else: 

3350 s = body 

3351 

3352 # now inject the encrypted payload into the packet 

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

3354 # and the auth_value 

3355 if signature: 

3356 pkt.auth_verifier.auth_value = signature 

3357 else: 

3358 pkt.auth_verifier = None 

3359 # Add to the list 

3360 pkts.append(pkt) 

3361 return pkts 

3362 else: 

3363 return [pkt] 

3364 

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

3366 """ 

3367 Used when DceRpcSession is used for passive sniffing. 

3368 """ 

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

3370 if pkt is not None and DceRpc5 in pkt: 

3371 rpkt = self.in_pkt(pkt) 

3372 if rpkt is None: 

3373 # We are passively dissecting a fragmented packet. Return 

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

3375 pkt[DceRpc5].payload.remove_payload() 

3376 return pkt 

3377 return rpkt 

3378 return pkt 

3379 

3380 

3381class DceRpcSocket(StreamSocket): 

3382 """ 

3383 A Wrapper around StreamSocket that uses a DceRpcSession 

3384 """ 

3385 

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

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

3388 self.session = DceRpcSession( 

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

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

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

3392 ) 

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

3394 

3395 def send(self, x, is_sr1=False, **kwargs): 

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

3397 if self.transport == DCERPC_Transport.NCACN_NP: 

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

3399 self.ins.send(pkt, is_sr1=is_sr1, **kwargs) 

3400 else: 

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

3402 

3403 def sr1(self, *args, **kwargs): 

3404 # We allow to use IOCTL only when sr1() is used, as we expect an answer. 

3405 return super(DceRpcSocket, self).sr1(*args, is_sr1=True, **kwargs) 

3406 

3407 def recv(self, x=None): 

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

3409 if pkt is not None: 

3410 return self.session.in_pkt(pkt) 

3411 

3412 

3413# --- TODO cleanup below 

3414 

3415# Heuristically way to find the payload class 

3416# 

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

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

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

3420# parameter. 

3421# 

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

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

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

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

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

3427# handling the dissection, False otherwise 

3428 

3429 

3430class DceRpc4Payload(Packet): 

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

3432 

3433 _payload_class = [] 

3434 

3435 @classmethod 

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

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

3438 for klass in cls._payload_class: 

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

3440 return klass 

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

3442 return Raw 

3443 

3444 @classmethod 

3445 def register_possible_payload(cls, pay): 

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

3447 possible payload""" 

3448 cls._payload_class.append(pay) 

3449 

3450 

3451bind_layers(DceRpc4, DceRpc4Payload)