• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2003-2016 CORE Security Technologies:
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#
7import base64
8import struct
9import calendar
10import time
11import hashlib
12import random
13import string
14import binascii
15
16from impacket.structure import Structure
17from impacket import LOG
18
19
20# This is important. NTLMv2 is not negotiated by the client or server.
21# It is used if set locally on both sides. Change this item if you don't want to use
22# NTLMv2 by default and fall back to NTLMv1 (with EXTENDED_SESSION_SECURITY or not)
23# Check the following links:
24# http://davenport.sourceforge.net/ntlm.html
25# http://blogs.msdn.com/b/openspecification/archive/2010/04/20/ntlm-keys-and-sundry-stuff.aspx
26# http://social.msdn.microsoft.com/Forums/en-US/os_interopscenarios/thread/c8f488ed-1b96-4e06-bd65-390aa41138d1/
27# So I'm setting a global variable to control this, this can also be set programmatically
28
29USE_NTLMv2 = True # if false will fall back to NTLMv1 (or NTLMv1 with ESS a.k.a NTLM2)
30
31
32def computeResponse(flags, serverChallenge, clientChallenge, serverName, domain, user, password, lmhash='', nthash='',
33                    use_ntlmv2=USE_NTLMv2):
34    if use_ntlmv2:
35        return computeResponseNTLMv2(flags, serverChallenge, clientChallenge, serverName, domain, user, password,
36                                     lmhash, nthash, use_ntlmv2=use_ntlmv2)
37    else:
38        return computeResponseNTLMv1(flags, serverChallenge, clientChallenge, serverName, domain, user, password,
39                                     lmhash, nthash, use_ntlmv2=use_ntlmv2)
40try:
41    POW = None
42    from Crypto.Cipher import ARC4
43    from Crypto.Cipher import DES
44    from Crypto.Hash import MD4
45except Exception:
46    try:
47        import POW
48    except Exception:
49        LOG.critical("Warning: You don't have any crypto installed. You need PyCrypto")
50        LOG.critical("See http://www.pycrypto.org/")
51
52NTLM_AUTH_NONE          = 1
53NTLM_AUTH_CONNECT       = 2
54NTLM_AUTH_CALL          = 3
55NTLM_AUTH_PKT           = 4
56NTLM_AUTH_PKT_INTEGRITY = 5
57NTLM_AUTH_PKT_PRIVACY   = 6
58
59# If set, requests 56-bit encryption. If the client sends NTLMSSP_NEGOTIATE_SEAL or NTLMSSP_NEGOTIATE_SIGN
60# with NTLMSSP_NEGOTIATE_56 to the server in the NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_56 to
61# the client in the CHALLENGE_MESSAGE. Otherwise it is ignored. If both NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128
62# are requested and supported by the client and server, NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 will both be
63# returned to the client. Clients and servers that set NTLMSSP_NEGOTIATE_SEAL SHOULD set NTLMSSP_NEGOTIATE_56 if it is
64# supported. An alternate name for this field is NTLMSSP_NEGOTIATE_56.
65NTLMSSP_NEGOTIATE_56                       = 0x80000000
66
67# If set, requests an explicit key exchange. This capability SHOULD be used because it improves security for message
68# integrity or confidentiality. See sections 3.2.5.1.2, 3.2.5.2.1, and 3.2.5.2.2 for details. An alternate name for
69# this field is NTLMSSP_NEGOTIATE_KEY_EXCH.
70NTLMSSP_NEGOTIATE_KEY_EXCH                 = 0x40000000
71
72# If set, requests 128-bit session key negotiation. An alternate name for this field is NTLMSSP_NEGOTIATE_128.
73# If the client sends NTLMSSP_NEGOTIATE_128 to the server in the NEGOTIATE_MESSAGE, the server MUST return
74# NTLMSSP_NEGOTIATE_128 to the client in the CHALLENGE_MESSAGE only if the client sets NTLMSSP_NEGOTIATE_SEAL or
75# NTLMSSP_NEGOTIATE_SIGN. Otherwise it is ignored. If both NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 are
76# requested and supported by the client and server, NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 will both be
77# returned to the client. Clients and servers that set NTLMSSP_NEGOTIATE_SEAL SHOULD set NTLMSSP_NEGOTIATE_128 if it
78# is supported. An alternate name for this field is NTLMSSP_NEGOTIATE_128
79NTLMSSP_NEGOTIATE_128                      = 0x20000000
80
81NTLMSSP_RESERVED_1                         = 0x10000000
82NTLMSSP_RESERVED_2                         = 0x08000000
83NTLMSSP_RESERVED_3                         = 0x04000000
84
85# If set, requests the protocol version number. The data corresponding to this flag is provided in the Version field
86# of the NEGOTIATE_MESSAGE, the CHALLENGE_MESSAGE, and the AUTHENTICATE_MESSAGE.<22> An alternate name for this field
87# is NTLMSSP_NEGOTIATE_VERSION
88NTLMSSP_NEGOTIATE_VERSION                  = 0x02000000
89NTLMSSP_RESERVED_4                         = 0x01000000
90
91# If set, indicates that the TargetInfo fields in the CHALLENGE_MESSAGE (section 2.2.1.2) are populated.
92# An alternate name for this field is NTLMSSP_NEGOTIATE_TARGET_INFO.
93NTLMSSP_NEGOTIATE_TARGET_INFO              = 0x00800000
94
95# If set, requests the usage of the LMOWF (section 3.3). An alternate name for this field is
96# NTLMSSP_REQUEST_NON_NT_SESSION_KEY.
97NTLMSSP_REQUEST_NON_NT_SESSION_KEY         = 0x00400000
98NTLMSSP_RESERVED_5                         = 0x00200000
99
100# If set, requests an identify level token. An alternate name for this field is NTLMSSP_NEGOTIATE_IDENTIFY
101NTLMSSP_NEGOTIATE_IDENTIFY                 = 0x00100000
102
103# If set, requests usage of the NTLM v2 session security. NTLM v2 session security is a misnomer because it is not
104# NTLM v2. It is NTLM v1 using the extended session security that is also in NTLM v2. NTLMSSP_NEGOTIATE_LM_KEY and
105# NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY are mutually exclusive. If both NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
106# and NTLMSSP_NEGOTIATE_LM_KEY are requested, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY alone MUST be returned to the
107# client. NTLM v2 authentication session key generation MUST be supported by both the client and the DC in order to be
108# used, and extended session security signing and sealing requires support from the client and the server in order to
109# be used.<23> An alternate name for this field is NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
110NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000
111NTLMSSP_NEGOTIATE_NTLM2                    = 0x00080000
112NTLMSSP_TARGET_TYPE_SHARE                  = 0x00040000
113
114# If set, TargetName MUST be a server name. The data corresponding to this flag is provided by the server in the
115# TargetName field of the CHALLENGE_MESSAGE. If this bit is set, then NTLMSSP_TARGET_TYPE_DOMAIN MUST NOT be set.
116# This flag MUST be ignored in the NEGOTIATE_MESSAGE and the AUTHENTICATE_MESSAGE. An alternate name for this field
117# is NTLMSSP_TARGET_TYPE_SERVER
118NTLMSSP_TARGET_TYPE_SERVER                 = 0x00020000
119
120# If set, TargetName MUST be a domain name. The data corresponding to this flag is provided by the server in the
121# TargetName field of the CHALLENGE_MESSAGE. If set, then NTLMSSP_TARGET_TYPE_SERVER MUST NOT be set. This flag MUST
122# be ignored in the NEGOTIATE_MESSAGE and the AUTHENTICATE_MESSAGE. An alternate name for this field is
123# NTLMSSP_TARGET_TYPE_DOMAIN.
124NTLMSSP_TARGET_TYPE_DOMAIN                 = 0x00010000
125
126# If set, requests the presence of a signature block on all messages. NTLMSSP_NEGOTIATE_ALWAYS_SIGN MUST be set in the
127# NEGOTIATE_MESSAGE to the server and the CHALLENGE_MESSAGE to the client. NTLMSSP_NEGOTIATE_ALWAYS_SIGN is overridden
128# by NTLMSSP_NEGOTIATE_SIGN and NTLMSSP_NEGOTIATE_SEAL, if they are supported. An alternate name for this field is
129# NTLMSSP_NEGOTIATE_ALWAYS_SIGN.
130NTLMSSP_NEGOTIATE_ALWAYS_SIGN              = 0x00008000       # forces the other end to sign packets
131NTLMSSP_RESERVED_6                         = 0x00004000
132
133# This flag indicates whether the Workstation field is present. If this flag is not set, the Workstation field MUST be
134# ignored. If this flag is set, the length field of the Workstation field specifies whether the workstation name is
135# nonempty or not.<24> An alternate name for this field is NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED.
136NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x00002000
137
138# If set, the domain name is provided (section 2.2.1.1).<25> An alternate name for this field is
139# NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED
140NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED      = 0x00001000
141NTLMSSP_RESERVED_7                         = 0x00000800
142
143
144# If set, LM authentication is not allowed and only NT authentication is used.
145NTLMSSP_NEGOTIATE_NT_ONLY                  = 0x00000400
146
147# If set, requests usage of the NTLM v1 session security protocol. NTLMSSP_NEGOTIATE_NTLM MUST be set in the
148# NEGOTIATE_MESSAGE to the server and the CHALLENGE_MESSAGE to the client. An alternate name for this field is
149# NTLMSSP_NEGOTIATE_NTLM
150NTLMSSP_NEGOTIATE_NTLM                     = 0x00000200
151NTLMSSP_RESERVED_8                         = 0x00000100
152
153# If set, requests LAN Manager (LM) session key computation. NTLMSSP_NEGOTIATE_LM_KEY and
154# NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY are mutually exclusive. If both NTLMSSP_NEGOTIATE_LM_KEY and
155# NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY are requested, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY alone MUST be
156# returned to the client. NTLM v2 authentication session key generation MUST be supported by both the client and the
157# DC in order to be used, and extended session security signing and sealing requires support from the client and the
158# server to be used. An alternate name for this field is NTLMSSP_NEGOTIATE_LM_KEY.
159NTLMSSP_NEGOTIATE_LM_KEY                   = 0x00000080
160
161# If set, requests connectionless authentication. If NTLMSSP_NEGOTIATE_DATAGRAM is set, then NTLMSSP_NEGOTIATE_KEY_EXCH
162# MUST always be set in the AUTHENTICATE_MESSAGE to the server and the CHALLENGE_MESSAGE to the client. An alternate
163# name for this field is NTLMSSP_NEGOTIATE_DATAGRAM.
164NTLMSSP_NEGOTIATE_DATAGRAM                 = 0x00000040
165
166# If set, requests session key negotiation for message confidentiality. If the client sends NTLMSSP_NEGOTIATE_SEAL to
167# the server in the NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_SEAL to the client in the
168# CHALLENGE_MESSAGE. Clients and servers that set NTLMSSP_NEGOTIATE_SEAL SHOULD always set NTLMSSP_NEGOTIATE_56 and
169# NTLMSSP_NEGOTIATE_128, if they are supported. An alternate name for this field is NTLMSSP_NEGOTIATE_SEAL.
170NTLMSSP_NEGOTIATE_SEAL                     = 0x00000020
171
172# If set, requests session key negotiation for message signatures. If the client sends NTLMSSP_NEGOTIATE_SIGN to the
173# server in the NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_SIGN to the client in the CHALLENGE_MESSAGE.
174# An alternate name for this field is NTLMSSP_NEGOTIATE_SIGN.
175NTLMSSP_NEGOTIATE_SIGN                     = 0x00000010       # means packet is signed, if verifier is wrong it fails
176NTLMSSP_RESERVED_9                         = 0x00000008
177
178# If set, a TargetName field of the CHALLENGE_MESSAGE (section 2.2.1.2) MUST be supplied. An alternate name for this
179# field is NTLMSSP_REQUEST_TARGET.
180NTLMSSP_REQUEST_TARGET                     = 0x00000004
181
182# If set, requests OEM character set encoding. An alternate name for this field is NTLM_NEGOTIATE_OEM. See bit A for
183# details.
184NTLM_NEGOTIATE_OEM                         = 0x00000002
185
186# If set, requests Unicode character set encoding. An alternate name for this field is NTLMSSP_NEGOTIATE_UNICODE.
187NTLMSSP_NEGOTIATE_UNICODE                  = 0x00000001
188
189# AV_PAIR constants
190NTLMSSP_AV_EOL              = 0x00
191NTLMSSP_AV_HOSTNAME         = 0x01
192NTLMSSP_AV_DOMAINNAME       = 0x02
193NTLMSSP_AV_DNS_HOSTNAME     = 0x03
194NTLMSSP_AV_DNS_DOMAINNAME   = 0x04
195NTLMSSP_AV_DNS_TREENAME     = 0x05
196NTLMSSP_AV_FLAGS            = 0x06
197NTLMSSP_AV_TIME             = 0x07
198NTLMSSP_AV_RESTRICTIONS     = 0x08
199NTLMSSP_AV_TARGET_NAME      = 0x09
200NTLMSSP_AV_CHANNEL_BINDINGS = 0x0a
201
202class AV_PAIRS():
203    def __init__(self, data = None):
204        self.fields = {}
205        if data is not None:
206            self.fromString(data)
207
208    def __setitem__(self,key,value):
209        self.fields[key] = (len(value),value)
210
211    def __getitem__(self, key):
212        if self.fields.has_key(key):
213           return self.fields[key]
214        return None
215
216    def __delitem__(self, key):
217        del self.fields[key]
218
219    def __len__(self):
220        return len(self.getData())
221
222    def __str__(self):
223        return len(self.getData())
224
225    def fromString(self, data):
226        tInfo = data
227        fType = 0xff
228        while fType is not NTLMSSP_AV_EOL:
229            fType = struct.unpack('<H',tInfo[:struct.calcsize('<H')])[0]
230            tInfo = tInfo[struct.calcsize('<H'):]
231            length = struct.unpack('<H',tInfo[:struct.calcsize('<H')])[0]
232            tInfo = tInfo[struct.calcsize('<H'):]
233            content = tInfo[:length]
234            self.fields[fType]=(length,content)
235            tInfo = tInfo[length:]
236
237    def dump(self):
238        for i in self.fields.keys():
239            print "%s: {%r}" % (i,self[i])
240
241    def getData(self):
242        if self.fields.has_key(NTLMSSP_AV_EOL):
243            del self.fields[NTLMSSP_AV_EOL]
244        ans = ''
245        for i in self.fields.keys():
246            ans+= struct.pack('<HH', i, self[i][0])
247            ans+= self[i][1]
248
249        # end with a NTLMSSP_AV_EOL
250        ans += struct.pack('<HH', NTLMSSP_AV_EOL, 0)
251
252        return ans
253
254class NTLMAuthMixin:
255    def get_os_version(self):
256        if self['os_version'] == '':
257            return None
258        else:
259            mayor_v = struct.unpack('B',self['os_version'][0])[0]
260            minor_v = struct.unpack('B',self['os_version'][1])[0]
261            build_v = struct.unpack('H',self['os_version'][2:4])
262            return (mayor_v,minor_v,build_v)
263
264class NTLMAuthNegotiate(Structure, NTLMAuthMixin):
265
266    structure = (
267        ('','"NTLMSSP\x00'),
268        ('message_type','<L=1'),
269        ('flags','<L'),
270        ('domain_len','<H-domain_name'),
271        ('domain_max_len','<H-domain_name'),
272        ('domain_offset','<L=0'),
273        ('host_len','<H-host_name'),
274        ('host_maxlen','<H-host_name'),
275        ('host_offset','<L=0'),
276        ('os_version',':'),
277        ('host_name',':'),
278        ('domain_name',':'))
279
280    def __init__(self):
281        Structure.__init__(self)
282        self['flags']= (
283               NTLMSSP_NEGOTIATE_128     |
284               NTLMSSP_NEGOTIATE_KEY_EXCH|
285               # NTLMSSP_LM_KEY      |
286               NTLMSSP_NEGOTIATE_NTLM    |
287               NTLMSSP_NEGOTIATE_UNICODE     |
288               # NTLMSSP_ALWAYS_SIGN |
289               NTLMSSP_NEGOTIATE_SIGN        |
290               NTLMSSP_NEGOTIATE_SEAL        |
291               # NTLMSSP_TARGET      |
292               0)
293        self['host_name']=''
294        self['domain_name']=''
295        self['os_version']=''
296
297    def getData(self):
298        if len(self.fields['host_name']) > 0:
299            self['flags'] |= NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED
300        if len(self.fields['domain_name']) > 0:
301            self['flags'] |= NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED
302        if len(self.fields['os_version']) > 0:
303            self['flags'] |= NTLMSSP_NEGOTIATE_VERSION
304        if (self['flags'] & NTLMSSP_NEGOTIATE_VERSION) == NTLMSSP_NEGOTIATE_VERSION:
305            version_len = 8
306        else:
307            version_len = 0
308        if (self['flags'] & NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED) == NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED:
309            self['host_offset']=32 + version_len
310        if (self['flags'] & NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED) == NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED:
311            self['domain_offset']=32+len(self['host_name']) + version_len
312        return Structure.getData(self)
313
314    def fromString(self,data):
315        Structure.fromString(self,data)
316
317        domain_offset = self['domain_offset']
318        domain_end    = self['domain_len'] + domain_offset
319        self['domain_name'] = data[ domain_offset : domain_end ]
320
321        host_offset = self['host_offset']
322        host_end    = self['host_len'] + host_offset
323        self['host_name'] = data[ host_offset : host_end ]
324
325        hasOsInfo = self['flags'] & NTLMSSP_NEGOTIATE_VERSION
326        if len(data) >= 36 and hasOsInfo:
327            self['os_version'] = data[32:40]
328        else:
329            self['os_version'] = ''
330
331class NTLMAuthChallenge(Structure):
332
333    structure = (
334        ('','"NTLMSSP\x00'),
335        ('message_type','<L=2'),
336        ('domain_len','<H-domain_name'),
337        ('domain_max_len','<H-domain_name'),
338        ('domain_offset','<L=40'),
339        ('flags','<L=0'),
340        ('challenge','8s'),
341        ('reserved','8s=""'),
342        ('TargetInfoFields_len','<H-TargetInfoFields'),
343        ('TargetInfoFields_max_len','<H-TargetInfoFields'),
344        ('TargetInfoFields_offset','<L'),
345        ('VersionLen','_-Version','self.checkVersion(self["flags"])'),
346        ('Version',':'),
347        ('domain_name',':'),
348        ('TargetInfoFields',':'))
349
350    def checkVersion(self, flags):
351        if flags is not None:
352           if flags & NTLMSSP_NEGOTIATE_VERSION == 0:
353              return 0
354        return 8
355
356    def getData(self):
357        if self['TargetInfoFields'] is not None and type(self['TargetInfoFields']) is not str:
358            raw_av_fields = self['TargetInfoFields'].getData()
359            self['TargetInfoFields'] = raw_av_fields
360        return Structure.getData(self)
361
362    def fromString(self,data):
363        Structure.fromString(self,data)
364        # Just in case there's more data after the TargetInfoFields
365        self['TargetInfoFields'] = self['TargetInfoFields'][:self['TargetInfoFields_len']]
366        # We gotta process the TargetInfoFields
367        #if self['TargetInfoFields_len'] > 0:
368        #    av_pairs = AV_PAIRS(self['TargetInfoFields'][:self['TargetInfoFields_len']])
369        #    self['TargetInfoFields'] = av_pairs
370
371        return self
372
373class NTLMAuthChallengeResponse(Structure, NTLMAuthMixin):
374
375    structure = (
376        ('','"NTLMSSP\x00'),
377        ('message_type','<L=3'),
378        ('lanman_len','<H-lanman'),
379        ('lanman_max_len','<H-lanman'),
380        ('lanman_offset','<L'),
381        ('ntlm_len','<H-ntlm'),
382        ('ntlm_max_len','<H-ntlm'),
383        ('ntlm_offset','<L'),
384        ('domain_len','<H-domain_name'),
385        ('domain_max_len','<H-domain_name'),
386        ('domain_offset','<L'),
387        ('user_len','<H-user_name'),
388        ('user_max_len','<H-user_name'),
389        ('user_offset','<L'),
390        ('host_len','<H-host_name'),
391        ('host_max_len','<H-host_name'),
392        ('host_offset','<L'),
393        ('session_key_len','<H-session_key'),
394        ('session_key_max_len','<H-session_key'),
395        ('session_key_offset','<L'),
396        ('flags','<L'),
397        ('VersionLen','_-Version','self.checkVersion(self["flags"])'),
398        ('Version',':=""'),
399        ('MICLen','_-MIC','self.checkMIC(self["flags"])'),
400        ('MIC',':=""'),
401        ('domain_name',':'),
402        ('user_name',':'),
403        ('host_name',':'),
404        ('lanman',':'),
405        ('ntlm',':'),
406        ('session_key',':'))
407
408    def __init__(self, username = '', password = '', challenge = '', lmhash = '', nthash = '', flags = 0):
409        Structure.__init__(self)
410        self['session_key']=''
411        self['user_name']=username.encode('utf-16le')
412        self['domain_name']='' #"CLON".encode('utf-16le')
413        self['host_name']='' #"BETS".encode('utf-16le')
414        self['flags'] = (   #authResp['flags']
415                # we think (beto & gera) that his flags force a memory conten leakage when a windows 2000 answers using uninitializaed verifiers
416           NTLMSSP_NEGOTIATE_128     |
417           NTLMSSP_NEGOTIATE_KEY_EXCH|
418           # NTLMSSP_LM_KEY      |
419           NTLMSSP_NEGOTIATE_NTLM    |
420           NTLMSSP_NEGOTIATE_UNICODE     |
421           # NTLMSSP_ALWAYS_SIGN |
422           NTLMSSP_NEGOTIATE_SIGN        |
423           NTLMSSP_NEGOTIATE_SEAL        |
424           # NTLMSSP_TARGET      |
425           0)
426        # Here we do the stuff
427        if username and ( lmhash != '' or nthash != ''):
428            self['lanman'] = get_ntlmv1_response(lmhash, challenge)
429            self['ntlm'] = get_ntlmv1_response(nthash, challenge)
430        elif (username and password):
431            lmhash = compute_lmhash(password)
432            nthash = compute_nthash(password)
433            self['lanman']=get_ntlmv1_response(lmhash, challenge)
434            self['ntlm']=get_ntlmv1_response(nthash, challenge)    # This is not used for LM_KEY nor NTLM_KEY
435        else:
436            self['lanman'] = ''
437            self['ntlm'] = ''
438            if not self['host_name']:
439                self['host_name'] = 'NULL'.encode('utf-16le')      # for NULL session there must be a hostname
440
441    def checkVersion(self, flags):
442        if flags is not None:
443           if flags & NTLMSSP_NEGOTIATE_VERSION == 0:
444              return 0
445        return 8
446
447    def checkMIC(self, flags):
448        # TODO: Find a proper way to check the MIC is in there
449        if flags is not None:
450           if flags & NTLMSSP_NEGOTIATE_VERSION == 0:
451              return 0
452        return 16
453
454    def getData(self):
455        self['domain_offset']=64+self.checkMIC(self["flags"])+self.checkVersion(self["flags"])
456        self['user_offset']=64+self.checkMIC(self["flags"])+self.checkVersion(self["flags"])+len(self['domain_name'])
457        self['host_offset']=self['user_offset']+len(self['user_name'])
458        self['lanman_offset']=self['host_offset']+len(self['host_name'])
459        self['ntlm_offset']=self['lanman_offset']+len(self['lanman'])
460        self['session_key_offset']=self['ntlm_offset']+len(self['ntlm'])
461        return Structure.getData(self)
462
463    def fromString(self,data):
464        Structure.fromString(self,data)
465        # [MS-NLMP] page 27
466        # Payload data can be present in any order within the Payload field,
467        # with variable-length padding before or after the data
468
469        domain_offset = self['domain_offset']
470        domain_end = self['domain_len'] + domain_offset
471        self['domain_name'] = data[ domain_offset : domain_end ]
472
473        host_offset = self['host_offset']
474        host_end    = self['host_len'] + host_offset
475        self['host_name'] = data[ host_offset: host_end ]
476
477        user_offset = self['user_offset']
478        user_end    = self['user_len'] + user_offset
479        self['user_name'] = data[ user_offset: user_end ]
480
481        ntlm_offset = self['ntlm_offset']
482        ntlm_end    = self['ntlm_len'] + ntlm_offset
483        self['ntlm'] = data[ ntlm_offset : ntlm_end ]
484
485        lanman_offset = self['lanman_offset']
486        lanman_end    = self['lanman_len'] + lanman_offset
487        self['lanman'] = data[ lanman_offset : lanman_end]
488
489        #if len(data) >= 36:
490        #    self['os_version'] = data[32:36]
491        #else:
492        #    self['os_version'] = ''
493
494class ImpacketStructure(Structure):
495    def set_parent(self, other):
496        self.parent = other
497
498    def get_packet(self):
499        return str(self)
500
501    def get_size(self):
502        return len(self)
503
504class ExtendedOrNotMessageSignature(Structure):
505    def __init__(self, flags = 0, **kargs):
506        if flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
507            self.structure = self.extendedMessageSignature
508        else:
509            self.structure = self.MessageSignature
510        return Structure.__init__(self, **kargs)
511
512class NTLMMessageSignature(ExtendedOrNotMessageSignature):
513      extendedMessageSignature = (
514          ('Version','<L=1'),
515          ('Checksum','<q'),
516          ('SeqNum','<i'),
517      )
518
519      MessageSignature = (
520          ('Version','<L=1'),
521          ('RandomPad','<i=0'),
522          ('Checksum','<i'),
523          ('SeqNum','<i'),
524      )
525
526KNOWN_DES_INPUT = "KGS!@#$%"
527
528def __expand_DES_key( key):
529    # Expand the key from a 7-byte password key into a 8-byte DES key
530    key  = key[:7]
531    key += '\x00'*(7-len(key))
532    s = chr(((ord(key[0]) >> 1) & 0x7f) << 1)
533    s = s + chr(((ord(key[0]) & 0x01) << 6 | ((ord(key[1]) >> 2) & 0x3f)) << 1)
534    s = s + chr(((ord(key[1]) & 0x03) << 5 | ((ord(key[2]) >> 3) & 0x1f)) << 1)
535    s = s + chr(((ord(key[2]) & 0x07) << 4 | ((ord(key[3]) >> 4) & 0x0f)) << 1)
536    s = s + chr(((ord(key[3]) & 0x0f) << 3 | ((ord(key[4]) >> 5) & 0x07)) << 1)
537    s = s + chr(((ord(key[4]) & 0x1f) << 2 | ((ord(key[5]) >> 6) & 0x03)) << 1)
538    s = s + chr(((ord(key[5]) & 0x3f) << 1 | ((ord(key[6]) >> 7) & 0x01)) << 1)
539    s = s + chr((ord(key[6]) & 0x7f) << 1)
540    return s
541
542def __DES_block(key, msg):
543    if POW:
544        cipher = POW.Symmetric(POW.DES_ECB)
545        cipher.encryptInit(__expand_DES_key(key))
546        return cipher.update(msg)
547    else:
548        cipher = DES.new(__expand_DES_key(key),DES.MODE_ECB)
549        return cipher.encrypt(msg)
550
551def ntlmssp_DES_encrypt(key, challenge):
552    answer  = __DES_block(key[:7], challenge)
553    answer += __DES_block(key[7:14], challenge)
554    answer += __DES_block(key[14:], challenge)
555    return answer
556
557# High level functions to use NTLMSSP
558
559def getNTLMSSPType1(workstation='', domain='', signingRequired = False, use_ntlmv2 = USE_NTLMv2):
560    # Let's do some encoding checks before moving on. Kind of dirty, but found effective when dealing with
561    # international characters.
562    import sys
563    encoding = sys.getfilesystemencoding()
564    if encoding is not None:
565        try:
566            workstation.encode('utf-16le')
567        except:
568            workstation = workstation.decode(encoding)
569        try:
570            domain.encode('utf-16le')
571        except:
572            domain = domain.decode(encoding)
573
574    # Let's prepare a Type 1 NTLMSSP Message
575    auth = NTLMAuthNegotiate()
576    auth['flags']=0
577    if signingRequired:
578       auth['flags'] = NTLMSSP_NEGOTIATE_KEY_EXCH | NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_SEAL
579    if use_ntlmv2:
580       auth['flags'] |= NTLMSSP_NEGOTIATE_TARGET_INFO
581    auth['flags'] |= NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET |  NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_56
582    auth['domain_name'] = domain.encode('utf-16le')
583    return auth
584
585def getNTLMSSPType3(type1, type2, user, password, domain, lmhash = '', nthash = '', use_ntlmv2 = USE_NTLMv2):
586
587    # Let's do some encoding checks before moving on. Kind of dirty, but found effective when dealing with
588    # international characters.
589    import sys
590    encoding = sys.getfilesystemencoding()
591    if encoding is not None:
592        try:
593            user.encode('utf-16le')
594        except:
595            user = user.decode(encoding)
596        try:
597            password.encode('utf-16le')
598        except:
599            password = password.decode(encoding)
600        try:
601            domain.encode('utf-16le')
602        except:
603            domain = user.decode(encoding)
604
605    ntlmChallenge = NTLMAuthChallenge(type2)
606
607    # Let's start with the original flags sent in the type1 message
608    responseFlags = type1['flags']
609
610    # Token received and parsed. Depending on the authentication
611    # method we will create a valid ChallengeResponse
612    ntlmChallengeResponse = NTLMAuthChallengeResponse(user, password, ntlmChallenge['challenge'])
613
614    clientChallenge = "".join([random.choice(string.digits+string.letters) for i in xrange(8)])
615
616    serverName = ntlmChallenge['TargetInfoFields']
617
618    ntResponse, lmResponse, sessionBaseKey = computeResponse(ntlmChallenge['flags'], ntlmChallenge['challenge'], clientChallenge, serverName, domain, user, password, lmhash, nthash, use_ntlmv2 )
619
620    # Let's check the return flags
621    if (ntlmChallenge['flags'] & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) == 0:
622        # No extended session security, taking it out
623        responseFlags &= 0xffffffff ^ NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
624    if (ntlmChallenge['flags'] & NTLMSSP_NEGOTIATE_128 ) == 0:
625        # No support for 128 key len, taking it out
626        responseFlags &= 0xffffffff ^ NTLMSSP_NEGOTIATE_128
627    if (ntlmChallenge['flags'] & NTLMSSP_NEGOTIATE_KEY_EXCH) == 0:
628        # No key exchange supported, taking it out
629        responseFlags &= 0xffffffff ^ NTLMSSP_NEGOTIATE_KEY_EXCH
630    if (ntlmChallenge['flags'] & NTLMSSP_NEGOTIATE_SEAL) == 0:
631        # No sign available, taking it out
632        responseFlags &= 0xffffffff ^ NTLMSSP_NEGOTIATE_SEAL
633    if (ntlmChallenge['flags'] & NTLMSSP_NEGOTIATE_SIGN) == 0:
634        # No sign available, taking it out
635        responseFlags &= 0xffffffff ^ NTLMSSP_NEGOTIATE_SIGN
636    if (ntlmChallenge['flags'] & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) == 0:
637        # No sign available, taking it out
638        responseFlags &= 0xffffffff ^ NTLMSSP_NEGOTIATE_ALWAYS_SIGN
639
640    keyExchangeKey = KXKEY(ntlmChallenge['flags'],sessionBaseKey, lmResponse, ntlmChallenge['challenge'], password, lmhash, nthash,use_ntlmv2)
641
642    # Special case for anonymous login
643    if user == '' and password == '' and lmhash == '' and nthash == '':
644      keyExchangeKey = '\x00'*16
645
646    # If we set up key exchange, let's fill the right variables
647    if ntlmChallenge['flags'] & NTLMSSP_NEGOTIATE_KEY_EXCH:
648       # not exactly what I call random tho :\
649       # exportedSessionKey = this is the key we should use to sign
650       exportedSessionKey = "".join([random.choice(string.digits+string.letters) for i in xrange(16)])
651       #exportedSessionKey = "A"*16
652       #print "keyExchangeKey %r" % keyExchangeKey
653       # Let's generate the right session key based on the challenge flags
654       #if responseFlags & NTLMSSP_NTLM2_KEY:
655           # Extended session security enabled
656       #    if responseFlags & NTLMSSP_KEY_128:
657               # Full key
658       #        exportedSessionKey = exportedSessionKey
659       #    elif responseFlags & NTLMSSP_KEY_56:
660               # Only 56-bit key
661       #        exportedSessionKey = exportedSessionKey[:7]
662       #    else:
663       #        exportedSessionKey = exportedSessionKey[:5]
664       #elif responseFlags & NTLMSSP_KEY_56:
665           # No extended session security, just 56 bits key
666       #    exportedSessionKey = exportedSessionKey[:7] + '\xa0'
667       #else:
668       #    exportedSessionKey = exportedSessionKey[:5] + '\xe5\x38\xb0'
669
670       encryptedRandomSessionKey = generateEncryptedSessionKey(keyExchangeKey, exportedSessionKey)
671    else:
672       encryptedRandomSessionKey = None
673       # [MS-NLMP] page 46
674       exportedSessionKey        = keyExchangeKey
675
676    ntlmChallengeResponse['flags'] = responseFlags
677    ntlmChallengeResponse['domain_name'] = domain.encode('utf-16le')
678    ntlmChallengeResponse['lanman'] = lmResponse
679    ntlmChallengeResponse['ntlm'] = ntResponse
680    if encryptedRandomSessionKey is not None:
681        ntlmChallengeResponse['session_key'] = encryptedRandomSessionKey
682
683    return ntlmChallengeResponse, exportedSessionKey
684
685
686# NTLMv1 Algorithm
687
688def generateSessionKeyV1(password, lmhash, nthash):
689    if POW:
690        hash = POW.Digest(POW.MD4_DIGEST)
691    else:
692        hash = MD4.new()
693    hash.update(NTOWFv1(password, lmhash, nthash))
694    return hash.digest()
695
696def computeResponseNTLMv1(flags, serverChallenge, clientChallenge, serverName, domain, user, password, lmhash='', nthash='', use_ntlmv2 = USE_NTLMv2):
697    if (user == '' and password == ''):
698        # Special case for anonymous authentication
699        lmResponse = ''
700        ntResponse = ''
701    else:
702        lmhash = LMOWFv1(password, lmhash, nthash)
703        nthash = NTOWFv1(password, lmhash, nthash)
704        if flags & NTLMSSP_NEGOTIATE_LM_KEY:
705           ntResponse = ''
706           lmResponse = get_ntlmv1_response(lmhash, serverChallenge)
707        elif flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
708           md5 = hashlib.new('md5')
709           chall = (serverChallenge + clientChallenge)
710           md5.update(chall)
711           ntResponse = ntlmssp_DES_encrypt(nthash, md5.digest()[:8])
712           lmResponse = clientChallenge + '\x00'*16
713        else:
714           ntResponse = get_ntlmv1_response(nthash,serverChallenge)
715           lmResponse = get_ntlmv1_response(lmhash, serverChallenge)
716
717    sessionBaseKey = generateSessionKeyV1(password, lmhash, nthash)
718    return ntResponse, lmResponse, sessionBaseKey
719
720def compute_lmhash(password):
721    # This is done according to Samba's encryption specification (docs/html/ENCRYPTION.html)
722    password = password.upper()
723    lmhash  = __DES_block(password[:7], KNOWN_DES_INPUT)
724    lmhash += __DES_block(password[7:14], KNOWN_DES_INPUT)
725    return lmhash
726
727def NTOWFv1(password, lmhash = '', nthash=''):
728    if nthash != '':
729       return nthash
730    return compute_nthash(password)
731
732def LMOWFv1(password, lmhash = '', nthash=''):
733    if lmhash != '':
734       return lmhash
735    return compute_lmhash(password)
736
737def compute_nthash(password):
738    # This is done according to Samba's encryption specification (docs/html/ENCRYPTION.html)
739    try:
740        password = unicode(password).encode('utf_16le')
741    except UnicodeDecodeError:
742        import sys
743        password = password.decode(sys.getfilesystemencoding()).encode('utf_16le')
744
745    if POW:
746        hash = POW.Digest(POW.MD4_DIGEST)
747    else:
748        hash = MD4.new()
749    hash.update(password)
750    return hash.digest()
751
752def get_ntlmv1_response(key, challenge):
753    return ntlmssp_DES_encrypt(key, challenge)
754
755# NTLMv2 Algorithm - as described in MS-NLMP Section 3.3.2
756
757# Crypto Stuff
758
759def MAC(flags, handle, signingKey, seqNum, message):
760   # [MS-NLMP] Section 3.4.4
761   # Returns the right messageSignature depending on the flags
762   messageSignature = NTLMMessageSignature(flags)
763   if flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
764       if flags & NTLMSSP_NEGOTIATE_KEY_EXCH:
765           messageSignature['Version'] = 1
766           messageSignature['Checksum'] = struct.unpack('<q',handle(hmac_md5(signingKey, struct.pack('<i',seqNum)+message)[:8]))[0]
767           messageSignature['SeqNum'] = seqNum
768           seqNum += 1
769       else:
770           messageSignature['Version'] = 1
771           messageSignature['Checksum'] = struct.unpack('<q',hmac_md5(signingKey, struct.pack('<i',seqNum)+message)[:8])[0]
772           messageSignature['SeqNum'] = seqNum
773           seqNum += 1
774   else:
775       messageSignature['Version'] = 1
776       messageSignature['Checksum'] = struct.pack('<i',binascii.crc32(message))
777       messageSignature['RandomPad'] = 0
778       messageSignature['RandomPad'] = handle(struct.pack('<i',messageSignature['RandomPad']))
779       messageSignature['Checksum'] = struct.unpack('<i',handle(messageSignature['Checksum']))[0]
780       messageSignature['SeqNum'] = handle('\x00\x00\x00\x00')
781       messageSignature['SeqNum'] = struct.unpack('<i',messageSignature['SeqNum'])[0] ^ seqNum
782       messageSignature['RandomPad'] = 0
783
784   return messageSignature
785
786def SEAL(flags, signingKey, sealingKey, messageToSign, messageToEncrypt, seqNum, handle):
787   sealedMessage = handle(messageToEncrypt)
788   signature = MAC(flags, handle, signingKey, seqNum, messageToSign)
789   return sealedMessage, signature
790
791def SIGN(flags, signingKey, message, seqNum, handle):
792   return MAC(flags, handle, signingKey, seqNum, message)
793
794def SIGNKEY(flags, randomSessionKey, mode = 'Client'):
795   if flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
796       if mode == 'Client':
797           md5 = hashlib.new('md5')
798           md5.update(randomSessionKey + "session key to client-to-server signing key magic constant\x00")
799           signKey = md5.digest()
800       else:
801           md5 = hashlib.new('md5')
802           md5.update(randomSessionKey + "session key to server-to-client signing key magic constant\x00")
803           signKey = md5.digest()
804   else:
805       signKey = None
806   return signKey
807
808def SEALKEY(flags, randomSessionKey, mode = 'Client'):
809   if flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
810       if flags & NTLMSSP_NEGOTIATE_128:
811           sealKey = randomSessionKey
812       elif flags & NTLMSSP_NEGOTIATE_56:
813           sealKey = randomSessionKey[:7]
814       else:
815           sealKey = randomSessionKey[:5]
816
817       if mode == 'Client':
818               md5 = hashlib.new('md5')
819               md5.update(sealKey + 'session key to client-to-server sealing key magic constant\x00')
820               sealKey = md5.digest()
821       else:
822               md5 = hashlib.new('md5')
823               md5.update(sealKey + 'session key to server-to-client sealing key magic constant\x00')
824               sealKey = md5.digest()
825
826   elif flags & NTLMSSP_NEGOTIATE_56:
827       sealKey = randomSessionKey[:7] + '\xa0'
828   else:
829       sealKey = randomSessionKey[:5] + '\xe5\x38\xb0'
830
831   return sealKey
832
833
834def generateEncryptedSessionKey(keyExchangeKey, exportedSessionKey):
835   if POW:
836       cipher = POW.Symmetric(POW.RC4)
837       cipher.encryptInit(keyExchangeKey)
838       cipher_encrypt = cipher.update
839   else:
840       cipher = ARC4.new(keyExchangeKey)
841       cipher_encrypt = cipher.encrypt
842
843   sessionKey = cipher_encrypt(exportedSessionKey)
844   return sessionKey
845
846def KXKEY(flags, sessionBaseKey, lmChallengeResponse, serverChallenge, password, lmhash, nthash, use_ntlmv2 = USE_NTLMv2):
847   if use_ntlmv2:
848       return sessionBaseKey
849
850   if flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
851       if flags & NTLMSSP_NEGOTIATE_NTLM:
852          keyExchangeKey = hmac_md5(sessionBaseKey, serverChallenge + lmChallengeResponse[:8])
853       else:
854          keyExchangeKey = sessionBaseKey
855   elif flags & NTLMSSP_NEGOTIATE_NTLM:
856       if flags & NTLMSSP_NEGOTIATE_LM_KEY:
857          keyExchangeKey = __DES_block(LMOWFv1(password,lmhash)[:7], lmChallengeResponse[:8]) + __DES_block(LMOWFv1(password,lmhash)[7] + '\xBD\xBD\xBD\xBD\xBD\xBD', lmChallengeResponse[:8])
858       elif flags & NTLMSSP_REQUEST_NON_NT_SESSION_KEY:
859          keyExchangeKey = LMOWFv1(password,lmhash)[:8] + '\x00'*8
860       else:
861          keyExchangeKey = sessionBaseKey
862   else:
863       raise "Can't create a valid KXKEY!"
864
865   return keyExchangeKey
866
867def hmac_md5(key, data):
868    if POW:
869        h = POW.Hmac(POW.MD5_DIGEST, key)
870        h.update(data)
871        result = h.mac()
872    else:
873        import hmac
874        h = hmac.new(key)
875        h.update(data)
876        result = h.digest()
877    return result
878
879def NTOWFv2( user, password, domain, hash = ''):
880    if hash != '':
881       theHash = hash
882    else:
883       theHash = compute_nthash(password)
884    return hmac_md5(theHash, user.upper().encode('utf-16le') + domain.encode('utf-16le'))
885
886def LMOWFv2( user, password, domain, lmhash = ''):
887    return NTOWFv2( user, password, domain, lmhash)
888
889
890def computeResponseNTLMv2(flags, serverChallenge, clientChallenge,  serverName, domain, user, password, lmhash = '', nthash = '', use_ntlmv2 = USE_NTLMv2):
891
892    responseServerVersion = '\x01'
893    hiResponseServerVersion = '\x01'
894    responseKeyNT = NTOWFv2(user, password, domain, nthash)
895    responseKeyLM = LMOWFv2(user, password, domain, lmhash)
896
897    # If you're running test-ntlm, comment the following lines and uncoment the ones that are commented. Don't forget to turn it back after the tests!
898    ######################
899    av_pairs = AV_PAIRS(serverName)
900    # In order to support SPN target name validation, we have to add this to the serverName av_pairs. Otherwise we will get access denied
901    # This is set at Local Security Policy -> Local Policies -> Security Options -> Server SPN target name validation level
902    av_pairs[NTLMSSP_AV_TARGET_NAME] = 'cifs/'.encode('utf-16le') + av_pairs[NTLMSSP_AV_HOSTNAME][1]
903    if av_pairs[NTLMSSP_AV_TIME] is not None:
904       aTime = av_pairs[NTLMSSP_AV_TIME][1]
905    else:
906       aTime = struct.pack('<q', (116444736000000000 + calendar.timegm(time.gmtime()) * 10000000) )
907       #aTime = '\x00'*8
908       av_pairs[NTLMSSP_AV_TIME] = aTime
909    serverName = av_pairs.getData()
910
911    ######################
912    #aTime = '\x00'*8
913    ######################
914    temp = responseServerVersion + hiResponseServerVersion + '\x00' * 6 + aTime + clientChallenge + '\x00' * 4 + serverName + '\x00' * 4
915
916    ntProofStr = hmac_md5(responseKeyNT, serverChallenge + temp)
917
918    ntChallengeResponse = ntProofStr + temp
919    lmChallengeResponse = hmac_md5(responseKeyNT, serverChallenge + clientChallenge) + clientChallenge
920    sessionBaseKey = hmac_md5(responseKeyNT, ntProofStr)
921
922    if (user == '' and password == ''):
923        # Special case for anonymous authentication
924        ntChallengeResponse = ''
925        lmChallengeResponse = ''
926
927    return ntChallengeResponse, lmChallengeResponse, sessionBaseKey
928
929class NTLM_HTTP(object):
930    '''Parent class for NTLM HTTP classes.'''
931    MSG_TYPE = None
932
933    @classmethod
934    def get_instace(cls,msg_64):
935        msg = None
936        msg_type = 0
937        if msg_64 != '':
938            msg = base64.b64decode(msg_64[5:]) # Remove the 'NTLM '
939            msg_type = ord(msg[8])
940
941        for _cls in NTLM_HTTP.__subclasses__():
942            if msg_type == _cls.MSG_TYPE:
943                instance = _cls()
944                instance.fromString(msg)
945                return instance
946
947
948class NTLM_HTTP_AuthRequired(NTLM_HTTP):
949    commonHdr = ()
950    # Message 0 means the first HTTP request e.g. 'GET /bla.png'
951    MSG_TYPE = 0
952
953    def fromString(self,data):
954        pass
955
956
957class NTLM_HTTP_AuthNegotiate(NTLM_HTTP, NTLMAuthNegotiate):
958    commonHdr = ()
959    MSG_TYPE = 1
960
961    def __init__(self):
962        NTLMAuthNegotiate.__init__(self)
963
964
965class NTLM_HTTP_AuthChallengeResponse(NTLM_HTTP, NTLMAuthChallengeResponse):
966    commonHdr = ()
967    MSG_TYPE = 3
968
969    def __init__(self):
970        NTLMAuthChallengeResponse.__init__(self)
971
972