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

269 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) 

45from scapy.layers.gssapi import ( 

46 SSP, 

47 GSS_S_FAILURE, 

48 GSS_S_COMPLETE, 

49 GSS_S_CONTINUE_NEEDED, 

50 GSS_C_FLAGS, 

51) 

52from scapy.layers.smb2 import STATUS_ERREF 

53from scapy.layers.smbclient import ( 

54 SMB_RPC_SOCKET, 

55) 

56 

57# RPC 

58from scapy.layers.msrpce.ept import ( 

59 ept_map_Request, 

60 ept_map_Response, 

61 twr_p_t, 

62 protocol_tower_t, 

63 prot_and_addr_t, 

64 UUID, 

65) 

66 

67# Typing 

68from typing import ( 

69 Optional, 

70 Union, 

71) 

72 

73 

74class DCERPC_Client(object): 

75 """ 

76 A basic DCE/RPC client 

77 

78 :param transport: the transport to use. 

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

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

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

82 :param auth_level: the DCE_C_AUTHN_LEVEL to use 

83 """ 

84 

85 def __init__( 

86 self, 

87 transport: DCERPC_Transport, 

88 ndr64: Optional[bool] = None, 

89 ndrendian: str = "little", 

90 verb: bool = True, 

91 auth_level: Optional[DCE_C_AUTHN_LEVEL] = None, 

92 auth_context_id: int = 0, 

93 **kwargs, 

94 ): 

95 self.sock = None 

96 self.transport = transport 

97 assert isinstance( 

98 transport, DCERPC_Transport 

99 ), "transport must be from DCERPC_Transport" 

100 

101 # Counters 

102 self.call_id = 0 

103 self.all_cont_id = 0 # number of contexts sent 

104 

105 # Session parameters 

106 if ndr64 is None: 

107 ndr64 = conf.ndr64 

108 self.ndr64: bool = ndr64 

109 self.ndrendian = ndrendian 

110 self.verb = verb 

111 self.host: str = None 

112 self.port: int = -1 

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

114 self.sspcontext = None 

115 if auth_level is not None: 

116 self.auth_level = auth_level 

117 elif self.ssp is not None: 

118 self.auth_level = DCE_C_AUTHN_LEVEL.CONNECT 

119 else: 

120 self.auth_level = DCE_C_AUTHN_LEVEL.NONE 

121 self.auth_context_id = auth_context_id 

122 self._first_time_on_interface = True 

123 self.dcesockargs = kwargs 

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

125 

126 @classmethod 

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

128 """ 

129 Build a DCERPC_Client from a SMB_Client.smblink directly 

130 """ 

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

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

133 client.sock = DceRpcSocket( 

134 sock, 

135 DceRpc5, 

136 ssp=client.ssp, 

137 auth_level=client.auth_level, 

138 auth_context_id=client.auth_context_id, 

139 **client.dcesockargs, 

140 ) 

141 return client 

142 

143 @property 

144 def session(self) -> DceRpcSession: 

145 return self.sock.session 

146 

147 def connect(self, host, port=None, timeout=5, smb_kwargs={}): 

148 """ 

149 Initiate a connection. 

150 

151 :param host: the host to connect to 

152 :param port: (optional) the port to connect to 

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

154 """ 

155 if port is None: 

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

157 port = 135 

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

159 port = 445 

160 else: 

161 raise ValueError( 

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

163 ) 

164 self.host = host 

165 self.port = port 

166 sock = socket.socket() 

167 sock.settimeout(timeout) 

168 if self.verb: 

169 print( 

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

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

172 ) 

173 sock.connect((host, port)) 

174 if self.verb: 

175 print( 

176 conf.color_theme.green( 

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

178 ) 

179 ) 

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

181 # We pack the socket into a SMB_RPC_SOCKET 

182 sock = self.smbrpcsock = SMB_RPC_SOCKET.from_tcpsock( 

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

184 ) 

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

186 elif self.transport == DCERPC_Transport.NCACN_IP_TCP: 

187 self.sock = DceRpcSocket( 

188 sock, 

189 DceRpc5, 

190 ssp=self.ssp, 

191 auth_level=self.auth_level, 

192 auth_context_id=self.auth_context_id, 

193 **self.dcesockargs, 

194 ) 

195 

196 def close(self): 

197 """ 

198 Close the DCE/RPC client. 

199 """ 

200 if self.verb: 

201 print("X Connection closed\n") 

202 self.sock.close() 

203 

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

205 """ 

206 Send/Receive a DCE/RPC message. 

207 

208 The DCE/RPC header is added automatically. 

209 """ 

210 self.call_id += 1 

211 pkt = ( 

212 DceRpc5( 

213 call_id=self.call_id, 

214 pfc_flags="PFC_FIRST_FRAG+PFC_LAST_FRAG", 

215 endian=self.ndrendian, 

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

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

218 ) 

219 / pkt 

220 ) 

221 if "pfc_flags" in kwargs: 

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

223 if "objectuuid" in kwargs: 

224 pkt.pfc_flags += "PFC_OBJECT_UUID" 

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

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

227 

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

229 """ 

230 Send a DCE/RPC message. 

231 

232 The DCE/RPC header is added automatically. 

233 """ 

234 self.call_id += 1 

235 pkt = ( 

236 DceRpc5( 

237 call_id=self.call_id, 

238 pfc_flags="PFC_FIRST_FRAG+PFC_LAST_FRAG", 

239 endian=self.ndrendian, 

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

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

242 ) 

243 / pkt 

244 ) 

245 if "pfc_flags" in kwargs: 

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

247 if "objectuuid" in kwargs: 

248 pkt.pfc_flags += "PFC_OBJECT_UUID" 

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

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

251 

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

253 """ 

254 Send/Receive a DCE/RPC request. 

255 

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

257 """ 

258 if self.verb: 

259 if "objectuuid" in kwargs: 

260 # COM 

261 print( 

262 conf.color_theme.opening( 

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

264 ) 

265 ) 

266 else: 

267 print( 

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

269 ) 

270 # Add sectrailer if first time talking on this interface 

271 vt_trailer = b"" 

272 if ( 

273 self._first_time_on_interface 

274 and self.transport != DCERPC_Transport.NCACN_NP 

275 ): 

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

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

278 self._first_time_on_interface = False 

279 vt_trailer = DceRpcSecVT( 

280 commands=[ 

281 DceRpcSecVTCommand(SEC_VT_COMMAND_END=1) 

282 / DceRpcSecVTPcontext( 

283 InterfaceId=self.session.rpc_bind_interface.uuid, 

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

285 TransferVersion=1 if self.ndr64 else 2, 

286 ) 

287 ] 

288 ) 

289 

290 # Optional: force opnum 

291 opnum = {} 

292 if "opnum" in kwargs: 

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

294 

295 # Send/receive 

296 resp = self.sr1( 

297 DceRpc5Request( 

298 cont_id=self.session.cont_id, 

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

300 **opnum, 

301 ) 

302 / pkt, 

303 vt_trailer=vt_trailer, 

304 **kwargs, 

305 ) 

306 

307 # Parse result 

308 result = None 

309 if DceRpc5Response in resp: 

310 if self.verb: 

311 if "objectuuid" in kwargs: 

312 # COM 

313 print( 

314 conf.color_theme.success( 

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

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

317 ) 

318 ) 

319 else: 

320 print( 

321 conf.color_theme.success( 

322 "<< RESPONSE: %s" 

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

324 ) 

325 ) 

326 result = resp[DceRpc5Response].payload 

327 elif DceRpc5Fault in resp: 

328 if self.verb: 

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

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

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

332 resp[DceRpc5Fault].payload, conf.raw_layer 

333 ): 

334 resp[DceRpc5Fault].payload.show() 

335 result = resp 

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

337 if resp.status in _DCE_RPC_ERROR_CODES: 

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

339 elif resp.status in STATUS_ERREF: 

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

341 else: 

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

343 resp.show() 

344 return result 

345 

346 def _get_bind_context(self, interface): 

347 """ 

348 Internal: get the bind DCE/RPC context. 

349 """ 

350 # NDR 2.0 

351 contexts = [ 

352 DceRpc5Context( 

353 cont_id=self.all_cont_id, 

354 abstract_syntax=DceRpc5AbstractSyntax( 

355 if_uuid=interface.uuid, 

356 if_version=interface.if_version, 

357 ), 

358 transfer_syntaxes=[ 

359 DceRpc5TransferSyntax( 

360 # NDR 2.0 32-bit 

361 if_uuid="NDR 2.0", 

362 if_version=2, 

363 ) 

364 ], 

365 ), 

366 ] 

367 self.all_cont_id += 1 

368 

369 # NDR64 

370 if self.ndr64: 

371 contexts.append( 

372 DceRpc5Context( 

373 cont_id=self.all_cont_id, 

374 abstract_syntax=DceRpc5AbstractSyntax( 

375 if_uuid=interface.uuid, 

376 if_version=interface.if_version, 

377 ), 

378 transfer_syntaxes=[ 

379 DceRpc5TransferSyntax( 

380 # NDR64 

381 if_uuid="NDR64", 

382 if_version=1, 

383 ) 

384 ], 

385 ) 

386 ) 

387 self.all_cont_id += 1 

388 

389 # BindTimeFeatureNegotiationBitmask 

390 contexts.append( 

391 DceRpc5Context( 

392 cont_id=self.all_cont_id, 

393 abstract_syntax=DceRpc5AbstractSyntax( 

394 if_uuid=interface.uuid, 

395 if_version=interface.if_version, 

396 ), 

397 transfer_syntaxes=[ 

398 DceRpc5TransferSyntax( 

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

400 if_version=1, 

401 ) 

402 ], 

403 ) 

404 ) 

405 self.all_cont_id += 1 

406 

407 return contexts 

408 

409 def _bind(self, interface: Union[DceRpcInterface, ComInterface], reqcls, respcls): 

410 """ 

411 Internal: used to send a bind/alter request 

412 """ 

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

414 if self.verb: 

415 print( 

416 conf.color_theme.opening( 

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

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

419 ) 

420 ) 

421 # Do we need an authenticated bind 

422 if not self.ssp or ( 

423 self.sspcontext is not None 

424 or self.transport == DCERPC_Transport.NCACN_NP 

425 and self.auth_level < DCE_C_AUTHN_LEVEL.PKT_INTEGRITY 

426 ): 

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

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

429 resp = self.sr1( 

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

431 auth_verifier=None, 

432 ) 

433 status = GSS_S_COMPLETE 

434 else: 

435 # Perform authentication 

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

437 self.sspcontext, 

438 req_flags=( 

439 # SSPs need to be instantiated with some special flags 

440 # for DCE/RPC usages. 

441 GSS_C_FLAGS.GSS_C_DCE_STYLE 

442 | GSS_C_FLAGS.GSS_C_REPLAY_FLAG 

443 | GSS_C_FLAGS.GSS_C_SEQUENCE_FLAG 

444 | GSS_C_FLAGS.GSS_C_MUTUAL_FLAG 

445 | ( 

446 GSS_C_FLAGS.GSS_C_INTEG_FLAG 

447 if self.auth_level >= DCE_C_AUTHN_LEVEL.PKT_INTEGRITY 

448 else 0 

449 ) 

450 | ( 

451 GSS_C_FLAGS.GSS_C_CONF_FLAG 

452 if self.auth_level >= DCE_C_AUTHN_LEVEL.PKT_PRIVACY 

453 else 0 

454 ) 

455 ), 

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

457 ) 

458 if status not in [GSS_S_CONTINUE_NEEDED, GSS_S_COMPLETE]: 

459 # Authentication failed. 

460 self.sspcontext.clifailure() 

461 return False 

462 resp = self.sr1( 

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

464 auth_verifier=( 

465 None 

466 if not self.sspcontext 

467 else CommonAuthVerifier( 

468 auth_type=self.ssp.auth_type, 

469 auth_level=self.auth_level, 

470 auth_context_id=self.auth_context_id, 

471 auth_value=token, 

472 ) 

473 ), 

474 pfc_flags=( 

475 "PFC_FIRST_FRAG+PFC_LAST_FRAG" 

476 + ( 

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

478 "+PFC_SUPPORT_HEADER_SIGN" 

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

480 else "" 

481 ) 

482 ), 

483 ) 

484 if respcls not in resp: 

485 token = None 

486 status = GSS_S_FAILURE 

487 else: 

488 # Call the underlying SSP 

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

490 self.sspcontext, 

491 token=resp.auth_verifier.auth_value, 

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

493 ) 

494 if status in [GSS_S_CONTINUE_NEEDED, GSS_S_COMPLETE]: 

495 # Authentication should continue, in two ways: 

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

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

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

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

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

501 self.send( 

502 DceRpc5Auth3(), 

503 auth_verifier=CommonAuthVerifier( 

504 auth_type=self.ssp.auth_type, 

505 auth_level=self.auth_level, 

506 auth_context_id=self.auth_context_id, 

507 auth_value=token, 

508 ), 

509 ) 

510 status = GSS_S_COMPLETE 

511 else: 

512 while token: 

513 respcls = DceRpc5AlterContextResp 

514 resp = self.sr1( 

515 DceRpc5AlterContext( 

516 context_elem=self._get_bind_context(interface) 

517 ), 

518 auth_verifier=CommonAuthVerifier( 

519 auth_type=self.ssp.auth_type, 

520 auth_level=self.auth_level, 

521 auth_context_id=self.auth_context_id, 

522 auth_value=token, 

523 ), 

524 ) 

525 if respcls not in resp: 

526 status = GSS_S_FAILURE 

527 break 

528 if resp.auth_verifier is None: 

529 status = GSS_S_COMPLETE 

530 break 

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

532 self.sspcontext, 

533 token=resp.auth_verifier.auth_value, 

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

535 ) 

536 else: 

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

538 # Check context acceptance 

539 if ( 

540 status == GSS_S_COMPLETE 

541 and respcls in resp 

542 and any(x.result == 0 for x in resp.results[: int(self.ndr64) + 1]) 

543 ): 

544 self.call_id = 0 # reset call id 

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

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

547 self.ndr64 = self.session.ndr64 

548 self.cont_id = int(self.session.ndr64) # ctx 0 for NDR32, 1 for NDR64 

549 if self.verb: 

550 print( 

551 conf.color_theme.success( 

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

553 ) 

554 ) 

555 self.session.sspcontext = self.sspcontext 

556 self._first_time_on_interface = True 

557 return True 

558 else: 

559 if self.verb: 

560 if DceRpc5BindNak in resp: 

561 err_msg = resp.sprintf( 

562 "reject_reason: %DceRpc5BindNak.provider_reject_reason%" 

563 ) 

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

565 if DceRpc5BindNak in resp: 

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

567 resp[DceRpc5BindNak].payload, conf.raw_layer 

568 ): 

569 resp[DceRpc5BindNak].payload.show() 

570 elif DceRpc5Fault in resp: 

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

572 if resp.status in _DCE_RPC_ERROR_CODES: 

573 print( 

574 conf.color_theme.fail( 

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

576 ) 

577 ) 

578 elif resp.status in STATUS_ERREF: 

579 print( 

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

581 ) 

582 else: 

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

584 resp.show() 

585 if DceRpc5Fault in resp: 

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

587 resp[DceRpc5Fault].payload, conf.raw_layer 

588 ): 

589 resp[DceRpc5Fault].payload.show() 

590 else: 

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

592 resp.show() 

593 return False 

594 

595 def bind(self, interface: Union[DceRpcInterface, ComInterface]): 

596 """ 

597 Bind the client to an interface 

598 

599 :param interface: the DceRpcInterface object 

600 """ 

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

602 

603 def alter_context(self, interface: Union[DceRpcInterface, ComInterface]): 

604 """ 

605 Alter context: post-bind context negotiation 

606 

607 :param interface: the DceRpcInterface object 

608 """ 

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

610 

611 def bind_or_alter(self, interface: Union[DceRpcInterface, ComInterface]): 

612 """ 

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

614 

615 :param interface: the DceRpcInterface object 

616 """ 

617 if not self.session.rpc_bind_interface: 

618 # No interface is bound 

619 self.bind(interface) 

620 elif self.session.rpc_bind_interface != interface: 

621 # An interface is already bound 

622 self.alter_context(interface) 

623 

624 def open_smbpipe(self, name: str): 

625 """ 

626 Open a certain filehandle with the SMB automaton. 

627 

628 :param name: the name of the pipe 

629 """ 

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

631 self.smbrpcsock.open_pipe(name) 

632 

633 def close_smbpipe(self): 

634 """ 

635 Close the previously opened pipe 

636 """ 

637 self.smbrpcsock.set_TID(self.ipc_tid) 

638 self.smbrpcsock.close_pipe() 

639 self.smbrpcsock.tree_disconnect() 

640 

641 def connect_and_bind( 

642 self, 

643 ip: str, 

644 interface: DceRpcInterface, 

645 port: Optional[int] = None, 

646 timeout: int = 5, 

647 smb_kwargs={}, 

648 ): 

649 """ 

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

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

652 

653 :param ip: the ip to connect to 

654 :param interface: the DceRpcInterface object 

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

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

657 """ 

658 if self.transport == DCERPC_Transport.NCACN_IP_TCP: 

659 # IP/TCP 

660 # 1. ask the endpoint mapper (port 135) for the IP:PORT 

661 endpoints = get_endpoint( 

662 ip, 

663 interface, 

664 ndrendian=self.ndrendian, 

665 verb=self.verb, 

666 ) 

667 if endpoints: 

668 ip, port = endpoints[0] 

669 else: 

670 return 

671 # 2. Connect to that IP:PORT 

672 self.connect(ip, port=port, timeout=timeout) 

673 elif self.transport == DCERPC_Transport.NCACN_NP: 

674 # SMB 

675 # 1. ask the endpoint mapper (over SMB) for the namedpipe 

676 endpoints = get_endpoint( 

677 ip, 

678 interface, 

679 transport=self.transport, 

680 ndrendian=self.ndrendian, 

681 verb=self.verb, 

682 smb_kwargs=smb_kwargs, 

683 ) 

684 if endpoints: 

685 pipename = endpoints[0].lstrip("\\pipe\\") 

686 else: 

687 return 

688 # 2. connect to the SMB server 

689 self.connect(ip, port=port, timeout=timeout, smb_kwargs=smb_kwargs) 

690 # 3. open the new named pipe 

691 self.open_smbpipe(pipename) 

692 # Bind in RPC 

693 self.bind(interface) 

694 

695 def epm_map(self, interface): 

696 """ 

697 Calls ept_map (the EndPoint Manager) 

698 """ 

699 if self.ndr64: 

700 ndr_uuid = "NDR64" 

701 ndr_version = 1 

702 else: 

703 ndr_uuid = "NDR 2.0" 

704 ndr_version = 2 

705 pkt = self.sr1_req( 

706 ept_map_Request( 

707 obj=NDRPointer( 

708 referent_id=1, 

709 value=UUID( 

710 Data1=0, 

711 Data2=0, 

712 Data3=0, 

713 Data4=None, 

714 ), 

715 ), 

716 map_tower=NDRPointer( 

717 referent_id=2, 

718 value=twr_p_t( 

719 tower_octet_string=bytes( 

720 protocol_tower_t( 

721 floors=[ 

722 prot_and_addr_t( 

723 lhs_length=19, 

724 protocol_identifier=0xD, 

725 uuid=interface.uuid, 

726 version=interface.major_version, 

727 rhs_length=2, 

728 rhs=interface.minor_version, 

729 ), 

730 prot_and_addr_t( 

731 lhs_length=19, 

732 protocol_identifier=0xD, 

733 uuid=ndr_uuid, 

734 version=ndr_version, 

735 rhs_length=2, 

736 rhs=0, 

737 ), 

738 prot_and_addr_t( 

739 lhs_length=1, 

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

741 rhs_length=2, 

742 rhs=0, 

743 ), 

744 { 

745 DCERPC_Transport.NCACN_IP_TCP: ( 

746 prot_and_addr_t( 

747 lhs_length=1, 

748 protocol_identifier="NCACN_IP_TCP", 

749 rhs_length=2, 

750 rhs=135, 

751 ) 

752 ), 

753 DCERPC_Transport.NCACN_NP: ( 

754 prot_and_addr_t( 

755 lhs_length=1, 

756 protocol_identifier="NCACN_NP", 

757 rhs_length=2, 

758 rhs=b"0\x00", 

759 ) 

760 ), 

761 }[self.transport], 

762 { 

763 DCERPC_Transport.NCACN_IP_TCP: ( 

764 prot_and_addr_t( 

765 lhs_length=1, 

766 protocol_identifier="IP", 

767 rhs_length=4, 

768 rhs="0.0.0.0", 

769 ) 

770 ), 

771 DCERPC_Transport.NCACN_NP: ( 

772 prot_and_addr_t( 

773 lhs_length=1, 

774 protocol_identifier="NCACN_NB", 

775 rhs_length=10, 

776 rhs=b"127.0.0.1\x00", 

777 ) 

778 ), 

779 }[self.transport], 

780 ], 

781 ) 

782 ), 

783 ), 

784 ), 

785 entry_handle=NDRContextHandle( 

786 attributes=0, 

787 uuid=b"\x00" * 16, 

788 ), 

789 max_towers=500, 

790 ndr64=self.ndr64, 

791 ndrendian=self.ndrendian, 

792 ) 

793 ) 

794 if pkt and ept_map_Response in pkt: 

795 status = pkt[ept_map_Response].status 

796 # [MS-RPCE] sect 2.2.1.2.5 

797 if status == 0x00000000: 

798 towers = [ 

799 protocol_tower_t(x.value.tower_octet_string) 

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

801 ] 

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

803 endpoints = [] 

804 for t in towers: 

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

806 if self.verb: 

807 print( 

808 conf.color_theme.fail( 

809 "! Server answered with a different interface." 

810 ) 

811 ) 

812 raise ValueError 

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

814 if self.verb: 

815 print( 

816 conf.color_theme.fail( 

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

818 ) 

819 ) 

820 raise ValueError 

821 if self.transport == DCERPC_Transport.NCACN_IP_TCP: 

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

823 elif self.transport == DCERPC_Transport.NCACN_NP: 

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

825 return endpoints 

826 elif status == 0x16C9A0D6: 

827 if self.verb: 

828 pkt.show() 

829 print( 

830 conf.color_theme.fail( 

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

832 " the specified search criteria'." 

833 ) 

834 ) 

835 raise ValueError 

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

837 if pkt: 

838 pkt.show() 

839 raise ValueError("EPM Map failed") 

840 

841 

842def get_endpoint( 

843 ip, 

844 interface, 

845 transport=DCERPC_Transport.NCACN_IP_TCP, 

846 ndrendian="little", 

847 verb=True, 

848 ssp=None, 

849 smb_kwargs={}, 

850): 

851 """ 

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

853 

854 :param ip: 

855 :param interface: 

856 :param mode: 

857 :param verb: 

858 :param ssp: 

859 

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

861 """ 

862 client = DCERPC_Client( 

863 transport, 

864 ndr64=False, 

865 ndrendian=ndrendian, 

866 verb=verb, 

867 ssp=ssp, 

868 ) # EPM only works with NDR32 

869 client.connect(ip, smb_kwargs=smb_kwargs) 

870 if transport == DCERPC_Transport.NCACN_NP: # SMB 

871 client.open_smbpipe("epmapper") 

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

873 endpoints = client.epm_map(interface) 

874 client.close() 

875 return endpoints