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