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

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