Coverage for /root/GitHubProjects/impacket/impacket/spnego.py : 53%

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# Description:
10# SPNEGO functions used by SMB, SMB2/3 and DCERPC
11#
12# Author:
13# Alberto Solino (@agsolino)
14#
16from __future__ import division
17from __future__ import print_function
18from struct import pack, unpack, calcsize
20############### GSS Stuff ################
21GSS_API_SPNEGO_UUID = b'\x2b\x06\x01\x05\x05\x02'
22ASN1_SEQUENCE = 0x30
23ASN1_AID = 0x60
24ASN1_OID = 0x06
25ASN1_OCTET_STRING = 0x04
26ASN1_MECH_TYPE = 0xa0
27ASN1_MECH_TOKEN = 0xa2
28ASN1_SUPPORTED_MECH = 0xa1
29ASN1_RESPONSE_TOKEN = 0xa2
30ASN1_ENUMERATED = 0x0a
31MechTypes = {
32b'+\x06\x01\x04\x01\x827\x02\x02\n': 'NTLMSSP - Microsoft NTLM Security Support Provider',
33b'*\x86H\x82\xf7\x12\x01\x02\x02': 'MS KRB5 - Microsoft Kerberos 5',
34b'*\x86H\x86\xf7\x12\x01\x02\x02': 'KRB5 - Kerberos 5',
35b'*\x86H\x86\xf7\x12\x01\x02\x02\x03': 'KRB5 - Kerberos 5 - User to User',
36b'\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x1e': 'NEGOEX - SPNEGO Extended Negotiation Security Mechanism'
37}
39TypesMech = dict((v,k) for k, v in MechTypes.items())
41def asn1encode(data = ''):
42 #res = asn1.SEQUENCE(str).encode()
43 #import binascii
44 #print '\nalex asn1encode str: %s\n' % binascii.hexlify(str)
45 if 0 <= len(data) <= 0x7F:
46 res = pack('B', len(data)) + data
47 elif 0x80 <= len(data) <= 0xFF: 47 ↛ 48line 47 didn't jump to line 48, because the condition on line 47 was never true
48 res = pack('BB', 0x81, len(data)) + data
49 elif 0x100 <= len(data) <= 0xFFFF: 49 ↛ 51line 49 didn't jump to line 51, because the condition on line 49 was never false
50 res = pack('!BH', 0x82, len(data)) + data
51 elif 0x10000 <= len(data) <= 0xffffff:
52 res = pack('!BBH', 0x83, len(data) >> 16, len(data) & 0xFFFF) + data
53 elif 0x1000000 <= len(data) <= 0xffffffff:
54 res = pack('!BL', 0x84, len(data)) + data
55 else:
56 raise Exception('Error in asn1encode')
57 return res
59def asn1decode(data = ''):
60 len1 = unpack('B', data[:1])[0]
61 data = data[1:]
62 if len1 == 0x81:
63 pad = calcsize('B')
64 len2 = unpack('B',data[:pad])[0]
65 data = data[pad:]
66 ans = data[:len2]
67 elif len1 == 0x82: 67 ↛ 68line 67 didn't jump to line 68, because the condition on line 67 was never true
68 pad = calcsize('H')
69 len2 = unpack('!H', data[:pad])[0]
70 data = data[pad:]
71 ans = data[:len2]
72 elif len1 == 0x83: 72 ↛ 73line 72 didn't jump to line 73, because the condition on line 72 was never true
73 pad = calcsize('B') + calcsize('!H')
74 len2, len3 = unpack('!BH', data[:pad])
75 data = data[pad:]
76 ans = data[:len2 << 16 + len3]
77 elif len1 == 0x84: 77 ↛ 78line 77 didn't jump to line 78, because the condition on line 77 was never true
78 pad = calcsize('!L')
79 len2 = unpack('!L', data[:pad])[0]
80 data = data[pad:]
81 ans = data[:len2]
82 # 1 byte length, string <= 0x7F
83 else:
84 pad = 0
85 ans = data[:len1]
86 return ans, len(ans)+pad+1
88class GSSAPI:
89# Generic GSSAPI Header Format
90 def __init__(self, data = None):
91 self.fields = {}
92 self['UUID'] = GSS_API_SPNEGO_UUID
93 if data: 93 ↛ 94line 93 didn't jump to line 94, because the condition on line 93 was never true
94 self.fromString(data)
95 pass
97 def __setitem__(self,key,value):
98 self.fields[key] = value
100 def __getitem__(self, key):
101 return self.fields[key]
103 def __delitem__(self, key):
104 del self.fields[key]
106 def __len__(self):
107 return len(self.getData())
109 def __str__(self):
110 return len(self.getData())
112 def fromString(self, data = None):
113 # Manual parse of the GSSAPI Header Format
114 # It should be something like
115 # AID = 0x60 TAG, BER Length
116 # OID = 0x06 TAG
117 # GSSAPI OID
118 # UUID data (BER Encoded)
119 # Payload
120 next_byte = unpack('B',data[:1])[0]
121 if next_byte != ASN1_AID:
122 raise Exception('Unknown AID=%x' % next_byte)
123 data = data[1:]
124 decode_data, total_bytes = asn1decode(data)
125 # Now we should have a OID tag
126 next_byte = unpack('B',decode_data[:1])[0]
127 if next_byte != ASN1_OID:
128 raise Exception('OID tag not found %x' % next_byte)
129 decode_data = decode_data[1:]
130 # Now the OID contents, should be SPNEGO UUID
131 uuid, total_bytes = asn1decode(decode_data)
132 self['OID'] = uuid
133 # the rest should be the data
134 self['Payload'] = decode_data[total_bytes:]
135 #pass
137 def dump(self):
138 for i in list(self.fields.keys()):
139 print("%s: {%r}" % (i,self[i]))
141 def getData(self):
142 ans = pack('B',ASN1_AID)
143 ans += asn1encode(
144 pack('B',ASN1_OID) +
145 asn1encode(self['UUID']) +
146 self['Payload'] )
147 return ans
149class SPNEGO_NegTokenResp:
150 # https://tools.ietf.org/html/rfc4178#page-9
151 # NegTokenResp ::= SEQUENCE {
152 # negState [0] ENUMERATED {
153 # accept-completed (0),
154 # accept-incomplete (1),
155 # reject (2),
156 # request-mic (3)
157 # } OPTIONAL,
158 # -- REQUIRED in the first reply from the target
159 # supportedMech [1] MechType OPTIONAL,
160 # -- present only in the first reply from the target
161 # responseToken [2] OCTET STRING OPTIONAL,
162 # mechListMIC [3] OCTET STRING OPTIONAL,
163 # ...
164 # }
165 # This structure is not prepended by a GSS generic header!
166 SPNEGO_NEG_TOKEN_RESP = 0xa1
167 SPNEGO_NEG_TOKEN_TARG = 0xa0
169 def __init__(self, data = None):
170 self.fields = {}
171 if data:
172 self.fromString(data)
173 pass
175 def __setitem__(self,key,value):
176 self.fields[key] = value
178 def __getitem__(self, key):
179 return self.fields[key]
181 def __delitem__(self, key):
182 del self.fields[key]
184 def __len__(self):
185 return len(self.getData())
187 def __str__(self):
188 return self.getData()
190 def fromString(self, data = 0):
191 payload = data
192 next_byte = unpack('B', payload[:1])[0]
193 if next_byte != SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP: 193 ↛ 194line 193 didn't jump to line 194, because the condition on line 193 was never true
194 raise Exception('NegTokenResp not found %x' % next_byte)
195 payload = payload[1:]
196 decode_data, total_bytes = asn1decode(payload)
197 next_byte = unpack('B', decode_data[:1])[0]
198 if next_byte != ASN1_SEQUENCE: 198 ↛ 199line 198 didn't jump to line 199, because the condition on line 198 was never true
199 raise Exception('SEQUENCE tag not found %x' % next_byte)
200 decode_data = decode_data[1:]
201 decode_data, total_bytes = asn1decode(decode_data)
202 next_byte = unpack('B',decode_data[:1])[0]
204 if next_byte != ASN1_MECH_TYPE: 204 ↛ 206line 204 didn't jump to line 206, because the condition on line 204 was never true
205 # MechType not found, could be an AUTH answer
206 if next_byte != ASN1_RESPONSE_TOKEN:
207 raise Exception('MechType/ResponseToken tag not found %x' % next_byte)
208 else:
209 decode_data2 = decode_data[1:]
210 decode_data2, total_bytes = asn1decode(decode_data2)
211 next_byte = unpack('B', decode_data2[:1])[0]
212 if next_byte != ASN1_ENUMERATED: 212 ↛ 213line 212 didn't jump to line 213, because the condition on line 212 was never true
213 raise Exception('Enumerated tag not found %x' % next_byte)
214 item, total_bytes2 = asn1decode(decode_data2[1:])
215 self['NegState'] = item
216 decode_data = decode_data[1:]
217 decode_data = decode_data[total_bytes:]
219 # Do we have more data?
220 if len(decode_data) == 0: 220 ↛ 221line 220 didn't jump to line 221, because the condition on line 220 was never true
221 return
223 next_byte = unpack('B', decode_data[:1])[0]
224 if next_byte != ASN1_SUPPORTED_MECH: 224 ↛ 225line 224 didn't jump to line 225, because the condition on line 224 was never true
225 if next_byte != ASN1_RESPONSE_TOKEN:
226 raise Exception('Supported Mech/ResponseToken tag not found %x' % next_byte)
227 else:
228 decode_data2 = decode_data[1:]
229 decode_data2, total_bytes = asn1decode(decode_data2)
230 next_byte = unpack('B', decode_data2[:1])[0]
231 if next_byte != ASN1_OID: 231 ↛ 232line 231 didn't jump to line 232, because the condition on line 231 was never true
232 raise Exception('OID tag not found %x' % next_byte)
233 decode_data2 = decode_data2[1:]
234 item, total_bytes2 = asn1decode(decode_data2)
235 self['SupportedMech'] = item
237 decode_data = decode_data[1:]
238 decode_data = decode_data[total_bytes:]
239 next_byte = unpack('B', decode_data[:1])[0]
240 if next_byte != ASN1_RESPONSE_TOKEN: 240 ↛ 241line 240 didn't jump to line 241, because the condition on line 240 was never true
241 raise Exception('Response token tag not found %x' % next_byte)
243 decode_data = decode_data[1:]
244 decode_data, total_bytes = asn1decode(decode_data)
245 next_byte = unpack('B', decode_data[:1])[0]
246 if next_byte != ASN1_OCTET_STRING: 246 ↛ 247line 246 didn't jump to line 247, because the condition on line 246 was never true
247 raise Exception('Octet string token tag not found %x' % next_byte)
248 decode_data = decode_data[1:]
249 decode_data, total_bytes = asn1decode(decode_data)
250 self['ResponseToken'] = decode_data
252 def dump(self):
253 for i in list(self.fields.keys()):
254 print("%s: {%r}" % (i,self[i]))
255 def getData(self):
256 ans = pack('B',SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP)
257 if 'NegState' in self.fields and 'SupportedMech' in self.fields and 'ResponseToken' in self.fields: 257 ↛ 259line 257 didn't jump to line 259, because the condition on line 257 was never true
258 # Server resp
259 ans += asn1encode(
260 pack('B', ASN1_SEQUENCE) +
261 asn1encode(
262 pack('B',SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_TARG) +
263 asn1encode(
264 pack('B',ASN1_ENUMERATED) +
265 asn1encode( self['NegState'] )) +
266 pack('B',ASN1_SUPPORTED_MECH) +
267 asn1encode(
268 pack('B',ASN1_OID) +
269 asn1encode(self['SupportedMech'])) +
270 pack('B',ASN1_RESPONSE_TOKEN ) +
271 asn1encode(
272 pack('B', ASN1_OCTET_STRING) + asn1encode(self['ResponseToken']))))
273 elif 'NegState' in self.fields and 'SupportedMech' in self.fields: 273 ↛ 275line 273 didn't jump to line 275, because the condition on line 273 was never true
274 # Server resp
275 ans += asn1encode(
276 pack('B', ASN1_SEQUENCE) +
277 asn1encode(
278 pack('B',SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_TARG) +
279 asn1encode(
280 pack('B',ASN1_ENUMERATED) +
281 asn1encode( self['NegState'] )) +
282 pack('B',ASN1_SUPPORTED_MECH) +
283 asn1encode(
284 pack('B',ASN1_OID) +
285 asn1encode(self['SupportedMech']))))
286 elif 'NegState' in self.fields: 286 ↛ 288line 286 didn't jump to line 288, because the condition on line 286 was never true
287 # Server resp
288 ans += asn1encode(
289 pack('B', ASN1_SEQUENCE) +
290 asn1encode(
291 pack('B', SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_TARG) +
292 asn1encode(
293 pack('B',ASN1_ENUMERATED) +
294 asn1encode( self['NegState'] ))))
295 else:
296 # Client resp
297 ans += asn1encode(
298 pack('B', ASN1_SEQUENCE) +
299 asn1encode(
300 pack('B', ASN1_RESPONSE_TOKEN) +
301 asn1encode(
302 pack('B', ASN1_OCTET_STRING) + asn1encode(self['ResponseToken']))))
303 return ans
305class SPNEGO_NegTokenInit(GSSAPI):
306 # https://tools.ietf.org/html/rfc4178#page-8
307 # NegTokeInit :: = SEQUENCE {
308 # mechTypes [0] MechTypeList,
309 # reqFlags [1] ContextFlags OPTIONAL,
310 # mechToken [2] OCTET STRING OPTIONAL,
311 # mechListMIC [3] OCTET STRING OPTIONAL,
312 # }
313 SPNEGO_NEG_TOKEN_INIT = 0xa0
314 def fromString(self, data = 0):
315 GSSAPI.fromString(self, data)
316 payload = self['Payload']
317 next_byte = unpack('B', payload[:1])[0]
318 if next_byte != SPNEGO_NegTokenInit.SPNEGO_NEG_TOKEN_INIT:
319 raise Exception('NegTokenInit not found %x' % next_byte)
320 payload = payload[1:]
321 decode_data, total_bytes = asn1decode(payload)
322 # Now we should have a SEQUENCE Tag
323 next_byte = unpack('B', decode_data[:1])[0]
324 if next_byte != ASN1_SEQUENCE:
325 raise Exception('SEQUENCE tag not found %x' % next_byte)
326 decode_data = decode_data[1:]
327 decode_data, total_bytes2 = asn1decode(decode_data)
328 next_byte = unpack('B',decode_data[:1])[0]
329 if next_byte != ASN1_MECH_TYPE:
330 raise Exception('MechType tag not found %x' % next_byte)
331 decode_data = decode_data[1:]
332 remaining_data = decode_data
333 decode_data, total_bytes3 = asn1decode(decode_data)
334 next_byte = unpack('B', decode_data[:1])[0]
335 if next_byte != ASN1_SEQUENCE:
336 raise Exception('SEQUENCE tag not found %x' % next_byte)
337 decode_data = decode_data[1:]
338 decode_data, total_bytes4 = asn1decode(decode_data)
339 # And finally we should have the MechTypes
340 self['MechTypes'] = []
341 while decode_data:
342 next_byte = unpack('B', decode_data[:1])[0]
343 if next_byte != ASN1_OID:
344 # Not a valid OID, there must be something else we won't unpack
345 break
346 decode_data = decode_data[1:]
347 item, total_bytes = asn1decode(decode_data)
348 self['MechTypes'].append(item)
349 decode_data = decode_data[total_bytes:]
351 # Do we have MechTokens as well?
352 decode_data = remaining_data[total_bytes3:]
353 if len(decode_data) > 0:
354 next_byte = unpack('B', decode_data[:1])[0]
355 if next_byte == ASN1_MECH_TOKEN:
356 # We have tokens in here!
357 decode_data = decode_data[1:]
358 decode_data, total_bytes = asn1decode(decode_data)
359 next_byte = unpack('B', decode_data[:1])[0]
360 if next_byte == ASN1_OCTET_STRING:
361 decode_data = decode_data[1:]
362 decode_data, total_bytes = asn1decode(decode_data)
363 self['MechToken'] = decode_data
365 def getData(self):
366 mechTypes = b''
367 for i in self['MechTypes']:
368 mechTypes += pack('B', ASN1_OID)
369 mechTypes += asn1encode(i)
371 mechToken = b''
372 # Do we have tokens to send?
373 if 'MechToken' in self.fields: 373 ↛ 378line 373 didn't jump to line 378, because the condition on line 373 was never false
374 mechToken = pack('B', ASN1_MECH_TOKEN) + asn1encode(
375 pack('B', ASN1_OCTET_STRING) + asn1encode(
376 self['MechToken']))
378 ans = pack('B',SPNEGO_NegTokenInit.SPNEGO_NEG_TOKEN_INIT)
379 ans += asn1encode(
380 pack('B', ASN1_SEQUENCE) +
381 asn1encode(
382 pack('B', ASN1_MECH_TYPE) +
383 asn1encode(
384 pack('B', ASN1_SEQUENCE) +
385 asn1encode(mechTypes)) + mechToken ))
388 self['Payload'] = ans
389 return GSSAPI.getData(self)