Coverage for /root/GitHubProjects/impacket/impacket/examples/ntlmrelayx/clients/rpcrelayclient.py : 0%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Impacket - Collection of Python classes for working with network protocols.
2#
3# SECUREAUTH LABS. Copyright (C) 2020 SecureAuth Corporation. All rights reserved.
4#
5# This software is provided under a slightly modified version
6# of the Apache Software License. See the accompanying LICENSE file
7# for more information.
8#
9# Authors:
10# Arseniy Sharoglazov <mohemiv@gmail.com> / Positive Technologies (https://www.ptsecurity.com/)
11# Based on @agsolino and @_dirkjan code
12#
14from struct import unpack
16from impacket import LOG
17from impacket.examples.ntlmrelayx.clients import ProtocolClient
18from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED
19from impacket.ntlm import NTLMAuthChallenge
20from impacket.spnego import SPNEGO_NegTokenResp
22from impacket.dcerpc.v5 import transport, rpcrt, epm, tsch
23from impacket.dcerpc.v5.ndr import NDRCALL
24from impacket.dcerpc.v5.rpcrt import DCERPC_v5, MSRPCBind, CtxItem, MSRPCHeader, SEC_TRAILER, MSRPCBindAck, \
25 MSRPCRespHeader, MSRPCBindNak, DCERPCException, RPC_C_AUTHN_WINNT, RPC_C_AUTHN_LEVEL_CONNECT, \
26 rpc_status_codes, rpc_provider_reason
28PROTOCOL_CLIENT_CLASS = "RPCRelayClient"
30class RPCRelayClientException(Exception):
31 pass
33class MYDCERPC_v5(DCERPC_v5):
34 def __init__(self, transport):
35 DCERPC_v5.__init__(self, transport)
37 def sendBindType1(self, iface_uuid, auth_data):
38 bind = MSRPCBind()
40 item = CtxItem()
41 item['AbstractSyntax'] = iface_uuid
42 item['TransferSyntax'] = self.transfer_syntax
43 item['ContextID'] = 0
44 item['TransItems'] = 1
45 bind.addCtxItem(item)
47 packet = MSRPCHeader()
48 packet['type'] = rpcrt.MSRPC_BIND
49 packet['pduData'] = bind.getData()
50 packet['call_id'] = 0
52 sec_trailer = SEC_TRAILER()
53 sec_trailer['auth_type'] = RPC_C_AUTHN_WINNT
54 sec_trailer['auth_level'] = RPC_C_AUTHN_LEVEL_CONNECT
55 sec_trailer['auth_ctx_id'] = 79231
57 pad = (4 - (len(packet.get_packet()) % 4)) % 4
58 if pad != 0:
59 packet['pduData'] += b'\xFF' * pad
60 sec_trailer['auth_pad_len'] = pad
62 packet['sec_trailer'] = sec_trailer
63 packet['auth_data'] = auth_data
65 self._transport.send(packet.get_packet())
67 s = self._transport.recv()
69 if s != 0:
70 resp = MSRPCHeader(s)
71 else:
72 return 0 #mmm why not None?
74 if resp['type'] == rpcrt.MSRPC_BINDACK or resp['type'] == rpcrt.MSRPC_ALTERCTX_R:
75 bindResp = MSRPCBindAck(resp.getData())
76 elif resp['type'] == rpcrt.MSRPC_BINDNAK or resp['type'] == rpcrt.MSRPC_FAULT:
77 if resp['type'] == rpcrt.MSRPC_FAULT:
78 resp = MSRPCRespHeader(resp.getData())
79 status_code = unpack('<L', resp['pduData'][:4])[0]
80 else:
81 resp = MSRPCBindNak(resp['pduData'])
82 status_code = resp['RejectedReason']
83 if status_code in rpc_status_codes:
84 raise DCERPCException(error_code = status_code)
85 elif status_code in rpc_provider_reason:
86 raise DCERPCException("Bind context rejected: %s" % rpc_provider_reason[status_code])
87 else:
88 raise DCERPCException('Unknown DCE RPC fault status code: %.8x' % status_code)
89 else:
90 raise DCERPCException('Unknown DCE RPC packet type received: %d' % resp['type'])
92 self.set_max_tfrag(bindResp['max_rfrag'])
94 return bindResp
96 def sendBindType3(self, auth_data):
97 sec_trailer = SEC_TRAILER()
98 sec_trailer['auth_type'] = RPC_C_AUTHN_WINNT
99 sec_trailer['auth_level'] = RPC_C_AUTHN_LEVEL_CONNECT
100 sec_trailer['auth_ctx_id'] = 79231
102 auth3 = MSRPCHeader()
103 auth3['type'] = rpcrt.MSRPC_AUTH3
105 # pad (4 bytes): Can be set to any arbitrary value when set and MUST be
106 # ignored on receipt. The pad field MUST be immediately followed by a
107 # sec_trailer structure whose layout, location, and alignment are as
108 # specified in section 2.2.2.11
109 auth3['pduData'] = b' '
110 auth3['sec_trailer'] = sec_trailer
111 auth3['auth_data'] = auth_data
112 auth3['call_id'] = 0
114 self._transport.send(auth3.get_packet(), forceWriteAndx = 1)
116class DummyOp(NDRCALL):
117 opnum = 255
118 structure = (
119 )
121class RPCRelayClient(ProtocolClient):
122 PLUGIN_NAME = "RPC"
124 def __init__(self, serverConfig, target, targetPort=None, extendedSecurity=True):
125 ProtocolClient.__init__(self, serverConfig, target, targetPort, extendedSecurity)
127 # TODO: support relaying RPC to different endpoints (e.g. DCOM, SpoolSS)
128 # TODO: create a single LOG interface for ntlmrelayx to provide a user info which message/error to which thread belongs
129 self.endpoint = serverConfig.rpc_mode
131 if self.endpoint == "TSCH":
132 self.endpoint_uuid = tsch.MSRPC_UUID_TSCHS
133 else:
134 raise NotImplementedError("Not implemented!")
136 if self.serverConfig.rpc_use_smb:
137 if self.endpoint == "TSCH":
138 self.stringbinding = "ncacn_np:%s[\\pipe\\atsvc]" % target.netloc
139 else:
140 raise NotImplementedError("Not implemented!")
141 else:
142 LOG.debug("Connecting to ncacn_ip_tcp:%s[135] to determine %s stringbinding" % (target.netloc, self.endpoint))
143 self.stringbinding = epm.hept_map(target.netloc, self.endpoint_uuid, protocol='ncacn_ip_tcp')
145 LOG.debug("%s stringbinding is %s" % (self.endpoint, self.stringbinding))
147 def initConnection(self):
148 rpctransport = transport.DCERPCTransportFactory(self.stringbinding)
150 if self.serverConfig.rpc_use_smb:
151 LOG.info("Authenticating to smb://%s:%d with creds provided in cmdline" % (self.target.netloc, self.serverConfig.rpc_smb_port))
152 rpctransport.set_credentials(self.serverConfig.smbuser, self.serverConfig.smbpass, self.serverConfig.smbdomain, \
153 self.serverConfig.smblmhash, self.serverConfig.smbnthash)
154 rpctransport.set_dport(self.serverConfig.rpc_smb_port)
156 self.session = MYDCERPC_v5(rpctransport)
157 self.session.set_auth_level(RPC_C_AUTHN_LEVEL_CONNECT)
158 self.session.connect()
160 if self.serverConfig.rpc_use_smb:
161 LOG.info("Authentication to smb://%s:%d succeeded" % (self.target.netloc, self.serverConfig.rpc_smb_port))
163 return True
165 def sendNegotiate(self, auth_data):
166 bindResp = self.session.sendBindType1(self.endpoint_uuid, auth_data)
168 challenge = NTLMAuthChallenge()
169 challenge.fromString(bindResp['auth_data'])
171 return challenge
173 def sendAuth(self, authenticateMessageBlob, serverChallenge=None):
174 if unpack('B', authenticateMessageBlob[:1])[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP:
175 respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob)
176 auth_data = respToken2['ResponseToken']
177 else:
178 auth_data = authenticateMessageBlob
180 self.session.sendBindType3(auth_data)
182 try:
183 req = DummyOp()
184 self.session.request(req)
185 except DCERPCException as e:
186 if 'nca_s_op_rng_error' in str(e) or 'RPC_E_INVALID_HEADER' in str(e):
187 return None, STATUS_SUCCESS
188 elif 'rpc_s_access_denied' in str(e):
189 return None, STATUS_ACCESS_DENIED
190 else:
191 LOG.info("Unexpected rpc code received from %s: %s" % (self.stringbinding, str(e)))
192 return None, STATUS_ACCESS_DENIED
194 def killConnection(self):
195 if self.session is not None:
196 self.session.get_rpc_transport().disconnect()
197 self.session = None
199 def keepAlive(self):
200 try:
201 req = DummyOp()
202 self.session.request(req)
203 except DCERPCException as e:
204 if 'nca_s_op_rng_error' not in str(e) or 'RPC_E_INVALID_HEADER' not in str(e):
205 raise