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

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

295 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 smb_kwargs.setdefault("HOST", host) 

177 if endpoint is None and interface is not None: 

178 # Figure out the endpoint using the endpoint mapper 

179 

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

181 # IP/TCP 

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

183 endpoints = get_endpoint( 

184 host, 

185 interface, 

186 ndrendian=self.ndrendian, 

187 verb=self.verb, 

188 ) 

189 if endpoints: 

190 _, endpoint = endpoints[0] 

191 else: 

192 raise ValueError( 

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

194 ) 

195 elif self.transport == DCERPC_Transport.NCACN_NP: 

196 # SMB 

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

198 endpoints = get_endpoint( 

199 host, 

200 interface, 

201 transport=self.transport, 

202 ndrendian=self.ndrendian, 

203 verb=self.verb, 

204 ssp=self.ssp, 

205 smb_kwargs=smb_kwargs, 

206 ) 

207 if endpoints: 

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

209 else: 

210 return 

211 

212 # Assign the default port if no port is provided 

213 if port is None: 

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

215 port = endpoint or 135 

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

217 port = 445 

218 else: 

219 raise ValueError( 

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

221 ) 

222 

223 # Start socket and connect 

224 self.host = host 

225 self.port = port 

226 sock = socket.socket() 

227 sock.settimeout(timeout) 

228 if self.verb: 

229 print( 

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

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

232 ) 

233 sock.connect((host, port)) 

234 if self.verb: 

235 print( 

236 conf.color_theme.green( 

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

238 ) 

239 ) 

240 

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

242 if self.auth_level >= DCE_C_AUTHN_LEVEL.PKT_PRIVACY: 

243 smb_kwargs.setdefault("REQUIRE_ENCRYPTION", True) 

244 elif self.auth_level >= DCE_C_AUTHN_LEVEL.PKT_INTEGRITY: 

245 smb_kwargs.setdefault("REQUIRE_SIGNATURE", True) 

246 

247 # We pack the socket into a SMB_RPC_SOCKET 

248 sock = self.smbrpcsock = SMB_RPC_SOCKET.from_tcpsock( 

249 sock, 

250 ssp=self.ssp, 

251 **smb_kwargs, 

252 ) 

253 

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

255 if endpoint is not None: 

256 self.open_smbpipe(endpoint) 

257 

258 self.sock = DceRpcSocket( 

259 sock, 

260 DceRpc5, 

261 **self.dcesockargs, 

262 ) 

263 elif self.transport == DCERPC_Transport.NCACN_IP_TCP: 

264 self.sock = DceRpcSocket( 

265 sock, 

266 DceRpc5, 

267 ssp=self.ssp, 

268 auth_level=self.auth_level, 

269 **self.dcesockargs, 

270 ) 

271 

272 def close(self): 

273 """ 

274 Close the DCE/RPC client. 

275 """ 

276 if self.verb: 

277 print("X Connection closed\n") 

278 self.sock.close() 

279 

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

281 """ 

282 Send/Receive a DCE/RPC message. 

283 

284 The DCE/RPC header is added automatically. 

285 """ 

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

287 pkt = ( 

288 DceRpc5( 

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

290 pfc_flags="PFC_FIRST_FRAG+PFC_LAST_FRAG", 

291 endian=self.ndrendian, 

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

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

294 ) 

295 / pkt 

296 ) 

297 if "pfc_flags" in kwargs: 

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

299 if "objectuuid" in kwargs: 

300 pkt.pfc_flags += "PFC_OBJECT_UUID" 

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

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

303 

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

305 """ 

306 Send a DCE/RPC message. 

307 

308 The DCE/RPC header is added automatically. 

309 """ 

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

311 pkt = ( 

312 DceRpc5( 

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

314 pfc_flags="PFC_FIRST_FRAG+PFC_LAST_FRAG", 

315 endian=self.ndrendian, 

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

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

318 ) 

319 / pkt 

320 ) 

321 if "pfc_flags" in kwargs: 

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

323 if "objectuuid" in kwargs: 

324 pkt.pfc_flags += "PFC_OBJECT_UUID" 

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

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

327 

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

329 """ 

330 Send/Receive a DCE/RPC request. 

331 

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

333 """ 

334 if self.verb: 

335 if "objectuuid" in kwargs: 

336 # COM 

337 print( 

338 conf.color_theme.opening( 

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

340 ) 

341 ) 

342 else: 

343 print( 

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

345 ) 

346 

347 # Add sectrailer if first time talking on this interface 

348 vt_trailer = b"" 

349 if ( 

350 self._first_time_on_interface 

351 and self.transport != DCERPC_Transport.NCACN_NP 

352 ): 

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

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

355 self._first_time_on_interface = False 

356 vt_trailer = DceRpcSecVT( 

357 commands=[ 

358 DceRpcSecVTCommand(SEC_VT_COMMAND_END=1) 

359 / DceRpcSecVTPcontext( 

360 InterfaceId=self.session.rpc_bind_interface.uuid, 

361 Version=self.session.rpc_bind_interface.if_version, 

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

363 TransferVersion=1 if self.ndr64 else 2, 

364 ) 

365 ] 

366 ) 

367 

368 # Optional: force opnum 

369 opnum = {} 

370 if "opnum" in kwargs: 

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

372 

373 # Set NDR64 

374 pkt.ndr64 = self.ndr64 

375 

376 # Send/receive 

377 resp = self.sr1( 

378 DceRpc5Request( 

379 cont_id=self.session.cont_id, 

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

381 **opnum, 

382 ) 

383 / pkt, 

384 vt_trailer=vt_trailer, 

385 **kwargs, 

386 ) 

387 

388 # Parse result 

389 result = None 

390 if DceRpc5Response in resp: 

391 if self.verb: 

392 if "objectuuid" in kwargs: 

393 # COM 

394 print( 

395 conf.color_theme.success( 

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

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

398 ) 

399 ) 

400 else: 

401 print( 

402 conf.color_theme.success( 

403 "<< RESPONSE: %s" 

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

405 ) 

406 ) 

407 result = resp[DceRpc5Response].payload 

408 elif DceRpc5Fault in resp: 

409 if self.verb: 

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

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

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

413 resp[DceRpc5Fault].payload, conf.raw_layer 

414 ): 

415 resp[DceRpc5Fault].payload.show() 

416 result = resp 

417 

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

419 if resp.status in _DCE_RPC_ERROR_CODES: 

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

421 elif resp.status in STATUS_ERREF: 

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

423 else: 

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

425 resp.show() 

426 return result 

427 

428 def _get_bind_context(self, interface): 

429 """ 

430 Internal: get the bind DCE/RPC context. 

431 """ 

432 if interface in self.contexts: 

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

434 # reuse that. 

435 return self.contexts[interface] 

436 

437 # NDR 2.0 

438 contexts = [ 

439 DceRpc5Context( 

440 cont_id=self.next_cont_id, 

441 abstract_syntax=DceRpc5AbstractSyntax( 

442 if_uuid=interface.uuid, 

443 if_version=interface.if_version, 

444 ), 

445 transfer_syntaxes=[ 

446 DceRpc5TransferSyntax( 

447 # NDR 2.0 32-bit 

448 if_uuid="NDR 2.0", 

449 if_version=2, 

450 ) 

451 ], 

452 ), 

453 ] 

454 self.next_cont_id += 1 

455 

456 # NDR64 

457 if self.ndr64: 

458 contexts.append( 

459 DceRpc5Context( 

460 cont_id=self.next_cont_id, 

461 abstract_syntax=DceRpc5AbstractSyntax( 

462 if_uuid=interface.uuid, 

463 if_version=interface.if_version, 

464 ), 

465 transfer_syntaxes=[ 

466 DceRpc5TransferSyntax( 

467 # NDR64 

468 if_uuid="NDR64", 

469 if_version=1, 

470 ) 

471 ], 

472 ) 

473 ) 

474 self.next_cont_id += 1 

475 

476 # BindTimeFeatureNegotiationBitmask 

477 contexts.append( 

478 DceRpc5Context( 

479 cont_id=self.next_cont_id, 

480 abstract_syntax=DceRpc5AbstractSyntax( 

481 if_uuid=interface.uuid, 

482 if_version=interface.if_version, 

483 ), 

484 transfer_syntaxes=[ 

485 DceRpc5TransferSyntax( 

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

487 if_version=1, 

488 ) 

489 ], 

490 ) 

491 ) 

492 self.next_cont_id += 1 

493 

494 # Store contexts for this interface 

495 self.contexts[interface] = contexts 

496 

497 return contexts 

498 

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

500 """ 

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

502 """ 

503 for i, ctx in enumerate(contexts): 

504 if ctx.result == 0: 

505 # Context was accepted. Remove all others from cache 

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

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

508 return True 

509 

510 return False 

511 

512 def _bind( 

513 self, 

514 interface: Union[DceRpcInterface, ComInterface], 

515 reqcls, 

516 respcls, 

517 target_name: Optional[str] = None, 

518 ) -> bool: 

519 """ 

520 Internal: used to send a bind/alter request 

521 """ 

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

523 if self.verb: 

524 print( 

525 conf.color_theme.opening( 

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

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

528 ) 

529 ) 

530 

531 # Do we need an authenticated bind 

532 if not self.ssp or ( 

533 self.sspcontext is not None 

534 or self.transport == DCERPC_Transport.NCACN_NP 

535 and self.auth_level < DCE_C_AUTHN_LEVEL.PKT_INTEGRITY 

536 ): 

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

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

539 resp = self.sr1( 

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

541 auth_verifier=None, 

542 ) 

543 status = GSS_S_COMPLETE 

544 else: 

545 # Perform authentication 

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

547 self.sspcontext, 

548 req_flags=( 

549 # SSPs need to be instantiated with some special flags 

550 # for DCE/RPC usages. 

551 GSS_C_FLAGS.GSS_C_DCE_STYLE 

552 | GSS_C_FLAGS.GSS_C_REPLAY_FLAG 

553 | GSS_C_FLAGS.GSS_C_SEQUENCE_FLAG 

554 | GSS_C_FLAGS.GSS_C_MUTUAL_FLAG 

555 | ( 

556 GSS_C_FLAGS.GSS_C_INTEG_FLAG 

557 if self.auth_level >= DCE_C_AUTHN_LEVEL.PKT_INTEGRITY 

558 else 0 

559 ) 

560 | ( 

561 GSS_C_FLAGS.GSS_C_CONF_FLAG 

562 if self.auth_level >= DCE_C_AUTHN_LEVEL.PKT_PRIVACY 

563 else 0 

564 ) 

565 | ( 

566 GSS_C_FLAGS.GSS_C_IDENTIFY_FLAG 

567 if self.impersonation_type <= RPC_C_IMP_LEVEL.IDENTIFY 

568 else 0 

569 ) 

570 | ( 

571 GSS_C_FLAGS.GSS_C_DELEG_FLAG 

572 if self.impersonation_type == RPC_C_IMP_LEVEL.DELEGATE 

573 else 0 

574 ) 

575 ), 

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

577 ) 

578 

579 if status not in [GSS_S_CONTINUE_NEEDED, GSS_S_COMPLETE]: 

580 # Authentication failed. 

581 self.sspcontext.clifailure() 

582 return False 

583 

584 resp = self.sr1( 

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

586 auth_verifier=( 

587 None 

588 if not self.sspcontext 

589 else CommonAuthVerifier( 

590 auth_type=self.ssp.auth_type, 

591 auth_level=self.auth_level, 

592 auth_context_id=self.session.auth_context_id, 

593 auth_value=token, 

594 ) 

595 ), 

596 pfc_flags=( 

597 "PFC_FIRST_FRAG+PFC_LAST_FRAG" 

598 + ( 

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

600 "+PFC_SUPPORT_HEADER_SIGN" 

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

602 else "" 

603 ) 

604 ), 

605 ) 

606 

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

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

609 interface, resp.results 

610 ): 

611 token = None 

612 status = GSS_S_FAILURE 

613 else: 

614 # Call the underlying SSP 

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

616 self.sspcontext, 

617 input_token=resp.auth_verifier.auth_value, 

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

619 ) 

620 

621 if status in [GSS_S_CONTINUE_NEEDED, GSS_S_COMPLETE]: 

622 # Authentication should continue, in two ways: 

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

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

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

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

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

628 self.send( 

629 DceRpc5Auth3(), 

630 auth_verifier=CommonAuthVerifier( 

631 auth_type=self.ssp.auth_type, 

632 auth_level=self.auth_level, 

633 auth_context_id=self.session.auth_context_id, 

634 auth_value=token, 

635 ), 

636 ) 

637 status = GSS_S_COMPLETE 

638 else: 

639 while token: 

640 respcls = DceRpc5AlterContextResp 

641 resp = self.sr1( 

642 DceRpc5AlterContext( 

643 context_elem=self._get_bind_context(interface) 

644 ), 

645 auth_verifier=CommonAuthVerifier( 

646 auth_type=self.ssp.auth_type, 

647 auth_level=self.auth_level, 

648 auth_context_id=self.session.auth_context_id, 

649 auth_value=token, 

650 ), 

651 ) 

652 if respcls not in resp: 

653 status = GSS_S_FAILURE 

654 break 

655 if resp.auth_verifier is None: 

656 status = GSS_S_COMPLETE 

657 break 

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

659 self.sspcontext, 

660 input_token=resp.auth_verifier.auth_value, 

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

662 ) 

663 else: 

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

665 

666 # Check context acceptance 

667 if ( 

668 status == GSS_S_COMPLETE 

669 and respcls in resp 

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

671 ): 

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

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

674 self.ndr64 = self.session.ndr64 

675 if self.verb: 

676 print( 

677 conf.color_theme.success( 

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

679 ) 

680 ) 

681 self.session.sspcontext = self.sspcontext 

682 self._first_time_on_interface = True 

683 return True 

684 else: 

685 if self.verb: 

686 if DceRpc5BindNak in resp: 

687 err_msg = resp.sprintf( 

688 "reject_reason: %DceRpc5BindNak.provider_reject_reason%" 

689 ) 

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

691 if DceRpc5BindNak in resp: 

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

693 resp[DceRpc5BindNak].payload, conf.raw_layer 

694 ): 

695 resp[DceRpc5BindNak].payload.show() 

696 elif DceRpc5Fault in resp: 

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

698 if resp.status in _DCE_RPC_ERROR_CODES: 

699 print( 

700 conf.color_theme.fail( 

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

702 ) 

703 ) 

704 elif resp.status in STATUS_ERREF: 

705 print( 

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

707 ) 

708 else: 

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

710 resp.show() 

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

712 resp[DceRpc5Fault].payload, conf.raw_layer 

713 ): 

714 resp[DceRpc5Fault].payload.show() 

715 else: 

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

717 resp.show() 

718 return False 

719 

720 def bind( 

721 self, 

722 interface: Union[DceRpcInterface, ComInterface], 

723 target_name: Optional[str] = None, 

724 ) -> bool: 

725 """ 

726 Bind the client to an interface 

727 

728 :param interface: the DceRpcInterface object 

729 """ 

730 return self._bind( 

731 interface, 

732 DceRpc5Bind, 

733 DceRpc5BindAck, 

734 target_name=target_name, 

735 ) 

736 

737 def alter_context( 

738 self, 

739 interface: Union[DceRpcInterface, ComInterface], 

740 target_name: Optional[str] = None, 

741 ) -> bool: 

742 """ 

743 Alter context: post-bind context negotiation 

744 

745 :param interface: the DceRpcInterface object 

746 """ 

747 return self._bind( 

748 interface, 

749 DceRpc5AlterContext, 

750 DceRpc5AlterContextResp, 

751 target_name=target_name, 

752 ) 

753 

754 def bind_or_alter( 

755 self, 

756 interface: Union[DceRpcInterface, ComInterface], 

757 target_name: Optional[str] = None, 

758 ) -> bool: 

759 """ 

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

761 

762 :param interface: the DceRpcInterface object 

763 """ 

764 if not self.session.rpc_bind_interface: 

765 # No interface is bound 

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

767 elif self.session.rpc_bind_interface != interface: 

768 # An interface is already bound 

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

770 return True 

771 

772 def open_smbpipe(self, name: str): 

773 """ 

774 Open a certain filehandle with the SMB automaton. 

775 

776 :param name: the name of the pipe 

777 """ 

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

779 self.smbrpcsock.open_pipe(name) 

780 

781 def close_smbpipe(self): 

782 """ 

783 Close the previously opened pipe 

784 """ 

785 self.smbrpcsock.set_TID(self.ipc_tid) 

786 self.smbrpcsock.close_pipe() 

787 self.smbrpcsock.tree_disconnect() 

788 

789 def connect_and_bind( 

790 self, 

791 host: str, 

792 interface: DceRpcInterface, 

793 port: Optional[int] = None, 

794 timeout: int = 5, 

795 smb_kwargs={}, 

796 ): 

797 """ 

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

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

800 

801 :param host: the host to connect to 

802 :param interface: the DceRpcInterface object 

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

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

805 """ 

806 # Connect to the interface using the endpoint mapper 

807 self.connect( 

808 host=host, 

809 interface=interface, 

810 port=port, 

811 timeout=timeout, 

812 smb_kwargs=smb_kwargs, 

813 ) 

814 

815 # Bind in RPC 

816 self.bind(interface) 

817 

818 def epm_map(self, interface): 

819 """ 

820 Calls ept_map (the EndPoint Manager) 

821 """ 

822 if self.ndr64: 

823 ndr_uuid = "NDR64" 

824 ndr_version = 1 

825 else: 

826 ndr_uuid = "NDR 2.0" 

827 ndr_version = 2 

828 pkt = self.sr1_req( 

829 ept_map_Request( 

830 obj=NDRPointer( 

831 referent_id=1, 

832 value=UUID( 

833 Data1=0, 

834 Data2=0, 

835 Data3=0, 

836 Data4=None, 

837 ), 

838 ), 

839 map_tower=NDRPointer( 

840 referent_id=2, 

841 value=twr_p_t( 

842 tower_octet_string=bytes( 

843 protocol_tower_t( 

844 floors=[ 

845 prot_and_addr_t( 

846 lhs_length=19, 

847 protocol_identifier=0xD, 

848 uuid=interface.uuid, 

849 version=interface.major_version, 

850 rhs_length=2, 

851 rhs=interface.minor_version, 

852 ), 

853 prot_and_addr_t( 

854 lhs_length=19, 

855 protocol_identifier=0xD, 

856 uuid=ndr_uuid, 

857 version=ndr_version, 

858 rhs_length=2, 

859 rhs=0, 

860 ), 

861 prot_and_addr_t( 

862 lhs_length=1, 

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

864 rhs_length=2, 

865 rhs=0, 

866 ), 

867 { 

868 DCERPC_Transport.NCACN_IP_TCP: ( 

869 prot_and_addr_t( 

870 lhs_length=1, 

871 protocol_identifier="NCACN_IP_TCP", 

872 rhs_length=2, 

873 rhs=135, 

874 ) 

875 ), 

876 DCERPC_Transport.NCACN_NP: ( 

877 prot_and_addr_t( 

878 lhs_length=1, 

879 protocol_identifier="NCACN_NP", 

880 rhs_length=2, 

881 rhs=b"0\x00", 

882 ) 

883 ), 

884 }[self.transport], 

885 { 

886 DCERPC_Transport.NCACN_IP_TCP: ( 

887 prot_and_addr_t( 

888 lhs_length=1, 

889 protocol_identifier="IP", 

890 rhs_length=4, 

891 rhs="0.0.0.0", 

892 ) 

893 ), 

894 DCERPC_Transport.NCACN_NP: ( 

895 prot_and_addr_t( 

896 lhs_length=1, 

897 protocol_identifier="NCACN_NB", 

898 rhs_length=10, 

899 rhs=b"127.0.0.1\x00", 

900 ) 

901 ), 

902 }[self.transport], 

903 ], 

904 ) 

905 ), 

906 ), 

907 ), 

908 entry_handle=NDRContextHandle( 

909 attributes=0, 

910 uuid=b"\x00" * 16, 

911 ), 

912 max_towers=500, 

913 ndr64=self.ndr64, 

914 ndrendian=self.ndrendian, 

915 ) 

916 ) 

917 if pkt and ept_map_Response in pkt: 

918 status = pkt[ept_map_Response].status 

919 # [MS-RPCE] sect 2.2.1.2.5 

920 if status == 0x00000000: 

921 towers = [ 

922 protocol_tower_t(x.value.tower_octet_string) 

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

924 ] 

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

926 endpoints = [] 

927 for t in towers: 

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

929 if self.verb: 

930 print( 

931 conf.color_theme.fail( 

932 "! Server answered with a different interface." 

933 ) 

934 ) 

935 raise ValueError 

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

937 if self.verb: 

938 print( 

939 conf.color_theme.fail( 

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

941 ) 

942 ) 

943 raise ValueError 

944 if self.transport == DCERPC_Transport.NCACN_IP_TCP: 

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

946 elif self.transport == DCERPC_Transport.NCACN_NP: 

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

948 return endpoints 

949 elif status == 0x16C9A0D6: 

950 if self.verb: 

951 print( 

952 conf.color_theme.fail( 

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

954 " the specified search criteria'." 

955 ) 

956 ) 

957 raise ValueError 

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

959 if pkt: 

960 pkt.show() 

961 raise ValueError("EPM Map failed") 

962 

963 

964def get_endpoint( 

965 ip, 

966 interface, 

967 transport=DCERPC_Transport.NCACN_IP_TCP, 

968 ndrendian="little", 

969 verb=True, 

970 ssp=None, 

971 smb_kwargs={}, 

972): 

973 """ 

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

975 

976 :param ip: 

977 :param interface: 

978 :param mode: 

979 :param verb: 

980 :param ssp: 

981 

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

983 """ 

984 client = DCERPC_Client( 

985 transport, 

986 # EPM only works with NDR32 

987 ndr64=False, 

988 ndrendian=ndrendian, 

989 verb=verb, 

990 ssp=ssp, 

991 ) 

992 

993 if transport == DCERPC_Transport.NCACN_IP_TCP: 

994 endpoint = 135 

995 elif transport == DCERPC_Transport.NCACN_NP: 

996 endpoint = "epmapper" 

997 else: 

998 raise ValueError("Unknown transport value !") 

999 

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

1001 

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

1003 try: 

1004 endpoints = client.epm_map(interface) 

1005 finally: 

1006 client.close() 

1007 

1008 return endpoints