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

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

297 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""" 

7DCE/RPC client as per [MS-RPCE] 

8""" 

9 

10import collections 

11import uuid 

12import socket 

13 

14from scapy.config import conf 

15from scapy.error import log_runtime 

16 

17from scapy.layers.dcerpc import ( 

18 _DCE_RPC_ERROR_CODES, 

19 ComInterface, 

20 CommonAuthVerifier, 

21 DCE_C_AUTHN_LEVEL, 

22 DCERPC_Transport, 

23 DceRpc5, 

24 DceRpc5AbstractSyntax, 

25 DceRpc5AlterContext, 

26 DceRpc5AlterContextResp, 

27 DceRpc5Auth3, 

28 DceRpc5Bind, 

29 DceRpc5BindAck, 

30 DceRpc5BindNak, 

31 DceRpc5Context, 

32 DceRpc5Fault, 

33 DceRpc5Request, 

34 DceRpc5Response, 

35 DceRpc5TransferSyntax, 

36 DceRpcInterface, 

37 DceRpcSecVT, 

38 DceRpcSecVTCommand, 

39 DceRpcSecVTPcontext, 

40 DceRpcSession, 

41 DceRpcSocket, 

42 find_dcerpc_interface, 

43 NDRContextHandle, 

44 NDRPointer, 

45 RPC_C_IMP_LEVEL, 

46) 

47from scapy.layers.gssapi import ( 

48 SSP, 

49 GSS_S_FAILURE, 

50 GSS_S_COMPLETE, 

51 GSS_S_CONTINUE_NEEDED, 

52 GSS_C_FLAGS, 

53) 

54from scapy.layers.smbclient import ( 

55 SMB_RPC_SOCKET, 

56) 

57from scapy.layers.windows.erref import STATUS_ERREF 

58 

59# RPC 

60from scapy.layers.msrpce.ept import ( 

61 ept_map_Request, 

62 ept_map_Response, 

63 twr_p_t, 

64 protocol_tower_t, 

65 prot_and_addr_t, 

66 UUID, 

67) 

68 

69# Typing 

70from typing import ( 

71 Optional, 

72 Union, 

73) 

74 

75 

76class DCERPC_Client(object): 

77 """ 

78 A basic DCE/RPC client 

79 

80 :param transport: the transport to use. 

81 :param ndr64: should ask for NDR64 when binding (default conf.ndr64) 

82 :param ndrendian: the endianness to use (default little) 

83 :param verb: enable verbose logging (default True) 

84 :param auth_level: the DCE_C_AUTHN_LEVEL to use 

85 :param impersonation_type: the RPC_C_IMP_LEVEL to use 

86 """ 

87 

88 def __init__( 

89 self, 

90 transport: DCERPC_Transport, 

91 ndr64: Optional[bool] = None, 

92 ndrendian: str = "little", 

93 verb: bool = True, 

94 auth_level: Optional[DCE_C_AUTHN_LEVEL] = None, 

95 impersonation_type: RPC_C_IMP_LEVEL = RPC_C_IMP_LEVEL.DEFAULT, 

96 **kwargs, 

97 ): 

98 self.sock = None 

99 self.transport = transport 

100 assert isinstance( 

101 transport, DCERPC_Transport 

102 ), "transport must be from DCERPC_Transport" 

103 

104 # Counters 

105 self.call_ids = collections.defaultdict(lambda: 0) # by assoc_group_id 

106 self.next_cont_id = 0 # next available context id 

107 self.next_auth_contex_id = 0 # next available auth context id 

108 

109 # Session parameters 

110 if ndr64 is None: 

111 ndr64 = conf.ndr64 

112 self.ndr64: bool = ndr64 

113 self.ndrendian = ndrendian 

114 self.verb = verb 

115 self.host: str = None 

116 self.port: int = -1 

117 self.ssp = kwargs.pop("ssp", None) # type: SSP 

118 self.sspcontext = None 

119 if auth_level is not None: 

120 self.auth_level = auth_level 

121 elif self.ssp is not None: 

122 self.auth_level = DCE_C_AUTHN_LEVEL.CONNECT 

123 else: 

124 self.auth_level = DCE_C_AUTHN_LEVEL.NONE 

125 if impersonation_type == RPC_C_IMP_LEVEL.DEFAULT: 

126 # Same default as windows 

127 impersonation_type = RPC_C_IMP_LEVEL.IDENTIFY 

128 self.impersonation_type = impersonation_type 

129 self._first_time_on_interface = True 

130 self.contexts = {} 

131 self.dcesockargs = kwargs 

132 self.dcesockargs["transport"] = self.transport 

133 

134 @classmethod 

135 def from_smblink(cls, smbcli, smb_kwargs={}, **kwargs): 

136 """ 

137 Build a DCERPC_Client from a SMB_Client.smblink directly 

138 """ 

139 client = DCERPC_Client(DCERPC_Transport.NCACN_NP, **kwargs) 

140 sock = client.smbrpcsock = SMB_RPC_SOCKET(smbcli, **smb_kwargs) 

141 client.sock = DceRpcSocket( 

142 sock, 

143 DceRpc5, 

144 ssp=client.ssp, 

145 auth_level=client.auth_level, 

146 **client.dcesockargs, 

147 ) 

148 return client 

149 

150 @property 

151 def session(self) -> DceRpcSession: 

152 try: 

153 return self.sock.session 

154 except AttributeError: 

155 raise ValueError("Client is not connected ! Please connect()") 

156 

157 def connect( 

158 self, 

159 host, 

160 endpoint: Union[int, str] = None, 

161 port: Optional[int] = None, 

162 interface=None, 

163 timeout=5, 

164 smb_kwargs={}, 

165 ): 

166 """ 

167 Initiate a connection. 

168 

169 :param host: the host to connect to 

170 :param endpoint: (optional) the port/smb pipe to connect to 

171 :param interface: (optional) if endpoint isn't provided, uses the endpoint 

172 mapper to find the appropriate endpoint for that interface. 

173 :param timeout: (optional) the connection timeout (default 5) 

174 :param port: (optional) the port to connect to. (useful for SMB) 

175 """ 

176 self.sspcontext = None 

177 smb_kwargs.setdefault("HOST", host) 

178 if endpoint is None and interface is not None: 

179 # Figure out the endpoint using the endpoint mapper 

180 

181 if self.transport == DCERPC_Transport.NCACN_IP_TCP and port is None: 

182 # IP/TCP 

183 # ask the endpoint mapper (port 135) for the IP:PORT 

184 endpoints = get_endpoint( 

185 host, 

186 interface, 

187 ndrendian=self.ndrendian, 

188 verb=self.verb, 

189 ) 

190 if endpoints: 

191 _, endpoint = endpoints[0] 

192 else: 

193 raise ValueError( 

194 "Could not find an available endpoint for that interface !" 

195 ) 

196 elif self.transport == DCERPC_Transport.NCACN_NP: 

197 # SMB 

198 # ask the endpoint mapper (over SMB) for the namedpipe 

199 endpoints = get_endpoint( 

200 host, 

201 interface, 

202 transport=self.transport, 

203 ndrendian=self.ndrendian, 

204 verb=self.verb, 

205 ssp=self.ssp, 

206 smb_kwargs=smb_kwargs, 

207 ) 

208 if endpoints: 

209 endpoint = endpoints[0].lstrip("\\pipe\\") 

210 else: 

211 return 

212 

213 # Assign the default port if no port is provided 

214 if port is None: 

215 if self.transport == DCERPC_Transport.NCACN_IP_TCP: # IP/TCP 

216 port = endpoint or 135 

217 elif self.transport == DCERPC_Transport.NCACN_NP: # SMB 

218 port = 445 

219 else: 

220 raise ValueError( 

221 "Can't guess the port for transport: %s" % self.transport 

222 ) 

223 

224 # Start socket and connect 

225 self.host = host 

226 self.port = port 

227 sock = socket.socket() 

228 sock.settimeout(timeout) 

229 if self.verb: 

230 print( 

231 "\u2503 Connecting to %s on port %s via %s..." 

232 % (host, port, repr(self.transport)) 

233 ) 

234 sock.connect((host, port)) 

235 if self.verb: 

236 print( 

237 conf.color_theme.green( 

238 "\u2514 Connected from %s" % repr(sock.getsockname()) 

239 ) 

240 ) 

241 

242 if self.transport == DCERPC_Transport.NCACN_NP: # SMB 

243 if self.auth_level >= DCE_C_AUTHN_LEVEL.PKT_PRIVACY: 

244 smb_kwargs.setdefault("REQUIRE_ENCRYPTION", True) 

245 elif self.auth_level >= DCE_C_AUTHN_LEVEL.PKT_INTEGRITY: 

246 smb_kwargs.setdefault("REQUIRE_SIGNATURE", True) 

247 

248 # We pack the socket into a SMB_RPC_SOCKET 

249 sock = self.smbrpcsock = SMB_RPC_SOCKET.from_tcpsock( 

250 sock, 

251 ssp=self.ssp, 

252 **smb_kwargs, 

253 ) 

254 

255 # If the endpoint is provided, connect to it. 

256 if endpoint is not None: 

257 self.open_smbpipe(endpoint) 

258 

259 self.sock = DceRpcSocket( 

260 sock, 

261 DceRpc5, 

262 **self.dcesockargs, 

263 ) 

264 elif self.transport == DCERPC_Transport.NCACN_IP_TCP: 

265 self.sock = DceRpcSocket( 

266 sock, 

267 DceRpc5, 

268 ssp=self.ssp, 

269 auth_level=self.auth_level, 

270 **self.dcesockargs, 

271 ) 

272 

273 def close(self): 

274 """ 

275 Close the DCE/RPC client. 

276 """ 

277 if self.verb: 

278 print("X Connection closed\n") 

279 self.sock.close() 

280 

281 def sr1(self, pkt, **kwargs): 

282 """ 

283 Send/Receive a DCE/RPC message. 

284 

285 The DCE/RPC header is added automatically. 

286 """ 

287 self.call_ids[self.session.assoc_group_id] += 1 

288 pkt = ( 

289 DceRpc5( 

290 call_id=self.call_ids[self.session.assoc_group_id], 

291 pfc_flags="PFC_FIRST_FRAG+PFC_LAST_FRAG", 

292 endian=self.ndrendian, 

293 auth_verifier=kwargs.pop("auth_verifier", None), 

294 vt_trailer=kwargs.pop("vt_trailer", None), 

295 ) 

296 / pkt 

297 ) 

298 if "pfc_flags" in kwargs: 

299 pkt.pfc_flags = kwargs.pop("pfc_flags") 

300 if "objectuuid" in kwargs: 

301 pkt.pfc_flags += "PFC_OBJECT_UUID" 

302 pkt.object = kwargs.pop("objectuuid") 

303 return self.sock.sr1(pkt, verbose=0, **kwargs) 

304 

305 def send(self, pkt, **kwargs): 

306 """ 

307 Send a DCE/RPC message. 

308 

309 The DCE/RPC header is added automatically. 

310 """ 

311 self.call_ids[self.session.assoc_group_id] += 1 

312 pkt = ( 

313 DceRpc5( 

314 call_id=self.call_ids[self.session.assoc_group_id], 

315 pfc_flags="PFC_FIRST_FRAG+PFC_LAST_FRAG", 

316 endian=self.ndrendian, 

317 auth_verifier=kwargs.pop("auth_verifier", None), 

318 vt_trailer=kwargs.pop("vt_trailer", None), 

319 ) 

320 / pkt 

321 ) 

322 if "pfc_flags" in kwargs: 

323 pkt.pfc_flags = kwargs.pop("pfc_flags") 

324 if "objectuuid" in kwargs: 

325 pkt.pfc_flags += "PFC_OBJECT_UUID" 

326 pkt.object = kwargs.pop("objectuuid") 

327 return self.sock.send(pkt, **kwargs) 

328 

329 def sr1_req(self, pkt, **kwargs): 

330 """ 

331 Send/Receive a DCE/RPC request. 

332 

333 :param pkt: the inner DCE/RPC message, without any header. 

334 """ 

335 if self.verb: 

336 if "objectuuid" in kwargs: 

337 # COM 

338 print( 

339 conf.color_theme.opening( 

340 ">> REQUEST (COM): %s" % pkt.payload.__class__.__name__ 

341 ) 

342 ) 

343 else: 

344 print( 

345 conf.color_theme.opening(">> REQUEST: %s" % pkt.__class__.__name__) 

346 ) 

347 

348 # Add sectrailer if first time talking on this interface 

349 vt_trailer = b"" 

350 if ( 

351 self._first_time_on_interface 

352 and self.transport != DCERPC_Transport.NCACN_NP 

353 ): 

354 # In the first request after a bind, Windows sends a trailer to verify 

355 # that the negotiated transfer/interface wasn't altered. 

356 self._first_time_on_interface = False 

357 vt_trailer = DceRpcSecVT( 

358 commands=[ 

359 DceRpcSecVTCommand(SEC_VT_COMMAND_END=1) 

360 / DceRpcSecVTPcontext( 

361 InterfaceId=self.session.rpc_bind_interface.uuid, 

362 Version=self.session.rpc_bind_interface.if_version, 

363 TransferSyntax="NDR64" if self.ndr64 else "NDR 2.0", 

364 TransferVersion=1 if self.ndr64 else 2, 

365 ) 

366 ] 

367 ) 

368 

369 # Optional: force opnum 

370 opnum = {} 

371 if "opnum" in kwargs: 

372 opnum["opnum"] = kwargs.pop("opnum") 

373 

374 # Set NDR64 

375 pkt.ndr64 = self.ndr64 

376 

377 # Send/receive 

378 resp = self.sr1( 

379 DceRpc5Request( 

380 cont_id=self.session.cont_id, 

381 alloc_hint=len(pkt) + len(vt_trailer), 

382 **opnum, 

383 ) 

384 / pkt, 

385 vt_trailer=vt_trailer, 

386 **kwargs, 

387 ) 

388 

389 # Parse result 

390 result = None 

391 if DceRpc5Response in resp: 

392 if self.verb: 

393 if "objectuuid" in kwargs: 

394 # COM 

395 print( 

396 conf.color_theme.success( 

397 "<< RESPONSE (COM): %s" 

398 % (resp[DceRpc5Response].payload.payload.__class__.__name__) 

399 ) 

400 ) 

401 else: 

402 print( 

403 conf.color_theme.success( 

404 "<< RESPONSE: %s" 

405 % (resp[DceRpc5Response].payload.__class__.__name__) 

406 ) 

407 ) 

408 result = resp[DceRpc5Response].payload 

409 elif DceRpc5Fault in resp: 

410 if self.verb: 

411 print(conf.color_theme.success("<< FAULT")) 

412 # If [MS-EERR] is loaded, show the extended info 

413 if resp[DceRpc5Fault].payload and not isinstance( 

414 resp[DceRpc5Fault].payload, conf.raw_layer 

415 ): 

416 resp[DceRpc5Fault].payload.show() 

417 result = resp 

418 

419 if self.verb and getattr(resp, "status", 0) != 0: 

420 if resp.status in _DCE_RPC_ERROR_CODES: 

421 print(conf.color_theme.fail(f"! {_DCE_RPC_ERROR_CODES[resp.status]}")) 

422 elif resp.status in STATUS_ERREF: 

423 print(conf.color_theme.fail(f"! {STATUS_ERREF[resp.status]}")) 

424 else: 

425 print(conf.color_theme.fail("! Failure")) 

426 resp.show() 

427 return result 

428 

429 def _get_bind_context(self, interface): 

430 """ 

431 Internal: get the bind DCE/RPC context. 

432 """ 

433 if interface in self.contexts: 

434 # We have already found acceptable contexts for this interface, 

435 # reuse that. 

436 return self.contexts[interface] 

437 

438 # NDR 2.0 

439 contexts = [ 

440 DceRpc5Context( 

441 cont_id=self.next_cont_id, 

442 abstract_syntax=DceRpc5AbstractSyntax( 

443 if_uuid=interface.uuid, 

444 if_version=interface.if_version, 

445 ), 

446 transfer_syntaxes=[ 

447 DceRpc5TransferSyntax( 

448 # NDR 2.0 32-bit 

449 if_uuid="NDR 2.0", 

450 if_version=2, 

451 ) 

452 ], 

453 ), 

454 ] 

455 self.next_cont_id += 1 

456 

457 # NDR64 

458 if self.ndr64: 

459 contexts.append( 

460 DceRpc5Context( 

461 cont_id=self.next_cont_id, 

462 abstract_syntax=DceRpc5AbstractSyntax( 

463 if_uuid=interface.uuid, 

464 if_version=interface.if_version, 

465 ), 

466 transfer_syntaxes=[ 

467 DceRpc5TransferSyntax( 

468 # NDR64 

469 if_uuid="NDR64", 

470 if_version=1, 

471 ) 

472 ], 

473 ) 

474 ) 

475 self.next_cont_id += 1 

476 

477 # BindTimeFeatureNegotiationBitmask 

478 contexts.append( 

479 DceRpc5Context( 

480 cont_id=self.next_cont_id, 

481 abstract_syntax=DceRpc5AbstractSyntax( 

482 if_uuid=interface.uuid, 

483 if_version=interface.if_version, 

484 ), 

485 transfer_syntaxes=[ 

486 DceRpc5TransferSyntax( 

487 if_uuid=uuid.UUID("6cb71c2c-9812-4540-0300-000000000000"), 

488 if_version=1, 

489 ) 

490 ], 

491 ) 

492 ) 

493 self.next_cont_id += 1 

494 

495 # Store contexts for this interface 

496 self.contexts[interface] = contexts 

497 

498 return contexts 

499 

500 def _check_bind_context(self, interface, contexts) -> bool: 

501 """ 

502 Internal: check the answer DCE/RPC bind context, and update them. 

503 """ 

504 for i, ctx in enumerate(contexts): 

505 if ctx.result == 0: 

506 # Context was accepted. Remove all others from cache 

507 if len(self.contexts[interface]) != 1: 

508 self.contexts[interface] = [self.contexts[interface][i]] 

509 return True 

510 

511 return False 

512 

513 def _bind( 

514 self, 

515 interface: Union[DceRpcInterface, ComInterface], 

516 reqcls, 

517 respcls, 

518 target_name: Optional[str] = None, 

519 ) -> bool: 

520 """ 

521 Internal: used to send a bind/alter request 

522 """ 

523 # Build a security context: [MS-RPCE] 3.3.1.5.2 

524 if self.verb: 

525 print( 

526 conf.color_theme.opening( 

527 ">> %s on %s" % (reqcls.__name__, interface) 

528 + (" (with %s)" % self.ssp.__class__.__name__ if self.ssp else "") 

529 ) 

530 ) 

531 

532 # Do we need an authenticated bind 

533 if not self.ssp or ( 

534 self.sspcontext is not None 

535 or self.transport == DCERPC_Transport.NCACN_NP 

536 and self.auth_level < DCE_C_AUTHN_LEVEL.PKT_INTEGRITY 

537 ): 

538 # NCACN_NP = SMB without INTEGRITY/PRIVACY does not bind the RPC securely, 

539 # again as it has already authenticated during the SMB Session Setup 

540 resp = self.sr1( 

541 reqcls(context_elem=self._get_bind_context(interface)), 

542 auth_verifier=None, 

543 ) 

544 status = GSS_S_COMPLETE 

545 else: 

546 # Perform authentication 

547 self.sspcontext, token, status = self.ssp.GSS_Init_sec_context( 

548 self.sspcontext, 

549 req_flags=( 

550 # SSPs need to be instantiated with some special flags 

551 # for DCE/RPC usages. 

552 GSS_C_FLAGS.GSS_C_DCE_STYLE 

553 | GSS_C_FLAGS.GSS_C_REPLAY_FLAG 

554 | GSS_C_FLAGS.GSS_C_SEQUENCE_FLAG 

555 | GSS_C_FLAGS.GSS_C_MUTUAL_FLAG 

556 | ( 

557 GSS_C_FLAGS.GSS_C_INTEG_FLAG 

558 if self.auth_level >= DCE_C_AUTHN_LEVEL.PKT_INTEGRITY 

559 else 0 

560 ) 

561 | ( 

562 GSS_C_FLAGS.GSS_C_CONF_FLAG 

563 if self.auth_level >= DCE_C_AUTHN_LEVEL.PKT_PRIVACY 

564 else 0 

565 ) 

566 | ( 

567 GSS_C_FLAGS.GSS_C_IDENTIFY_FLAG 

568 if self.impersonation_type <= RPC_C_IMP_LEVEL.IDENTIFY 

569 else 0 

570 ) 

571 | ( 

572 GSS_C_FLAGS.GSS_C_DELEG_FLAG 

573 if self.impersonation_type == RPC_C_IMP_LEVEL.DELEGATE 

574 else 0 

575 ) 

576 ), 

577 target_name=target_name or ("host/" + self.host), 

578 ) 

579 

580 if status not in [GSS_S_CONTINUE_NEEDED, GSS_S_COMPLETE]: 

581 # Authentication failed. 

582 self.sspcontext.clifailure() 

583 return False 

584 

585 resp = self.sr1( 

586 reqcls(context_elem=self._get_bind_context(interface)), 

587 auth_verifier=( 

588 None 

589 if not self.sspcontext 

590 else CommonAuthVerifier( 

591 auth_type=self.ssp.auth_type, 

592 auth_level=self.auth_level, 

593 auth_context_id=self.session.auth_context_id, 

594 auth_value=token, 

595 ) 

596 ), 

597 pfc_flags=( 

598 "PFC_FIRST_FRAG+PFC_LAST_FRAG" 

599 + ( 

600 # If the SSP supports "Header Signing", advertise it 

601 "+PFC_SUPPORT_HEADER_SIGN" 

602 if self.ssp is not None and self.session.support_header_signing 

603 else "" 

604 ) 

605 ), 

606 ) 

607 

608 # Check that the answer looks valid and contexts were accepted 

609 if respcls not in resp or not self._check_bind_context( 

610 interface, resp.results 

611 ): 

612 token = None 

613 status = GSS_S_FAILURE 

614 else: 

615 # Call the underlying SSP 

616 self.sspcontext, token, status = self.ssp.GSS_Init_sec_context( 

617 self.sspcontext, 

618 input_token=resp.auth_verifier.auth_value, 

619 target_name=target_name or ("host/" + self.host), 

620 ) 

621 

622 if status in [GSS_S_CONTINUE_NEEDED, GSS_S_COMPLETE]: 

623 # Authentication should continue, in two ways: 

624 # - through DceRpc5Auth3 (e.g. NTLM) 

625 # - through DceRpc5AlterContext (e.g. Kerberos) 

626 if token and self.ssp.LegsAmount(self.sspcontext) % 2 == 1: 

627 # AUTH 3 for certain SSPs (e.g. NTLM) 

628 # "The server MUST NOT respond to an rpc_auth_3 PDU" 

629 self.send( 

630 DceRpc5Auth3(), 

631 auth_verifier=CommonAuthVerifier( 

632 auth_type=self.ssp.auth_type, 

633 auth_level=self.auth_level, 

634 auth_context_id=self.session.auth_context_id, 

635 auth_value=token, 

636 ), 

637 ) 

638 status = GSS_S_COMPLETE 

639 else: 

640 while token: 

641 respcls = DceRpc5AlterContextResp 

642 resp = self.sr1( 

643 DceRpc5AlterContext( 

644 context_elem=self._get_bind_context(interface) 

645 ), 

646 auth_verifier=CommonAuthVerifier( 

647 auth_type=self.ssp.auth_type, 

648 auth_level=self.auth_level, 

649 auth_context_id=self.session.auth_context_id, 

650 auth_value=token, 

651 ), 

652 ) 

653 if respcls not in resp: 

654 status = GSS_S_FAILURE 

655 break 

656 if resp.auth_verifier is None: 

657 status = GSS_S_COMPLETE 

658 break 

659 self.sspcontext, token, status = self.ssp.GSS_Init_sec_context( 

660 self.sspcontext, 

661 input_token=resp.auth_verifier.auth_value, 

662 target_name=target_name or ("host/" + self.host), 

663 ) 

664 else: 

665 log_runtime.error("GSS_Init_sec_context failed with %s !" % status) 

666 

667 # Check context acceptance 

668 if ( 

669 status == GSS_S_COMPLETE 

670 and respcls in resp 

671 and self._check_bind_context(interface, resp.results) 

672 ): 

673 port = resp.sec_addr.port_spec.decode() 

674 ndr = self.session.ndr64 and "NDR64" or "NDR32" 

675 self.ndr64 = self.session.ndr64 

676 if self.verb: 

677 print( 

678 conf.color_theme.success( 

679 f"<< {respcls.__name__} port '{port}' using {ndr}" 

680 ) 

681 ) 

682 self.session.sspcontext = self.sspcontext 

683 self._first_time_on_interface = True 

684 return True 

685 else: 

686 if self.verb: 

687 if DceRpc5BindNak in resp: 

688 err_msg = resp.sprintf( 

689 "reject_reason: %DceRpc5BindNak.provider_reject_reason%" 

690 ) 

691 print(conf.color_theme.fail("! Bind_nak (%s)" % err_msg)) 

692 if DceRpc5BindNak in resp: 

693 if resp[DceRpc5BindNak].payload and not isinstance( 

694 resp[DceRpc5BindNak].payload, conf.raw_layer 

695 ): 

696 resp[DceRpc5BindNak].payload.show() 

697 elif DceRpc5Fault in resp: 

698 if getattr(resp, "status", 0) != 0: 

699 if resp.status in _DCE_RPC_ERROR_CODES: 

700 print( 

701 conf.color_theme.fail( 

702 f"! {_DCE_RPC_ERROR_CODES[resp.status]}" 

703 ) 

704 ) 

705 elif resp.status in STATUS_ERREF: 

706 print( 

707 conf.color_theme.fail(f"! {STATUS_ERREF[resp.status]}") 

708 ) 

709 else: 

710 print(conf.color_theme.fail("! Failure")) 

711 resp.show() 

712 if resp[DceRpc5Fault].payload and not isinstance( 

713 resp[DceRpc5Fault].payload, conf.raw_layer 

714 ): 

715 resp[DceRpc5Fault].payload.show() 

716 else: 

717 print(conf.color_theme.fail("! Failure")) 

718 resp.show() 

719 return False 

720 

721 def bind( 

722 self, 

723 interface: Union[DceRpcInterface, ComInterface], 

724 target_name: Optional[str] = None, 

725 ) -> bool: 

726 """ 

727 Bind the client to an interface 

728 

729 :param interface: the DceRpcInterface object 

730 """ 

731 return self._bind( 

732 interface, 

733 DceRpc5Bind, 

734 DceRpc5BindAck, 

735 target_name=target_name, 

736 ) 

737 

738 def alter_context( 

739 self, 

740 interface: Union[DceRpcInterface, ComInterface], 

741 target_name: Optional[str] = None, 

742 ) -> bool: 

743 """ 

744 Alter context: post-bind context negotiation 

745 

746 :param interface: the DceRpcInterface object 

747 """ 

748 return self._bind( 

749 interface, 

750 DceRpc5AlterContext, 

751 DceRpc5AlterContextResp, 

752 target_name=target_name, 

753 ) 

754 

755 def bind_or_alter( 

756 self, 

757 interface: Union[DceRpcInterface, ComInterface], 

758 target_name: Optional[str] = None, 

759 ) -> bool: 

760 """ 

761 Bind the client to an interface or alter the context if already bound 

762 

763 :param interface: the DceRpcInterface object 

764 """ 

765 if not self.session.rpc_bind_interface: 

766 # No interface is bound 

767 return self.bind(interface, target_name=target_name) 

768 elif self.session.rpc_bind_interface != interface: 

769 # An interface is already bound 

770 return self.alter_context(interface, target_name=target_name) 

771 return True 

772 

773 def open_smbpipe(self, name: str): 

774 """ 

775 Open a certain filehandle with the SMB automaton. 

776 

777 :param name: the name of the pipe 

778 """ 

779 self.ipc_tid = self.smbrpcsock.tree_connect("IPC$") 

780 self.smbrpcsock.open_pipe(name) 

781 

782 def close_smbpipe(self): 

783 """ 

784 Close the previously opened pipe 

785 """ 

786 self.smbrpcsock.set_TID(self.ipc_tid) 

787 self.smbrpcsock.close_pipe() 

788 self.smbrpcsock.tree_disconnect() 

789 

790 def connect_and_bind( 

791 self, 

792 host: str, 

793 interface: DceRpcInterface, 

794 port: Optional[int] = None, 

795 timeout: int = 5, 

796 smb_kwargs={}, 

797 ): 

798 """ 

799 Asks the Endpoint Mapper what address to use to connect to the interface, 

800 then uses connect() followed by a bind() 

801 

802 :param host: the host to connect to 

803 :param interface: the DceRpcInterface object 

804 :param port: (optional, NCACN_NP only) the port to connect to 

805 :param timeout: (optional) the connection timeout (default 5) 

806 """ 

807 # Connect to the interface using the endpoint mapper 

808 self.connect( 

809 host=host, 

810 interface=interface, 

811 port=port, 

812 timeout=timeout, 

813 smb_kwargs=smb_kwargs, 

814 ) 

815 

816 # Bind in RPC 

817 if not self.bind(interface): 

818 raise ValueError("DCE/RPC bind failed") 

819 

820 def epm_map(self, interface): 

821 """ 

822 Calls ept_map (the EndPoint Manager) 

823 """ 

824 if self.ndr64: 

825 ndr_uuid = "NDR64" 

826 ndr_version = 1 

827 else: 

828 ndr_uuid = "NDR 2.0" 

829 ndr_version = 2 

830 pkt = self.sr1_req( 

831 ept_map_Request( 

832 obj=NDRPointer( 

833 referent_id=1, 

834 value=UUID( 

835 Data1=0, 

836 Data2=0, 

837 Data3=0, 

838 Data4=None, 

839 ), 

840 ), 

841 map_tower=NDRPointer( 

842 referent_id=2, 

843 value=twr_p_t( 

844 tower_octet_string=bytes( 

845 protocol_tower_t( 

846 floors=[ 

847 prot_and_addr_t( 

848 lhs_length=19, 

849 protocol_identifier=0xD, 

850 uuid=interface.uuid, 

851 version=interface.major_version, 

852 rhs_length=2, 

853 rhs=interface.minor_version, 

854 ), 

855 prot_and_addr_t( 

856 lhs_length=19, 

857 protocol_identifier=0xD, 

858 uuid=ndr_uuid, 

859 version=ndr_version, 

860 rhs_length=2, 

861 rhs=0, 

862 ), 

863 prot_and_addr_t( 

864 lhs_length=1, 

865 protocol_identifier="RPC connection-oriented protocol", # noqa: E501 

866 rhs_length=2, 

867 rhs=0, 

868 ), 

869 { 

870 DCERPC_Transport.NCACN_IP_TCP: ( 

871 prot_and_addr_t( 

872 lhs_length=1, 

873 protocol_identifier="NCACN_IP_TCP", 

874 rhs_length=2, 

875 rhs=135, 

876 ) 

877 ), 

878 DCERPC_Transport.NCACN_NP: ( 

879 prot_and_addr_t( 

880 lhs_length=1, 

881 protocol_identifier="NCACN_NP", 

882 rhs_length=2, 

883 rhs=b"0\x00", 

884 ) 

885 ), 

886 }[self.transport], 

887 { 

888 DCERPC_Transport.NCACN_IP_TCP: ( 

889 prot_and_addr_t( 

890 lhs_length=1, 

891 protocol_identifier="IP", 

892 rhs_length=4, 

893 rhs="0.0.0.0", 

894 ) 

895 ), 

896 DCERPC_Transport.NCACN_NP: ( 

897 prot_and_addr_t( 

898 lhs_length=1, 

899 protocol_identifier="NCACN_NB", 

900 rhs_length=10, 

901 rhs=b"127.0.0.1\x00", 

902 ) 

903 ), 

904 }[self.transport], 

905 ], 

906 ) 

907 ), 

908 ), 

909 ), 

910 entry_handle=NDRContextHandle( 

911 attributes=0, 

912 uuid=b"\x00" * 16, 

913 ), 

914 max_towers=500, 

915 ndr64=self.ndr64, 

916 ndrendian=self.ndrendian, 

917 ) 

918 ) 

919 if pkt and ept_map_Response in pkt: 

920 status = pkt[ept_map_Response].status 

921 # [MS-RPCE] sect 2.2.1.2.5 

922 if status == 0x00000000: 

923 towers = [ 

924 protocol_tower_t(x.value.tower_octet_string) 

925 for x in pkt[ept_map_Response].ITowers.value[0].value 

926 ] 

927 # Let's do some checks to know we know what we're doing 

928 endpoints = [] 

929 for t in towers: 

930 if t.floors[0].uuid != interface.uuid: 

931 if self.verb: 

932 print( 

933 conf.color_theme.fail( 

934 "! Server answered with a different interface." 

935 ) 

936 ) 

937 raise ValueError 

938 if t.floors[1].sprintf("%uuid%") != ndr_uuid: 

939 if self.verb: 

940 print( 

941 conf.color_theme.fail( 

942 "! Server answered with a different NDR version." 

943 ) 

944 ) 

945 raise ValueError 

946 if self.transport == DCERPC_Transport.NCACN_IP_TCP: 

947 endpoints.append((t.floors[4].rhs, t.floors[3].rhs)) 

948 elif self.transport == DCERPC_Transport.NCACN_NP: 

949 endpoints.append(t.floors[3].rhs.rstrip(b"\x00").decode()) 

950 return endpoints 

951 elif status == 0x16C9A0D6: 

952 if self.verb: 

953 print( 

954 conf.color_theme.fail( 

955 "! Server errored: 'There are no elements that satisfy" 

956 " the specified search criteria'." 

957 ) 

958 ) 

959 raise ValueError 

960 print(conf.color_theme.fail("! Failure.")) 

961 if pkt: 

962 pkt.show() 

963 raise ValueError("EPM Map failed") 

964 

965 

966def get_endpoint( 

967 ip, 

968 interface, 

969 transport=DCERPC_Transport.NCACN_IP_TCP, 

970 ndrendian="little", 

971 verb=True, 

972 ssp=None, 

973 smb_kwargs={}, 

974): 

975 """ 

976 Call the endpoint mapper on a remote IP to find an interface 

977 

978 :param ip: 

979 :param interface: 

980 :param mode: 

981 :param verb: 

982 :param ssp: 

983 

984 :return: a list of connection tuples for this interface 

985 """ 

986 client = DCERPC_Client( 

987 transport, 

988 # EPM only works with NDR32 

989 ndr64=False, 

990 ndrendian=ndrendian, 

991 verb=verb, 

992 ssp=ssp, 

993 ) 

994 

995 if transport == DCERPC_Transport.NCACN_IP_TCP: 

996 endpoint = 135 

997 elif transport == DCERPC_Transport.NCACN_NP: 

998 endpoint = "epmapper" 

999 else: 

1000 raise ValueError("Unknown transport value !") 

1001 

1002 client.connect(ip, endpoint=endpoint, smb_kwargs=smb_kwargs) 

1003 

1004 client.bind(find_dcerpc_interface("ept")) 

1005 try: 

1006 endpoints = client.epm_map(interface) 

1007 finally: 

1008 client.close() 

1009 

1010 return endpoints