Hide keyboard shortcuts

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# 

13 

14from struct import unpack 

15 

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 

21 

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 

27 

28PROTOCOL_CLIENT_CLASS = "RPCRelayClient" 

29 

30class RPCRelayClientException(Exception): 

31 pass 

32 

33class MYDCERPC_v5(DCERPC_v5): 

34 def __init__(self, transport): 

35 DCERPC_v5.__init__(self, transport) 

36 

37 def sendBindType1(self, iface_uuid, auth_data): 

38 bind = MSRPCBind() 

39 

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) 

46 

47 packet = MSRPCHeader() 

48 packet['type'] = rpcrt.MSRPC_BIND 

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

50 packet['call_id'] = 0 

51 

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 

56 

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 

61 

62 packet['sec_trailer'] = sec_trailer 

63 packet['auth_data'] = auth_data 

64 

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

66 

67 s = self._transport.recv() 

68 

69 if s != 0: 

70 resp = MSRPCHeader(s) 

71 else: 

72 return 0 #mmm why not None? 

73 

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

91 

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

93 

94 return bindResp 

95 

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 

101 

102 auth3 = MSRPCHeader() 

103 auth3['type'] = rpcrt.MSRPC_AUTH3 

104 

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 

113 

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

115 

116class DummyOp(NDRCALL): 

117 opnum = 255 

118 structure = ( 

119 ) 

120 

121class RPCRelayClient(ProtocolClient): 

122 PLUGIN_NAME = "RPC" 

123 

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

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

126 

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 

130 

131 if self.endpoint == "TSCH": 

132 self.endpoint_uuid = tsch.MSRPC_UUID_TSCHS 

133 else: 

134 raise NotImplementedError("Not implemented!") 

135 

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

144 

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

146 

147 def initConnection(self): 

148 rpctransport = transport.DCERPCTransportFactory(self.stringbinding) 

149 

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) 

155 

156 self.session = MYDCERPC_v5(rpctransport) 

157 self.session.set_auth_level(RPC_C_AUTHN_LEVEL_CONNECT) 

158 self.session.connect() 

159 

160 if self.serverConfig.rpc_use_smb: 

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

162 

163 return True 

164 

165 def sendNegotiate(self, auth_data): 

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

167 

168 challenge = NTLMAuthChallenge() 

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

170 

171 return challenge 

172 

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 

179 

180 self.session.sendBindType3(auth_data) 

181 

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 

193 

194 def killConnection(self): 

195 if self.session is not None: 

196 self.session.get_rpc_transport().disconnect() 

197 self.session = None 

198 

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