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