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

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

173 if endpoint is None and interface is not None: 

174 # Figure out the endpoint using the endpoint mapper 

175 

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

177 # IP/TCP 

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

179 endpoints = get_endpoint( 

180 host, 

181 interface, 

182 ndrendian=self.ndrendian, 

183 verb=self.verb, 

184 ) 

185 if endpoints: 

186 _, endpoint = endpoints[0] 

187 else: 

188 raise ValueError( 

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

190 ) 

191 elif self.transport == DCERPC_Transport.NCACN_NP: 

192 # SMB 

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

194 endpoints = get_endpoint( 

195 host, 

196 interface, 

197 transport=self.transport, 

198 ndrendian=self.ndrendian, 

199 verb=self.verb, 

200 smb_kwargs=smb_kwargs, 

201 ) 

202 if endpoints: 

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

204 else: 

205 return 

206 

207 # Assign the default port if no port is provided 

208 if port is None: 

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

210 port = endpoint or 135 

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

212 port = 445 

213 else: 

214 raise ValueError( 

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

216 ) 

217 

218 # Start socket and connect 

219 self.host = host 

220 self.port = port 

221 sock = socket.socket() 

222 sock.settimeout(timeout) 

223 if self.verb: 

224 print( 

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

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

227 ) 

228 sock.connect((host, port)) 

229 if self.verb: 

230 print( 

231 conf.color_theme.green( 

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

233 ) 

234 ) 

235 

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

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

238 if endpoint is not None: 

239 self.open_smbpipe(endpoint) 

240 

241 # We pack the socket into a SMB_RPC_SOCKET 

242 sock = self.smbrpcsock = SMB_RPC_SOCKET.from_tcpsock( 

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

244 ) 

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

246 elif self.transport == DCERPC_Transport.NCACN_IP_TCP: 

247 self.sock = DceRpcSocket( 

248 sock, 

249 DceRpc5, 

250 ssp=self.ssp, 

251 auth_level=self.auth_level, 

252 **self.dcesockargs, 

253 ) 

254 

255 def close(self): 

256 """ 

257 Close the DCE/RPC client. 

258 """ 

259 if self.verb: 

260 print("X Connection closed\n") 

261 self.sock.close() 

262 

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

264 """ 

265 Send/Receive a DCE/RPC message. 

266 

267 The DCE/RPC header is added automatically. 

268 """ 

269 self.call_id += 1 

270 pkt = ( 

271 DceRpc5( 

272 call_id=self.call_id, 

273 pfc_flags="PFC_FIRST_FRAG+PFC_LAST_FRAG", 

274 endian=self.ndrendian, 

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

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

277 ) 

278 / pkt 

279 ) 

280 if "pfc_flags" in kwargs: 

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

282 if "objectuuid" in kwargs: 

283 pkt.pfc_flags += "PFC_OBJECT_UUID" 

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

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

286 

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

288 """ 

289 Send a DCE/RPC message. 

290 

291 The DCE/RPC header is added automatically. 

292 """ 

293 self.call_id += 1 

294 pkt = ( 

295 DceRpc5( 

296 call_id=self.call_id, 

297 pfc_flags="PFC_FIRST_FRAG+PFC_LAST_FRAG", 

298 endian=self.ndrendian, 

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

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

301 ) 

302 / pkt 

303 ) 

304 if "pfc_flags" in kwargs: 

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

306 if "objectuuid" in kwargs: 

307 pkt.pfc_flags += "PFC_OBJECT_UUID" 

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

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

310 

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

312 """ 

313 Send/Receive a DCE/RPC request. 

314 

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

316 """ 

317 if self.verb: 

318 if "objectuuid" in kwargs: 

319 # COM 

320 print( 

321 conf.color_theme.opening( 

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

323 ) 

324 ) 

325 else: 

326 print( 

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

328 ) 

329 # Add sectrailer if first time talking on this interface 

330 vt_trailer = b"" 

331 if ( 

332 self._first_time_on_interface 

333 and self.transport != DCERPC_Transport.NCACN_NP 

334 ): 

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

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

337 self._first_time_on_interface = False 

338 vt_trailer = DceRpcSecVT( 

339 commands=[ 

340 DceRpcSecVTCommand(SEC_VT_COMMAND_END=1) 

341 / DceRpcSecVTPcontext( 

342 InterfaceId=self.session.rpc_bind_interface.uuid, 

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

344 TransferVersion=1 if self.ndr64 else 2, 

345 ) 

346 ] 

347 ) 

348 

349 # Optional: force opnum 

350 opnum = {} 

351 if "opnum" in kwargs: 

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

353 

354 # Send/receive 

355 resp = self.sr1( 

356 DceRpc5Request( 

357 cont_id=self.session.cont_id, 

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

359 **opnum, 

360 ) 

361 / pkt, 

362 vt_trailer=vt_trailer, 

363 **kwargs, 

364 ) 

365 

366 # Parse result 

367 result = None 

368 if DceRpc5Response in resp: 

369 if self.verb: 

370 if "objectuuid" in kwargs: 

371 # COM 

372 print( 

373 conf.color_theme.success( 

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

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

376 ) 

377 ) 

378 else: 

379 print( 

380 conf.color_theme.success( 

381 "<< RESPONSE: %s" 

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

383 ) 

384 ) 

385 result = resp[DceRpc5Response].payload 

386 elif DceRpc5Fault in resp: 

387 if self.verb: 

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

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

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

391 resp[DceRpc5Fault].payload, conf.raw_layer 

392 ): 

393 resp[DceRpc5Fault].payload.show() 

394 result = resp 

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

396 if resp.status in _DCE_RPC_ERROR_CODES: 

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

398 elif resp.status in STATUS_ERREF: 

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

400 else: 

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

402 resp.show() 

403 return result 

404 

405 def _get_bind_context(self, interface): 

406 """ 

407 Internal: get the bind DCE/RPC context. 

408 """ 

409 if interface in self.contexts: 

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

411 # reuse that. 

412 return self.contexts[interface] 

413 

414 # NDR 2.0 

415 contexts = [ 

416 DceRpc5Context( 

417 cont_id=self.next_cont_id, 

418 abstract_syntax=DceRpc5AbstractSyntax( 

419 if_uuid=interface.uuid, 

420 if_version=interface.if_version, 

421 ), 

422 transfer_syntaxes=[ 

423 DceRpc5TransferSyntax( 

424 # NDR 2.0 32-bit 

425 if_uuid="NDR 2.0", 

426 if_version=2, 

427 ) 

428 ], 

429 ), 

430 ] 

431 self.next_cont_id += 1 

432 

433 # NDR64 

434 if self.ndr64: 

435 contexts.append( 

436 DceRpc5Context( 

437 cont_id=self.next_cont_id, 

438 abstract_syntax=DceRpc5AbstractSyntax( 

439 if_uuid=interface.uuid, 

440 if_version=interface.if_version, 

441 ), 

442 transfer_syntaxes=[ 

443 DceRpc5TransferSyntax( 

444 # NDR64 

445 if_uuid="NDR64", 

446 if_version=1, 

447 ) 

448 ], 

449 ) 

450 ) 

451 self.next_cont_id += 1 

452 

453 # BindTimeFeatureNegotiationBitmask 

454 contexts.append( 

455 DceRpc5Context( 

456 cont_id=self.next_cont_id, 

457 abstract_syntax=DceRpc5AbstractSyntax( 

458 if_uuid=interface.uuid, 

459 if_version=interface.if_version, 

460 ), 

461 transfer_syntaxes=[ 

462 DceRpc5TransferSyntax( 

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

464 if_version=1, 

465 ) 

466 ], 

467 ) 

468 ) 

469 self.next_cont_id += 1 

470 

471 # Store contexts for this interface 

472 self.contexts[interface] = contexts 

473 

474 return contexts 

475 

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

477 """ 

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

479 """ 

480 for i, ctx in enumerate(contexts): 

481 if ctx.result == 0: 

482 # Context was accepted. Remove all others from cache 

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

484 return True 

485 

486 return False 

487 

488 def _bind( 

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

490 ) -> bool: 

491 """ 

492 Internal: used to send a bind/alter request 

493 """ 

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

495 if self.verb: 

496 print( 

497 conf.color_theme.opening( 

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

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

500 ) 

501 ) 

502 

503 # Do we need an authenticated bind 

504 if not self.ssp or ( 

505 self.sspcontext is not None 

506 or self.transport == DCERPC_Transport.NCACN_NP 

507 and self.auth_level < DCE_C_AUTHN_LEVEL.PKT_INTEGRITY 

508 ): 

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

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

511 resp = self.sr1( 

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

513 auth_verifier=None, 

514 ) 

515 status = GSS_S_COMPLETE 

516 else: 

517 # Perform authentication 

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

519 self.sspcontext, 

520 req_flags=( 

521 # SSPs need to be instantiated with some special flags 

522 # for DCE/RPC usages. 

523 GSS_C_FLAGS.GSS_C_DCE_STYLE 

524 | GSS_C_FLAGS.GSS_C_REPLAY_FLAG 

525 | GSS_C_FLAGS.GSS_C_SEQUENCE_FLAG 

526 | GSS_C_FLAGS.GSS_C_MUTUAL_FLAG 

527 | ( 

528 GSS_C_FLAGS.GSS_C_INTEG_FLAG 

529 if self.auth_level >= DCE_C_AUTHN_LEVEL.PKT_INTEGRITY 

530 else 0 

531 ) 

532 | ( 

533 GSS_C_FLAGS.GSS_C_CONF_FLAG 

534 if self.auth_level >= DCE_C_AUTHN_LEVEL.PKT_PRIVACY 

535 else 0 

536 ) 

537 | ( 

538 GSS_C_FLAGS.GSS_C_IDENTIFY_FLAG 

539 if self.impersonation_type <= RPC_C_IMP_LEVEL.IDENTIFY 

540 else 0 

541 ) 

542 | ( 

543 GSS_C_FLAGS.GSS_C_DELEG_FLAG 

544 if self.impersonation_type == RPC_C_IMP_LEVEL.DELEGATE 

545 else 0 

546 ) 

547 ), 

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

549 ) 

550 

551 if status not in [GSS_S_CONTINUE_NEEDED, GSS_S_COMPLETE]: 

552 # Authentication failed. 

553 self.sspcontext.clifailure() 

554 return False 

555 

556 resp = self.sr1( 

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

558 auth_verifier=( 

559 None 

560 if not self.sspcontext 

561 else CommonAuthVerifier( 

562 auth_type=self.ssp.auth_type, 

563 auth_level=self.auth_level, 

564 auth_context_id=self.session.auth_context_id, 

565 auth_value=token, 

566 ) 

567 ), 

568 pfc_flags=( 

569 "PFC_FIRST_FRAG+PFC_LAST_FRAG" 

570 + ( 

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

572 "+PFC_SUPPORT_HEADER_SIGN" 

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

574 else "" 

575 ) 

576 ), 

577 ) 

578 

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

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

581 interface, resp.results 

582 ): 

583 token = None 

584 status = GSS_S_FAILURE 

585 else: 

586 # Call the underlying SSP 

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

588 self.sspcontext, 

589 input_token=resp.auth_verifier.auth_value, 

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

591 ) 

592 

593 if status in [GSS_S_CONTINUE_NEEDED, GSS_S_COMPLETE]: 

594 # Authentication should continue, in two ways: 

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

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

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

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

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

600 self.send( 

601 DceRpc5Auth3(), 

602 auth_verifier=CommonAuthVerifier( 

603 auth_type=self.ssp.auth_type, 

604 auth_level=self.auth_level, 

605 auth_context_id=self.session.auth_context_id, 

606 auth_value=token, 

607 ), 

608 ) 

609 status = GSS_S_COMPLETE 

610 else: 

611 while token: 

612 respcls = DceRpc5AlterContextResp 

613 resp = self.sr1( 

614 DceRpc5AlterContext( 

615 context_elem=self._get_bind_context(interface) 

616 ), 

617 auth_verifier=CommonAuthVerifier( 

618 auth_type=self.ssp.auth_type, 

619 auth_level=self.auth_level, 

620 auth_context_id=self.session.auth_context_id, 

621 auth_value=token, 

622 ), 

623 ) 

624 if respcls not in resp: 

625 status = GSS_S_FAILURE 

626 break 

627 if resp.auth_verifier is None: 

628 status = GSS_S_COMPLETE 

629 break 

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

631 self.sspcontext, 

632 input_token=resp.auth_verifier.auth_value, 

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

634 ) 

635 else: 

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

637 

638 # Check context acceptance 

639 if ( 

640 status == GSS_S_COMPLETE 

641 and respcls in resp 

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

643 ): 

644 self.call_id = 0 # reset call id 

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

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

647 self.ndr64 = self.session.ndr64 

648 if self.verb: 

649 print( 

650 conf.color_theme.success( 

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

652 ) 

653 ) 

654 self.session.sspcontext = self.sspcontext 

655 self._first_time_on_interface = True 

656 return True 

657 else: 

658 if self.verb: 

659 if DceRpc5BindNak in resp: 

660 err_msg = resp.sprintf( 

661 "reject_reason: %DceRpc5BindNak.provider_reject_reason%" 

662 ) 

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

664 if DceRpc5BindNak in resp: 

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

666 resp[DceRpc5BindNak].payload, conf.raw_layer 

667 ): 

668 resp[DceRpc5BindNak].payload.show() 

669 elif DceRpc5Fault in resp: 

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

671 if resp.status in _DCE_RPC_ERROR_CODES: 

672 print( 

673 conf.color_theme.fail( 

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

675 ) 

676 ) 

677 elif resp.status in STATUS_ERREF: 

678 print( 

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

680 ) 

681 else: 

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

683 resp.show() 

684 if DceRpc5Fault in resp: 

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

686 resp[DceRpc5Fault].payload, conf.raw_layer 

687 ): 

688 resp[DceRpc5Fault].payload.show() 

689 else: 

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

691 resp.show() 

692 return False 

693 

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

695 """ 

696 Bind the client to an interface 

697 

698 :param interface: the DceRpcInterface object 

699 """ 

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

701 

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

703 """ 

704 Alter context: post-bind context negotiation 

705 

706 :param interface: the DceRpcInterface object 

707 """ 

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

709 

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

711 """ 

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

713 

714 :param interface: the DceRpcInterface object 

715 """ 

716 if not self.session.rpc_bind_interface: 

717 # No interface is bound 

718 return self.bind(interface) 

719 elif self.session.rpc_bind_interface != interface: 

720 # An interface is already bound 

721 return self.alter_context(interface) 

722 return True 

723 

724 def open_smbpipe(self, name: str): 

725 """ 

726 Open a certain filehandle with the SMB automaton. 

727 

728 :param name: the name of the pipe 

729 """ 

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

731 self.smbrpcsock.open_pipe(name) 

732 

733 def close_smbpipe(self): 

734 """ 

735 Close the previously opened pipe 

736 """ 

737 self.smbrpcsock.set_TID(self.ipc_tid) 

738 self.smbrpcsock.close_pipe() 

739 self.smbrpcsock.tree_disconnect() 

740 

741 def connect_and_bind( 

742 self, 

743 host: str, 

744 interface: DceRpcInterface, 

745 port: Optional[int] = None, 

746 timeout: int = 5, 

747 smb_kwargs={}, 

748 ): 

749 """ 

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

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

752 

753 :param host: the host to connect to 

754 :param interface: the DceRpcInterface object 

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

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

757 """ 

758 # Connect to the interface using the endpoint mapper 

759 self.connect( 

760 host=host, 

761 interface=interface, 

762 port=port, 

763 timeout=timeout, 

764 smb_kwargs=smb_kwargs, 

765 ) 

766 

767 # Bind in RPC 

768 self.bind(interface) 

769 

770 def epm_map(self, interface): 

771 """ 

772 Calls ept_map (the EndPoint Manager) 

773 """ 

774 if self.ndr64: 

775 ndr_uuid = "NDR64" 

776 ndr_version = 1 

777 else: 

778 ndr_uuid = "NDR 2.0" 

779 ndr_version = 2 

780 pkt = self.sr1_req( 

781 ept_map_Request( 

782 obj=NDRPointer( 

783 referent_id=1, 

784 value=UUID( 

785 Data1=0, 

786 Data2=0, 

787 Data3=0, 

788 Data4=None, 

789 ), 

790 ), 

791 map_tower=NDRPointer( 

792 referent_id=2, 

793 value=twr_p_t( 

794 tower_octet_string=bytes( 

795 protocol_tower_t( 

796 floors=[ 

797 prot_and_addr_t( 

798 lhs_length=19, 

799 protocol_identifier=0xD, 

800 uuid=interface.uuid, 

801 version=interface.major_version, 

802 rhs_length=2, 

803 rhs=interface.minor_version, 

804 ), 

805 prot_and_addr_t( 

806 lhs_length=19, 

807 protocol_identifier=0xD, 

808 uuid=ndr_uuid, 

809 version=ndr_version, 

810 rhs_length=2, 

811 rhs=0, 

812 ), 

813 prot_and_addr_t( 

814 lhs_length=1, 

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

816 rhs_length=2, 

817 rhs=0, 

818 ), 

819 { 

820 DCERPC_Transport.NCACN_IP_TCP: ( 

821 prot_and_addr_t( 

822 lhs_length=1, 

823 protocol_identifier="NCACN_IP_TCP", 

824 rhs_length=2, 

825 rhs=135, 

826 ) 

827 ), 

828 DCERPC_Transport.NCACN_NP: ( 

829 prot_and_addr_t( 

830 lhs_length=1, 

831 protocol_identifier="NCACN_NP", 

832 rhs_length=2, 

833 rhs=b"0\x00", 

834 ) 

835 ), 

836 }[self.transport], 

837 { 

838 DCERPC_Transport.NCACN_IP_TCP: ( 

839 prot_and_addr_t( 

840 lhs_length=1, 

841 protocol_identifier="IP", 

842 rhs_length=4, 

843 rhs="0.0.0.0", 

844 ) 

845 ), 

846 DCERPC_Transport.NCACN_NP: ( 

847 prot_and_addr_t( 

848 lhs_length=1, 

849 protocol_identifier="NCACN_NB", 

850 rhs_length=10, 

851 rhs=b"127.0.0.1\x00", 

852 ) 

853 ), 

854 }[self.transport], 

855 ], 

856 ) 

857 ), 

858 ), 

859 ), 

860 entry_handle=NDRContextHandle( 

861 attributes=0, 

862 uuid=b"\x00" * 16, 

863 ), 

864 max_towers=500, 

865 ndr64=self.ndr64, 

866 ndrendian=self.ndrendian, 

867 ) 

868 ) 

869 if pkt and ept_map_Response in pkt: 

870 status = pkt[ept_map_Response].status 

871 # [MS-RPCE] sect 2.2.1.2.5 

872 if status == 0x00000000: 

873 towers = [ 

874 protocol_tower_t(x.value.tower_octet_string) 

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

876 ] 

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

878 endpoints = [] 

879 for t in towers: 

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

881 if self.verb: 

882 print( 

883 conf.color_theme.fail( 

884 "! Server answered with a different interface." 

885 ) 

886 ) 

887 raise ValueError 

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

889 if self.verb: 

890 print( 

891 conf.color_theme.fail( 

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

893 ) 

894 ) 

895 raise ValueError 

896 if self.transport == DCERPC_Transport.NCACN_IP_TCP: 

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

898 elif self.transport == DCERPC_Transport.NCACN_NP: 

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

900 return endpoints 

901 elif status == 0x16C9A0D6: 

902 if self.verb: 

903 pkt.show() 

904 print( 

905 conf.color_theme.fail( 

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

907 " the specified search criteria'." 

908 ) 

909 ) 

910 raise ValueError 

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

912 if pkt: 

913 pkt.show() 

914 raise ValueError("EPM Map failed") 

915 

916 

917def get_endpoint( 

918 ip, 

919 interface, 

920 transport=DCERPC_Transport.NCACN_IP_TCP, 

921 ndrendian="little", 

922 verb=True, 

923 ssp=None, 

924 smb_kwargs={}, 

925): 

926 """ 

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

928 

929 :param ip: 

930 :param interface: 

931 :param mode: 

932 :param verb: 

933 :param ssp: 

934 

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

936 """ 

937 client = DCERPC_Client( 

938 transport, 

939 # EPM only works with NDR32 

940 ndr64=False, 

941 ndrendian=ndrendian, 

942 verb=verb, 

943 ssp=ssp, 

944 ) 

945 

946 if transport == DCERPC_Transport.NCACN_IP_TCP: 

947 endpoint = 135 

948 elif transport == DCERPC_Transport.NCACN_NP: 

949 endpoint = "epmapper" 

950 else: 

951 raise ValueError("Unknown transport value !") 

952 

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

954 

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

956 endpoints = client.epm_map(interface) 

957 

958 client.close() 

959 return endpoints