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

286 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 uuid 

11import socket 

12 

13from scapy.config import conf 

14from scapy.error import log_runtime 

15 

16from scapy.layers.dcerpc import ( 

17 _DCE_RPC_ERROR_CODES, 

18 ComInterface, 

19 CommonAuthVerifier, 

20 DCE_C_AUTHN_LEVEL, 

21 DCERPC_Transport, 

22 DceRpc5, 

23 DceRpc5AbstractSyntax, 

24 DceRpc5AlterContext, 

25 DceRpc5AlterContextResp, 

26 DceRpc5Auth3, 

27 DceRpc5Bind, 

28 DceRpc5BindAck, 

29 DceRpc5BindNak, 

30 DceRpc5Context, 

31 DceRpc5Fault, 

32 DceRpc5Request, 

33 DceRpc5Response, 

34 DceRpc5TransferSyntax, 

35 DceRpcInterface, 

36 DceRpcSecVT, 

37 DceRpcSecVTCommand, 

38 DceRpcSecVTPcontext, 

39 DceRpcSession, 

40 DceRpcSocket, 

41 find_dcerpc_interface, 

42 NDRContextHandle, 

43 NDRPointer, 

44 RPC_C_IMP_LEVEL, 

45) 

46from scapy.layers.gssapi import ( 

47 SSP, 

48 GSS_S_FAILURE, 

49 GSS_S_COMPLETE, 

50 GSS_S_CONTINUE_NEEDED, 

51 GSS_C_FLAGS, 

52) 

53from scapy.layers.smb2 import STATUS_ERREF 

54from scapy.layers.smbclient import ( 

55 SMB_RPC_SOCKET, 

56) 

57 

58# RPC 

59from scapy.layers.msrpce.ept import ( 

60 ept_map_Request, 

61 ept_map_Response, 

62 twr_p_t, 

63 protocol_tower_t, 

64 prot_and_addr_t, 

65 UUID, 

66) 

67 

68# Typing 

69from typing import ( 

70 Optional, 

71 Union, 

72) 

73 

74 

75class DCERPC_Client(object): 

76 """ 

77 A basic DCE/RPC client 

78 

79 :param transport: the transport to use. 

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

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

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

83 :param auth_level: the DCE_C_AUTHN_LEVEL to use 

84 :param impersonation_type: the RPC_C_IMP_LEVEL to use 

85 """ 

86 

87 def __init__( 

88 self, 

89 transport: DCERPC_Transport, 

90 ndr64: Optional[bool] = None, 

91 ndrendian: str = "little", 

92 verb: bool = True, 

93 auth_level: Optional[DCE_C_AUTHN_LEVEL] = None, 

94 impersonation_type: RPC_C_IMP_LEVEL = RPC_C_IMP_LEVEL.DEFAULT, 

95 **kwargs, 

96 ): 

97 self.sock = None 

98 self.transport = transport 

99 assert isinstance( 

100 transport, DCERPC_Transport 

101 ), "transport must be from DCERPC_Transport" 

102 

103 # Counters 

104 self.call_id = 0 

105 self.next_cont_id = 0 # next available context id 

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

107 

108 # Session parameters 

109 if ndr64 is None: 

110 ndr64 = conf.ndr64 

111 self.ndr64: bool = ndr64 

112 self.ndrendian = ndrendian 

113 self.verb = verb 

114 self.host: str = None 

115 self.port: int = -1 

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

117 self.sspcontext = None 

118 if auth_level is not None: 

119 self.auth_level = auth_level 

120 elif self.ssp is not None: 

121 self.auth_level = DCE_C_AUTHN_LEVEL.CONNECT 

122 else: 

123 self.auth_level = DCE_C_AUTHN_LEVEL.NONE 

124 if impersonation_type == RPC_C_IMP_LEVEL.DEFAULT: 

125 # Same default as windows 

126 impersonation_type = RPC_C_IMP_LEVEL.IDENTIFY 

127 self.impersonation_type = impersonation_type 

128 self._first_time_on_interface = True 

129 self.contexts = {} 

130 self.dcesockargs = kwargs 

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

132 

133 @classmethod 

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

135 """ 

136 Build a DCERPC_Client from a SMB_Client.smblink directly 

137 """ 

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

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

140 client.sock = DceRpcSocket( 

141 sock, 

142 DceRpc5, 

143 ssp=client.ssp, 

144 auth_level=client.auth_level, 

145 **client.dcesockargs, 

146 ) 

147 return client 

148 

149 @property 

150 def session(self) -> DceRpcSession: 

151 return self.sock.session 

152 

153 def connect( 

154 self, 

155 host, 

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

157 port: Optional[int] = None, 

158 interface=None, 

159 timeout=5, 

160 smb_kwargs={}, 

161 ): 

162 """ 

163 Initiate a connection. 

164 

165 :param host: the host to connect to 

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

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

168 mapper to find the appropriate endpoint for that interface. 

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

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

171 """ 

172 if endpoint is None and interface is not None: 

173 # Figure out the endpoint using the endpoint mapper 

174 

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

176 # IP/TCP 

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

178 endpoints = get_endpoint( 

179 host, 

180 interface, 

181 ndrendian=self.ndrendian, 

182 verb=self.verb, 

183 ) 

184 if endpoints: 

185 _, endpoint = endpoints[0] 

186 else: 

187 raise ValueError( 

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

189 ) 

190 elif self.transport == DCERPC_Transport.NCACN_NP: 

191 # SMB 

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

193 endpoints = get_endpoint( 

194 host, 

195 interface, 

196 transport=self.transport, 

197 ndrendian=self.ndrendian, 

198 verb=self.verb, 

199 smb_kwargs=smb_kwargs, 

200 ) 

201 if endpoints: 

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

203 else: 

204 return 

205 

206 # Assign the default port if no port is provided 

207 if port is None: 

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

209 port = endpoint or 135 

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

211 port = 445 

212 else: 

213 raise ValueError( 

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

215 ) 

216 

217 # Start socket and connect 

218 self.host = host 

219 self.port = port 

220 sock = socket.socket() 

221 sock.settimeout(timeout) 

222 if self.verb: 

223 print( 

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

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

226 ) 

227 sock.connect((host, port)) 

228 if self.verb: 

229 print( 

230 conf.color_theme.green( 

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

232 ) 

233 ) 

234 

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

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

237 if endpoint is not None: 

238 self.open_smbpipe(endpoint) 

239 

240 # We pack the socket into a SMB_RPC_SOCKET 

241 sock = self.smbrpcsock = SMB_RPC_SOCKET.from_tcpsock( 

242 sock, ssp=self.ssp, **smb_kwargs 

243 ) 

244 self.sock = DceRpcSocket(sock, DceRpc5, **self.dcesockargs) 

245 elif self.transport == DCERPC_Transport.NCACN_IP_TCP: 

246 self.sock = DceRpcSocket( 

247 sock, 

248 DceRpc5, 

249 ssp=self.ssp, 

250 auth_level=self.auth_level, 

251 **self.dcesockargs, 

252 ) 

253 

254 def close(self): 

255 """ 

256 Close the DCE/RPC client. 

257 """ 

258 if self.verb: 

259 print("X Connection closed\n") 

260 self.sock.close() 

261 

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

263 """ 

264 Send/Receive a DCE/RPC message. 

265 

266 The DCE/RPC header is added automatically. 

267 """ 

268 self.call_id += 1 

269 pkt = ( 

270 DceRpc5( 

271 call_id=self.call_id, 

272 pfc_flags="PFC_FIRST_FRAG+PFC_LAST_FRAG", 

273 endian=self.ndrendian, 

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

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

276 ) 

277 / pkt 

278 ) 

279 if "pfc_flags" in kwargs: 

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

281 if "objectuuid" in kwargs: 

282 pkt.pfc_flags += "PFC_OBJECT_UUID" 

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

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

285 

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

287 """ 

288 Send a DCE/RPC message. 

289 

290 The DCE/RPC header is added automatically. 

291 """ 

292 self.call_id += 1 

293 pkt = ( 

294 DceRpc5( 

295 call_id=self.call_id, 

296 pfc_flags="PFC_FIRST_FRAG+PFC_LAST_FRAG", 

297 endian=self.ndrendian, 

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

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

300 ) 

301 / pkt 

302 ) 

303 if "pfc_flags" in kwargs: 

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

305 if "objectuuid" in kwargs: 

306 pkt.pfc_flags += "PFC_OBJECT_UUID" 

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

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

309 

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

311 """ 

312 Send/Receive a DCE/RPC request. 

313 

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

315 """ 

316 if self.verb: 

317 if "objectuuid" in kwargs: 

318 # COM 

319 print( 

320 conf.color_theme.opening( 

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

322 ) 

323 ) 

324 else: 

325 print( 

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

327 ) 

328 # Add sectrailer if first time talking on this interface 

329 vt_trailer = b"" 

330 if ( 

331 self._first_time_on_interface 

332 and self.transport != DCERPC_Transport.NCACN_NP 

333 ): 

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

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

336 self._first_time_on_interface = False 

337 vt_trailer = DceRpcSecVT( 

338 commands=[ 

339 DceRpcSecVTCommand(SEC_VT_COMMAND_END=1) 

340 / DceRpcSecVTPcontext( 

341 InterfaceId=self.session.rpc_bind_interface.uuid, 

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

343 TransferVersion=1 if self.ndr64 else 2, 

344 ) 

345 ] 

346 ) 

347 

348 # Optional: force opnum 

349 opnum = {} 

350 if "opnum" in kwargs: 

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

352 

353 # Send/receive 

354 resp = self.sr1( 

355 DceRpc5Request( 

356 cont_id=self.session.cont_id, 

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

358 **opnum, 

359 ) 

360 / pkt, 

361 vt_trailer=vt_trailer, 

362 **kwargs, 

363 ) 

364 

365 # Parse result 

366 result = None 

367 if DceRpc5Response in resp: 

368 if self.verb: 

369 if "objectuuid" in kwargs: 

370 # COM 

371 print( 

372 conf.color_theme.success( 

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

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

375 ) 

376 ) 

377 else: 

378 print( 

379 conf.color_theme.success( 

380 "<< RESPONSE: %s" 

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

382 ) 

383 ) 

384 result = resp[DceRpc5Response].payload 

385 elif DceRpc5Fault in resp: 

386 if self.verb: 

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

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

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

390 resp[DceRpc5Fault].payload, conf.raw_layer 

391 ): 

392 resp[DceRpc5Fault].payload.show() 

393 result = resp 

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

395 if resp.status in _DCE_RPC_ERROR_CODES: 

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

397 elif resp.status in STATUS_ERREF: 

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

399 else: 

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

401 resp.show() 

402 return result 

403 

404 def _get_bind_context(self, interface): 

405 """ 

406 Internal: get the bind DCE/RPC context. 

407 """ 

408 if interface in self.contexts: 

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

410 # reuse that. 

411 return self.contexts[interface] 

412 

413 # NDR 2.0 

414 contexts = [ 

415 DceRpc5Context( 

416 cont_id=self.next_cont_id, 

417 abstract_syntax=DceRpc5AbstractSyntax( 

418 if_uuid=interface.uuid, 

419 if_version=interface.if_version, 

420 ), 

421 transfer_syntaxes=[ 

422 DceRpc5TransferSyntax( 

423 # NDR 2.0 32-bit 

424 if_uuid="NDR 2.0", 

425 if_version=2, 

426 ) 

427 ], 

428 ), 

429 ] 

430 self.next_cont_id += 1 

431 

432 # NDR64 

433 if self.ndr64: 

434 contexts.append( 

435 DceRpc5Context( 

436 cont_id=self.next_cont_id, 

437 abstract_syntax=DceRpc5AbstractSyntax( 

438 if_uuid=interface.uuid, 

439 if_version=interface.if_version, 

440 ), 

441 transfer_syntaxes=[ 

442 DceRpc5TransferSyntax( 

443 # NDR64 

444 if_uuid="NDR64", 

445 if_version=1, 

446 ) 

447 ], 

448 ) 

449 ) 

450 self.next_cont_id += 1 

451 

452 # BindTimeFeatureNegotiationBitmask 

453 contexts.append( 

454 DceRpc5Context( 

455 cont_id=self.next_cont_id, 

456 abstract_syntax=DceRpc5AbstractSyntax( 

457 if_uuid=interface.uuid, 

458 if_version=interface.if_version, 

459 ), 

460 transfer_syntaxes=[ 

461 DceRpc5TransferSyntax( 

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

463 if_version=1, 

464 ) 

465 ], 

466 ) 

467 ) 

468 self.next_cont_id += 1 

469 

470 # Store contexts for this interface 

471 self.contexts[interface] = contexts 

472 

473 return contexts 

474 

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

476 """ 

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

478 """ 

479 for i, ctx in enumerate(contexts): 

480 if ctx.result == 0: 

481 # Context was accepted. Remove all others from cache 

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

483 return True 

484 

485 return False 

486 

487 def _bind( 

488 self, interface: Union[DceRpcInterface, ComInterface], reqcls, respcls 

489 ) -> bool: 

490 """ 

491 Internal: used to send a bind/alter request 

492 """ 

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

494 if self.verb: 

495 print( 

496 conf.color_theme.opening( 

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

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

499 ) 

500 ) 

501 

502 # Do we need an authenticated bind 

503 if not self.ssp or ( 

504 self.sspcontext is not None 

505 or self.transport == DCERPC_Transport.NCACN_NP 

506 and self.auth_level < DCE_C_AUTHN_LEVEL.PKT_INTEGRITY 

507 ): 

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

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

510 resp = self.sr1( 

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

512 auth_verifier=None, 

513 ) 

514 status = GSS_S_COMPLETE 

515 else: 

516 # Perform authentication 

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

518 self.sspcontext, 

519 req_flags=( 

520 # SSPs need to be instantiated with some special flags 

521 # for DCE/RPC usages. 

522 GSS_C_FLAGS.GSS_C_DCE_STYLE 

523 | GSS_C_FLAGS.GSS_C_REPLAY_FLAG 

524 | GSS_C_FLAGS.GSS_C_SEQUENCE_FLAG 

525 | GSS_C_FLAGS.GSS_C_MUTUAL_FLAG 

526 | ( 

527 GSS_C_FLAGS.GSS_C_INTEG_FLAG 

528 if self.auth_level >= DCE_C_AUTHN_LEVEL.PKT_INTEGRITY 

529 else 0 

530 ) 

531 | ( 

532 GSS_C_FLAGS.GSS_C_CONF_FLAG 

533 if self.auth_level >= DCE_C_AUTHN_LEVEL.PKT_PRIVACY 

534 else 0 

535 ) 

536 | ( 

537 GSS_C_FLAGS.GSS_C_IDENTIFY_FLAG 

538 if self.impersonation_type <= RPC_C_IMP_LEVEL.IDENTIFY 

539 else 0 

540 ) 

541 | ( 

542 GSS_C_FLAGS.GSS_C_DELEG_FLAG 

543 if self.impersonation_type == RPC_C_IMP_LEVEL.DELEGATE 

544 else 0 

545 ) 

546 ), 

547 target_name="host/" + self.host, 

548 ) 

549 

550 if status not in [GSS_S_CONTINUE_NEEDED, GSS_S_COMPLETE]: 

551 # Authentication failed. 

552 self.sspcontext.clifailure() 

553 return False 

554 

555 resp = self.sr1( 

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

557 auth_verifier=( 

558 None 

559 if not self.sspcontext 

560 else CommonAuthVerifier( 

561 auth_type=self.ssp.auth_type, 

562 auth_level=self.auth_level, 

563 auth_context_id=self.session.auth_context_id, 

564 auth_value=token, 

565 ) 

566 ), 

567 pfc_flags=( 

568 "PFC_FIRST_FRAG+PFC_LAST_FRAG" 

569 + ( 

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

571 "+PFC_SUPPORT_HEADER_SIGN" 

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

573 else "" 

574 ) 

575 ), 

576 ) 

577 

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

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

580 interface, resp.results 

581 ): 

582 token = None 

583 status = GSS_S_FAILURE 

584 else: 

585 # Call the underlying SSP 

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

587 self.sspcontext, 

588 input_token=resp.auth_verifier.auth_value, 

589 target_name="host/" + self.host, 

590 ) 

591 

592 if status in [GSS_S_CONTINUE_NEEDED, GSS_S_COMPLETE]: 

593 # Authentication should continue, in two ways: 

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

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

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

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

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

599 self.send( 

600 DceRpc5Auth3(), 

601 auth_verifier=CommonAuthVerifier( 

602 auth_type=self.ssp.auth_type, 

603 auth_level=self.auth_level, 

604 auth_context_id=self.session.auth_context_id, 

605 auth_value=token, 

606 ), 

607 ) 

608 status = GSS_S_COMPLETE 

609 else: 

610 while token: 

611 respcls = DceRpc5AlterContextResp 

612 resp = self.sr1( 

613 DceRpc5AlterContext( 

614 context_elem=self._get_bind_context(interface) 

615 ), 

616 auth_verifier=CommonAuthVerifier( 

617 auth_type=self.ssp.auth_type, 

618 auth_level=self.auth_level, 

619 auth_context_id=self.session.auth_context_id, 

620 auth_value=token, 

621 ), 

622 ) 

623 if respcls not in resp: 

624 status = GSS_S_FAILURE 

625 break 

626 if resp.auth_verifier is None: 

627 status = GSS_S_COMPLETE 

628 break 

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

630 self.sspcontext, 

631 input_token=resp.auth_verifier.auth_value, 

632 target_name="host/" + self.host, 

633 ) 

634 else: 

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

636 

637 # Check context acceptance 

638 if ( 

639 status == GSS_S_COMPLETE 

640 and respcls in resp 

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

642 ): 

643 self.call_id = 0 # reset call id 

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

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

646 self.ndr64 = self.session.ndr64 

647 if self.verb: 

648 print( 

649 conf.color_theme.success( 

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

651 ) 

652 ) 

653 self.session.sspcontext = self.sspcontext 

654 self._first_time_on_interface = True 

655 return True 

656 else: 

657 if self.verb: 

658 if DceRpc5BindNak in resp: 

659 err_msg = resp.sprintf( 

660 "reject_reason: %DceRpc5BindNak.provider_reject_reason%" 

661 ) 

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

663 if DceRpc5BindNak in resp: 

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

665 resp[DceRpc5BindNak].payload, conf.raw_layer 

666 ): 

667 resp[DceRpc5BindNak].payload.show() 

668 elif DceRpc5Fault in resp: 

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

670 if resp.status in _DCE_RPC_ERROR_CODES: 

671 print( 

672 conf.color_theme.fail( 

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

674 ) 

675 ) 

676 elif resp.status in STATUS_ERREF: 

677 print( 

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

679 ) 

680 else: 

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

682 resp.show() 

683 if DceRpc5Fault in resp: 

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

685 resp[DceRpc5Fault].payload, conf.raw_layer 

686 ): 

687 resp[DceRpc5Fault].payload.show() 

688 else: 

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

690 resp.show() 

691 return False 

692 

693 def bind(self, interface: Union[DceRpcInterface, ComInterface]) -> bool: 

694 """ 

695 Bind the client to an interface 

696 

697 :param interface: the DceRpcInterface object 

698 """ 

699 return self._bind(interface, DceRpc5Bind, DceRpc5BindAck) 

700 

701 def alter_context(self, interface: Union[DceRpcInterface, ComInterface]) -> bool: 

702 """ 

703 Alter context: post-bind context negotiation 

704 

705 :param interface: the DceRpcInterface object 

706 """ 

707 return self._bind(interface, DceRpc5AlterContext, DceRpc5AlterContextResp) 

708 

709 def bind_or_alter(self, interface: Union[DceRpcInterface, ComInterface]) -> bool: 

710 """ 

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

712 

713 :param interface: the DceRpcInterface object 

714 """ 

715 if not self.session.rpc_bind_interface: 

716 # No interface is bound 

717 return self.bind(interface) 

718 elif self.session.rpc_bind_interface != interface: 

719 # An interface is already bound 

720 return self.alter_context(interface) 

721 return True 

722 

723 def open_smbpipe(self, name: str): 

724 """ 

725 Open a certain filehandle with the SMB automaton. 

726 

727 :param name: the name of the pipe 

728 """ 

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

730 self.smbrpcsock.open_pipe(name) 

731 

732 def close_smbpipe(self): 

733 """ 

734 Close the previously opened pipe 

735 """ 

736 self.smbrpcsock.set_TID(self.ipc_tid) 

737 self.smbrpcsock.close_pipe() 

738 self.smbrpcsock.tree_disconnect() 

739 

740 def connect_and_bind( 

741 self, 

742 host: str, 

743 interface: DceRpcInterface, 

744 port: Optional[int] = None, 

745 timeout: int = 5, 

746 smb_kwargs={}, 

747 ): 

748 """ 

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

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

751 

752 :param host: the host to connect to 

753 :param interface: the DceRpcInterface object 

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

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

756 """ 

757 # Connect to the interface using the endpoint mapper 

758 self.connect( 

759 host=host, 

760 interface=interface, 

761 port=port, 

762 timeout=timeout, 

763 smb_kwargs=smb_kwargs, 

764 ) 

765 

766 # Bind in RPC 

767 self.bind(interface) 

768 

769 def epm_map(self, interface): 

770 """ 

771 Calls ept_map (the EndPoint Manager) 

772 """ 

773 if self.ndr64: 

774 ndr_uuid = "NDR64" 

775 ndr_version = 1 

776 else: 

777 ndr_uuid = "NDR 2.0" 

778 ndr_version = 2 

779 pkt = self.sr1_req( 

780 ept_map_Request( 

781 obj=NDRPointer( 

782 referent_id=1, 

783 value=UUID( 

784 Data1=0, 

785 Data2=0, 

786 Data3=0, 

787 Data4=None, 

788 ), 

789 ), 

790 map_tower=NDRPointer( 

791 referent_id=2, 

792 value=twr_p_t( 

793 tower_octet_string=bytes( 

794 protocol_tower_t( 

795 floors=[ 

796 prot_and_addr_t( 

797 lhs_length=19, 

798 protocol_identifier=0xD, 

799 uuid=interface.uuid, 

800 version=interface.major_version, 

801 rhs_length=2, 

802 rhs=interface.minor_version, 

803 ), 

804 prot_and_addr_t( 

805 lhs_length=19, 

806 protocol_identifier=0xD, 

807 uuid=ndr_uuid, 

808 version=ndr_version, 

809 rhs_length=2, 

810 rhs=0, 

811 ), 

812 prot_and_addr_t( 

813 lhs_length=1, 

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

815 rhs_length=2, 

816 rhs=0, 

817 ), 

818 { 

819 DCERPC_Transport.NCACN_IP_TCP: ( 

820 prot_and_addr_t( 

821 lhs_length=1, 

822 protocol_identifier="NCACN_IP_TCP", 

823 rhs_length=2, 

824 rhs=135, 

825 ) 

826 ), 

827 DCERPC_Transport.NCACN_NP: ( 

828 prot_and_addr_t( 

829 lhs_length=1, 

830 protocol_identifier="NCACN_NP", 

831 rhs_length=2, 

832 rhs=b"0\x00", 

833 ) 

834 ), 

835 }[self.transport], 

836 { 

837 DCERPC_Transport.NCACN_IP_TCP: ( 

838 prot_and_addr_t( 

839 lhs_length=1, 

840 protocol_identifier="IP", 

841 rhs_length=4, 

842 rhs="0.0.0.0", 

843 ) 

844 ), 

845 DCERPC_Transport.NCACN_NP: ( 

846 prot_and_addr_t( 

847 lhs_length=1, 

848 protocol_identifier="NCACN_NB", 

849 rhs_length=10, 

850 rhs=b"127.0.0.1\x00", 

851 ) 

852 ), 

853 }[self.transport], 

854 ], 

855 ) 

856 ), 

857 ), 

858 ), 

859 entry_handle=NDRContextHandle( 

860 attributes=0, 

861 uuid=b"\x00" * 16, 

862 ), 

863 max_towers=500, 

864 ndr64=self.ndr64, 

865 ndrendian=self.ndrendian, 

866 ) 

867 ) 

868 if pkt and ept_map_Response in pkt: 

869 status = pkt[ept_map_Response].status 

870 # [MS-RPCE] sect 2.2.1.2.5 

871 if status == 0x00000000: 

872 towers = [ 

873 protocol_tower_t(x.value.tower_octet_string) 

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

875 ] 

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

877 endpoints = [] 

878 for t in towers: 

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

880 if self.verb: 

881 print( 

882 conf.color_theme.fail( 

883 "! Server answered with a different interface." 

884 ) 

885 ) 

886 raise ValueError 

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

888 if self.verb: 

889 print( 

890 conf.color_theme.fail( 

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

892 ) 

893 ) 

894 raise ValueError 

895 if self.transport == DCERPC_Transport.NCACN_IP_TCP: 

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

897 elif self.transport == DCERPC_Transport.NCACN_NP: 

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

899 return endpoints 

900 elif status == 0x16C9A0D6: 

901 if self.verb: 

902 pkt.show() 

903 print( 

904 conf.color_theme.fail( 

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

906 " the specified search criteria'." 

907 ) 

908 ) 

909 raise ValueError 

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

911 if pkt: 

912 pkt.show() 

913 raise ValueError("EPM Map failed") 

914 

915 

916def get_endpoint( 

917 ip, 

918 interface, 

919 transport=DCERPC_Transport.NCACN_IP_TCP, 

920 ndrendian="little", 

921 verb=True, 

922 ssp=None, 

923 smb_kwargs={}, 

924): 

925 """ 

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

927 

928 :param ip: 

929 :param interface: 

930 :param mode: 

931 :param verb: 

932 :param ssp: 

933 

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

935 """ 

936 client = DCERPC_Client( 

937 transport, 

938 # EPM only works with NDR32 

939 ndr64=False, 

940 ndrendian=ndrendian, 

941 verb=verb, 

942 ssp=ssp, 

943 ) 

944 

945 if transport == DCERPC_Transport.NCACN_IP_TCP: 

946 endpoint = 135 

947 elif transport == DCERPC_Transport.NCACN_NP: 

948 endpoint = "epmapper" 

949 else: 

950 raise ValueError("Unknown transport value !") 

951 

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

953 

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

955 endpoints = client.epm_map(interface) 

956 

957 client.close() 

958 return endpoints