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

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

## 132 statements

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#

11

12 from struct import unpack

13

14 from impacket import LOG

15 from impacket.examples.ntlmrelayx.clients import ProtocolClient

16 from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED

17 from impacket.ntlm import NTLMAuthChallenge

18 from impacket.spnego import SPNEGO_NegTokenResp

19

20 from impacket.dcerpc.v5 import transport, rpcrt, epm, tsch

21 from impacket.dcerpc.v5.ndr import NDRCALL

22 from 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

25

26 PROTOCOL_CLIENT_CLASS = "RPCRelayClient"

27

28 class RPCRelayClientException(Exception):

29 pass

30

31 class MYDCERPC_v5(DCERPC_v5):

32 def __init__(self, transport):

33 DCERPC_v5.__init__(self, transport)

34

35 def sendBindType1(self, iface_uuid, auth_data):

36 bind = MSRPCBind()

37

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)

44

45 packet = MSRPCHeader()

46 packet['type'] = rpcrt.MSRPC_BIND

47 packet['pduData'] = bind.getData()

48 packet['call_id'] = 0

49

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

54

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

59

60 packet['sec_trailer'] = sec_trailer

61 packet['auth_data'] = auth_data

62

63 self._transport.send(packet.get_packet())

64

65 s = self._transport.recv()

66

67 if s != 0:

68 resp = MSRPCHeader(s)

69 else:

70 return 0 #mmm why not None?

71

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'])

89

90 self.set_max_tfrag(bindResp['max_rfrag'])

91

92 return bindResp

93

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

99

100 auth3 = MSRPCHeader()

101 auth3['type'] = rpcrt.MSRPC_AUTH3

102

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

111

112 self._transport.send(auth3.get_packet(), forceWriteAndx = 1)

113

114 class DummyOp(NDRCALL):

115 opnum = 255

116 structure = (

117 )

118

119 class RPCRelayClient(ProtocolClient):

120 PLUGIN_NAME = "RPC"

121

122 def __init__(self, serverConfig, target, targetPort=None, extendedSecurity=True):

123 ProtocolClient.__init__(self, serverConfig, target, targetPort, extendedSecurity)

124

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

128

129 if self.endpoint == "TSCH":

130 self.endpoint_uuid = tsch.MSRPC_UUID_TSCHS

131 else:

132 raise NotImplementedError("Not implemented!")

133

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

142

143 LOG.debug("%s stringbinding is %s" % (self.endpoint, self.stringbinding))

144

145 def initConnection(self):

146 rpctransport = transport.DCERPCTransportFactory(self.stringbinding)

147

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)

153

154 self.session = MYDCERPC_v5(rpctransport)

155 self.session.set_auth_level(RPC_C_AUTHN_LEVEL_CONNECT)

156 self.session.connect()

157

158 if self.serverConfig.rpc_use_smb:

159 LOG.info("Authentication to smb://%s:%d succeeded" % (self.target.netloc, self.serverConfig.rpc_smb_port))

160

161 return True

162

163 def sendNegotiate(self, auth_data):

164 bindResp = self.session.sendBindType1(self.endpoint_uuid, auth_data)

165

166 challenge = NTLMAuthChallenge()

167 challenge.fromString(bindResp['auth_data'])

168

169 return challenge

170

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

177

178 self.session.sendBindType3(auth_data)

179

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

191

192 def killConnection(self):

193 if self.session is not None:

194 self.session.get_rpc_transport().disconnect()

195 self.session = None

196

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