123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971 |
- # Copyright (c) 2003-2016 CORE Security Technologies:
- #
- # This software is provided under under a slightly modified version
- # of the Apache Software License. See the accompanying LICENSE file
- # for more information.
- #
- import base64
- import struct
- import calendar
- import time
- import hashlib
- import random
- import string
- import binascii
- from impacket.structure import Structure
- from impacket import LOG
- # This is important. NTLMv2 is not negotiated by the client or server.
- # It is used if set locally on both sides. Change this item if you don't want to use
- # NTLMv2 by default and fall back to NTLMv1 (with EXTENDED_SESSION_SECURITY or not)
- # Check the following links:
- # http://davenport.sourceforge.net/ntlm.html
- # http://blogs.msdn.com/b/openspecification/archive/2010/04/20/ntlm-keys-and-sundry-stuff.aspx
- # http://social.msdn.microsoft.com/Forums/en-US/os_interopscenarios/thread/c8f488ed-1b96-4e06-bd65-390aa41138d1/
- # So I'm setting a global variable to control this, this can also be set programmatically
- USE_NTLMv2 = True # if false will fall back to NTLMv1 (or NTLMv1 with ESS a.k.a NTLM2)
- def computeResponse(flags, serverChallenge, clientChallenge, serverName, domain, user, password, lmhash='', nthash='',
- use_ntlmv2=USE_NTLMv2):
- if use_ntlmv2:
- return computeResponseNTLMv2(flags, serverChallenge, clientChallenge, serverName, domain, user, password,
- lmhash, nthash, use_ntlmv2=use_ntlmv2)
- else:
- return computeResponseNTLMv1(flags, serverChallenge, clientChallenge, serverName, domain, user, password,
- lmhash, nthash, use_ntlmv2=use_ntlmv2)
- try:
- POW = None
- from Crypto.Cipher import ARC4
- from Crypto.Cipher import DES
- from Crypto.Hash import MD4
- except Exception:
- try:
- import POW
- except Exception:
- LOG.critical("Warning: You don't have any crypto installed. You need PyCrypto")
- LOG.critical("See http://www.pycrypto.org/")
- NTLM_AUTH_NONE = 1
- NTLM_AUTH_CONNECT = 2
- NTLM_AUTH_CALL = 3
- NTLM_AUTH_PKT = 4
- NTLM_AUTH_PKT_INTEGRITY = 5
- NTLM_AUTH_PKT_PRIVACY = 6
- # If set, requests 56-bit encryption. If the client sends NTLMSSP_NEGOTIATE_SEAL or NTLMSSP_NEGOTIATE_SIGN
- # with NTLMSSP_NEGOTIATE_56 to the server in the NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_56 to
- # the client in the CHALLENGE_MESSAGE. Otherwise it is ignored. If both NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128
- # are requested and supported by the client and server, NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 will both be
- # returned to the client. Clients and servers that set NTLMSSP_NEGOTIATE_SEAL SHOULD set NTLMSSP_NEGOTIATE_56 if it is
- # supported. An alternate name for this field is NTLMSSP_NEGOTIATE_56.
- NTLMSSP_NEGOTIATE_56 = 0x80000000
- # If set, requests an explicit key exchange. This capability SHOULD be used because it improves security for message
- # 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
- # this field is NTLMSSP_NEGOTIATE_KEY_EXCH.
- NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000
- # If set, requests 128-bit session key negotiation. An alternate name for this field is NTLMSSP_NEGOTIATE_128.
- # If the client sends NTLMSSP_NEGOTIATE_128 to the server in the NEGOTIATE_MESSAGE, the server MUST return
- # NTLMSSP_NEGOTIATE_128 to the client in the CHALLENGE_MESSAGE only if the client sets NTLMSSP_NEGOTIATE_SEAL or
- # NTLMSSP_NEGOTIATE_SIGN. Otherwise it is ignored. If both NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 are
- # requested and supported by the client and server, NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 will both be
- # returned to the client. Clients and servers that set NTLMSSP_NEGOTIATE_SEAL SHOULD set NTLMSSP_NEGOTIATE_128 if it
- # is supported. An alternate name for this field is NTLMSSP_NEGOTIATE_128
- NTLMSSP_NEGOTIATE_128 = 0x20000000
- NTLMSSP_RESERVED_1 = 0x10000000
- NTLMSSP_RESERVED_2 = 0x08000000
- NTLMSSP_RESERVED_3 = 0x04000000
- # If set, requests the protocol version number. The data corresponding to this flag is provided in the Version field
- # of the NEGOTIATE_MESSAGE, the CHALLENGE_MESSAGE, and the AUTHENTICATE_MESSAGE.<22> An alternate name for this field
- # is NTLMSSP_NEGOTIATE_VERSION
- NTLMSSP_NEGOTIATE_VERSION = 0x02000000
- NTLMSSP_RESERVED_4 = 0x01000000
- # If set, indicates that the TargetInfo fields in the CHALLENGE_MESSAGE (section 2.2.1.2) are populated.
- # An alternate name for this field is NTLMSSP_NEGOTIATE_TARGET_INFO.
- NTLMSSP_NEGOTIATE_TARGET_INFO = 0x00800000
- # If set, requests the usage of the LMOWF (section 3.3). An alternate name for this field is
- # NTLMSSP_REQUEST_NON_NT_SESSION_KEY.
- NTLMSSP_REQUEST_NON_NT_SESSION_KEY = 0x00400000
- NTLMSSP_RESERVED_5 = 0x00200000
- # If set, requests an identify level token. An alternate name for this field is NTLMSSP_NEGOTIATE_IDENTIFY
- NTLMSSP_NEGOTIATE_IDENTIFY = 0x00100000
- # If set, requests usage of the NTLM v2 session security. NTLM v2 session security is a misnomer because it is not
- # NTLM v2. It is NTLM v1 using the extended session security that is also in NTLM v2. NTLMSSP_NEGOTIATE_LM_KEY and
- # NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY are mutually exclusive. If both NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
- # and NTLMSSP_NEGOTIATE_LM_KEY are requested, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY alone MUST be returned to the
- # client. NTLM v2 authentication session key generation MUST be supported by both the client and the DC in order to be
- # used, and extended session security signing and sealing requires support from the client and the server in order to
- # be used.<23> An alternate name for this field is NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
- NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000
- NTLMSSP_NEGOTIATE_NTLM2 = 0x00080000
- NTLMSSP_TARGET_TYPE_SHARE = 0x00040000
- # If set, TargetName MUST be a server name. The data corresponding to this flag is provided by the server in the
- # TargetName field of the CHALLENGE_MESSAGE. If this bit is set, then NTLMSSP_TARGET_TYPE_DOMAIN MUST NOT be set.
- # This flag MUST be ignored in the NEGOTIATE_MESSAGE and the AUTHENTICATE_MESSAGE. An alternate name for this field
- # is NTLMSSP_TARGET_TYPE_SERVER
- NTLMSSP_TARGET_TYPE_SERVER = 0x00020000
- # If set, TargetName MUST be a domain name. The data corresponding to this flag is provided by the server in the
- # TargetName field of the CHALLENGE_MESSAGE. If set, then NTLMSSP_TARGET_TYPE_SERVER MUST NOT be set. This flag MUST
- # be ignored in the NEGOTIATE_MESSAGE and the AUTHENTICATE_MESSAGE. An alternate name for this field is
- # NTLMSSP_TARGET_TYPE_DOMAIN.
- NTLMSSP_TARGET_TYPE_DOMAIN = 0x00010000
- # If set, requests the presence of a signature block on all messages. NTLMSSP_NEGOTIATE_ALWAYS_SIGN MUST be set in the
- # NEGOTIATE_MESSAGE to the server and the CHALLENGE_MESSAGE to the client. NTLMSSP_NEGOTIATE_ALWAYS_SIGN is overridden
- # by NTLMSSP_NEGOTIATE_SIGN and NTLMSSP_NEGOTIATE_SEAL, if they are supported. An alternate name for this field is
- # NTLMSSP_NEGOTIATE_ALWAYS_SIGN.
- NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x00008000 # forces the other end to sign packets
- NTLMSSP_RESERVED_6 = 0x00004000
- # This flag indicates whether the Workstation field is present. If this flag is not set, the Workstation field MUST be
- # ignored. If this flag is set, the length field of the Workstation field specifies whether the workstation name is
- # nonempty or not.<24> An alternate name for this field is NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED.
- NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x00002000
- # If set, the domain name is provided (section 2.2.1.1).<25> An alternate name for this field is
- # NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED
- NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED = 0x00001000
- NTLMSSP_RESERVED_7 = 0x00000800
- # If set, LM authentication is not allowed and only NT authentication is used.
- NTLMSSP_NEGOTIATE_NT_ONLY = 0x00000400
- # If set, requests usage of the NTLM v1 session security protocol. NTLMSSP_NEGOTIATE_NTLM MUST be set in the
- # NEGOTIATE_MESSAGE to the server and the CHALLENGE_MESSAGE to the client. An alternate name for this field is
- # NTLMSSP_NEGOTIATE_NTLM
- NTLMSSP_NEGOTIATE_NTLM = 0x00000200
- NTLMSSP_RESERVED_8 = 0x00000100
- # If set, requests LAN Manager (LM) session key computation. NTLMSSP_NEGOTIATE_LM_KEY and
- # NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY are mutually exclusive. If both NTLMSSP_NEGOTIATE_LM_KEY and
- # NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY are requested, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY alone MUST be
- # returned to the client. NTLM v2 authentication session key generation MUST be supported by both the client and the
- # DC in order to be used, and extended session security signing and sealing requires support from the client and the
- # server to be used. An alternate name for this field is NTLMSSP_NEGOTIATE_LM_KEY.
- NTLMSSP_NEGOTIATE_LM_KEY = 0x00000080
- # If set, requests connectionless authentication. If NTLMSSP_NEGOTIATE_DATAGRAM is set, then NTLMSSP_NEGOTIATE_KEY_EXCH
- # MUST always be set in the AUTHENTICATE_MESSAGE to the server and the CHALLENGE_MESSAGE to the client. An alternate
- # name for this field is NTLMSSP_NEGOTIATE_DATAGRAM.
- NTLMSSP_NEGOTIATE_DATAGRAM = 0x00000040
- # If set, requests session key negotiation for message confidentiality. If the client sends NTLMSSP_NEGOTIATE_SEAL to
- # the server in the NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_SEAL to the client in the
- # CHALLENGE_MESSAGE. Clients and servers that set NTLMSSP_NEGOTIATE_SEAL SHOULD always set NTLMSSP_NEGOTIATE_56 and
- # NTLMSSP_NEGOTIATE_128, if they are supported. An alternate name for this field is NTLMSSP_NEGOTIATE_SEAL.
- NTLMSSP_NEGOTIATE_SEAL = 0x00000020
- # If set, requests session key negotiation for message signatures. If the client sends NTLMSSP_NEGOTIATE_SIGN to the
- # server in the NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_SIGN to the client in the CHALLENGE_MESSAGE.
- # An alternate name for this field is NTLMSSP_NEGOTIATE_SIGN.
- NTLMSSP_NEGOTIATE_SIGN = 0x00000010 # means packet is signed, if verifier is wrong it fails
- NTLMSSP_RESERVED_9 = 0x00000008
- # If set, a TargetName field of the CHALLENGE_MESSAGE (section 2.2.1.2) MUST be supplied. An alternate name for this
- # field is NTLMSSP_REQUEST_TARGET.
- NTLMSSP_REQUEST_TARGET = 0x00000004
- # If set, requests OEM character set encoding. An alternate name for this field is NTLM_NEGOTIATE_OEM. See bit A for
- # details.
- NTLM_NEGOTIATE_OEM = 0x00000002
- # If set, requests Unicode character set encoding. An alternate name for this field is NTLMSSP_NEGOTIATE_UNICODE.
- NTLMSSP_NEGOTIATE_UNICODE = 0x00000001
- # AV_PAIR constants
- NTLMSSP_AV_EOL = 0x00
- NTLMSSP_AV_HOSTNAME = 0x01
- NTLMSSP_AV_DOMAINNAME = 0x02
- NTLMSSP_AV_DNS_HOSTNAME = 0x03
- NTLMSSP_AV_DNS_DOMAINNAME = 0x04
- NTLMSSP_AV_DNS_TREENAME = 0x05
- NTLMSSP_AV_FLAGS = 0x06
- NTLMSSP_AV_TIME = 0x07
- NTLMSSP_AV_RESTRICTIONS = 0x08
- NTLMSSP_AV_TARGET_NAME = 0x09
- NTLMSSP_AV_CHANNEL_BINDINGS = 0x0a
- class AV_PAIRS():
- def __init__(self, data = None):
- self.fields = {}
- if data is not None:
- self.fromString(data)
- def __setitem__(self,key,value):
- self.fields[key] = (len(value),value)
- def __getitem__(self, key):
- if self.fields.has_key(key):
- return self.fields[key]
- return None
- def __delitem__(self, key):
- del self.fields[key]
- def __len__(self):
- return len(self.getData())
- def __str__(self):
- return len(self.getData())
- def fromString(self, data):
- tInfo = data
- fType = 0xff
- while fType is not NTLMSSP_AV_EOL:
- fType = struct.unpack('<H',tInfo[:struct.calcsize('<H')])[0]
- tInfo = tInfo[struct.calcsize('<H'):]
- length = struct.unpack('<H',tInfo[:struct.calcsize('<H')])[0]
- tInfo = tInfo[struct.calcsize('<H'):]
- content = tInfo[:length]
- self.fields[fType]=(length,content)
- tInfo = tInfo[length:]
- def dump(self):
- for i in self.fields.keys():
- print "%s: {%r}" % (i,self[i])
- def getData(self):
- if self.fields.has_key(NTLMSSP_AV_EOL):
- del self.fields[NTLMSSP_AV_EOL]
- ans = ''
- for i in self.fields.keys():
- ans+= struct.pack('<HH', i, self[i][0])
- ans+= self[i][1]
-
- # end with a NTLMSSP_AV_EOL
- ans += struct.pack('<HH', NTLMSSP_AV_EOL, 0)
- return ans
- class NTLMAuthMixin:
- def get_os_version(self):
- if self['os_version'] == '':
- return None
- else:
- mayor_v = struct.unpack('B',self['os_version'][0])[0]
- minor_v = struct.unpack('B',self['os_version'][1])[0]
- build_v = struct.unpack('H',self['os_version'][2:4])
- return (mayor_v,minor_v,build_v)
-
- class NTLMAuthNegotiate(Structure, NTLMAuthMixin):
- structure = (
- ('','"NTLMSSP\x00'),
- ('message_type','<L=1'),
- ('flags','<L'),
- ('domain_len','<H-domain_name'),
- ('domain_max_len','<H-domain_name'),
- ('domain_offset','<L=0'),
- ('host_len','<H-host_name'),
- ('host_maxlen','<H-host_name'),
- ('host_offset','<L=0'),
- ('os_version',':'),
- ('host_name',':'),
- ('domain_name',':'))
-
- def __init__(self):
- Structure.__init__(self)
- self['flags']= (
- NTLMSSP_NEGOTIATE_128 |
- NTLMSSP_NEGOTIATE_KEY_EXCH|
- # NTLMSSP_LM_KEY |
- NTLMSSP_NEGOTIATE_NTLM |
- NTLMSSP_NEGOTIATE_UNICODE |
- # NTLMSSP_ALWAYS_SIGN |
- NTLMSSP_NEGOTIATE_SIGN |
- NTLMSSP_NEGOTIATE_SEAL |
- # NTLMSSP_TARGET |
- 0)
- self['host_name']=''
- self['domain_name']=''
- self['os_version']=''
-
- def getData(self):
- if len(self.fields['host_name']) > 0:
- self['flags'] |= NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED
- if len(self.fields['domain_name']) > 0:
- self['flags'] |= NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED
- if len(self.fields['os_version']) > 0:
- self['flags'] |= NTLMSSP_NEGOTIATE_VERSION
- if (self['flags'] & NTLMSSP_NEGOTIATE_VERSION) == NTLMSSP_NEGOTIATE_VERSION:
- version_len = 8
- else:
- version_len = 0
- if (self['flags'] & NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED) == NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED:
- self['host_offset']=32 + version_len
- if (self['flags'] & NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED) == NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED:
- self['domain_offset']=32+len(self['host_name']) + version_len
- return Structure.getData(self)
- def fromString(self,data):
- Structure.fromString(self,data)
- domain_offset = self['domain_offset']
- domain_end = self['domain_len'] + domain_offset
- self['domain_name'] = data[ domain_offset : domain_end ]
- host_offset = self['host_offset']
- host_end = self['host_len'] + host_offset
- self['host_name'] = data[ host_offset : host_end ]
- hasOsInfo = self['flags'] & NTLMSSP_NEGOTIATE_VERSION
- if len(data) >= 36 and hasOsInfo:
- self['os_version'] = data[32:40]
- else:
- self['os_version'] = ''
- class NTLMAuthChallenge(Structure):
- structure = (
- ('','"NTLMSSP\x00'),
- ('message_type','<L=2'),
- ('domain_len','<H-domain_name'),
- ('domain_max_len','<H-domain_name'),
- ('domain_offset','<L=40'),
- ('flags','<L=0'),
- ('challenge','8s'),
- ('reserved','8s=""'),
- ('TargetInfoFields_len','<H-TargetInfoFields'),
- ('TargetInfoFields_max_len','<H-TargetInfoFields'),
- ('TargetInfoFields_offset','<L'),
- ('VersionLen','_-Version','self.checkVersion(self["flags"])'),
- ('Version',':'),
- ('domain_name',':'),
- ('TargetInfoFields',':'))
- def checkVersion(self, flags):
- if flags is not None:
- if flags & NTLMSSP_NEGOTIATE_VERSION == 0:
- return 0
- return 8
- def getData(self):
- if self['TargetInfoFields'] is not None and type(self['TargetInfoFields']) is not str:
- raw_av_fields = self['TargetInfoFields'].getData()
- self['TargetInfoFields'] = raw_av_fields
- return Structure.getData(self)
- def fromString(self,data):
- Structure.fromString(self,data)
- # Just in case there's more data after the TargetInfoFields
- self['TargetInfoFields'] = self['TargetInfoFields'][:self['TargetInfoFields_len']]
- # We gotta process the TargetInfoFields
- #if self['TargetInfoFields_len'] > 0:
- # av_pairs = AV_PAIRS(self['TargetInfoFields'][:self['TargetInfoFields_len']])
- # self['TargetInfoFields'] = av_pairs
- return self
-
- class NTLMAuthChallengeResponse(Structure, NTLMAuthMixin):
- structure = (
- ('','"NTLMSSP\x00'),
- ('message_type','<L=3'),
- ('lanman_len','<H-lanman'),
- ('lanman_max_len','<H-lanman'),
- ('lanman_offset','<L'),
- ('ntlm_len','<H-ntlm'),
- ('ntlm_max_len','<H-ntlm'),
- ('ntlm_offset','<L'),
- ('domain_len','<H-domain_name'),
- ('domain_max_len','<H-domain_name'),
- ('domain_offset','<L'),
- ('user_len','<H-user_name'),
- ('user_max_len','<H-user_name'),
- ('user_offset','<L'),
- ('host_len','<H-host_name'),
- ('host_max_len','<H-host_name'),
- ('host_offset','<L'),
- ('session_key_len','<H-session_key'),
- ('session_key_max_len','<H-session_key'),
- ('session_key_offset','<L'),
- ('flags','<L'),
- ('VersionLen','_-Version','self.checkVersion(self["flags"])'),
- ('Version',':=""'),
- ('MICLen','_-MIC','self.checkMIC(self["flags"])'),
- ('MIC',':=""'),
- ('domain_name',':'),
- ('user_name',':'),
- ('host_name',':'),
- ('lanman',':'),
- ('ntlm',':'),
- ('session_key',':'))
- def __init__(self, username = '', password = '', challenge = '', lmhash = '', nthash = '', flags = 0):
- Structure.__init__(self)
- self['session_key']=''
- self['user_name']=username.encode('utf-16le')
- self['domain_name']='' #"CLON".encode('utf-16le')
- self['host_name']='' #"BETS".encode('utf-16le')
- self['flags'] = ( #authResp['flags']
- # we think (beto & gera) that his flags force a memory conten leakage when a windows 2000 answers using uninitializaed verifiers
- NTLMSSP_NEGOTIATE_128 |
- NTLMSSP_NEGOTIATE_KEY_EXCH|
- # NTLMSSP_LM_KEY |
- NTLMSSP_NEGOTIATE_NTLM |
- NTLMSSP_NEGOTIATE_UNICODE |
- # NTLMSSP_ALWAYS_SIGN |
- NTLMSSP_NEGOTIATE_SIGN |
- NTLMSSP_NEGOTIATE_SEAL |
- # NTLMSSP_TARGET |
- 0)
- # Here we do the stuff
- if username and ( lmhash != '' or nthash != ''):
- self['lanman'] = get_ntlmv1_response(lmhash, challenge)
- self['ntlm'] = get_ntlmv1_response(nthash, challenge)
- elif (username and password):
- lmhash = compute_lmhash(password)
- nthash = compute_nthash(password)
- self['lanman']=get_ntlmv1_response(lmhash, challenge)
- self['ntlm']=get_ntlmv1_response(nthash, challenge) # This is not used for LM_KEY nor NTLM_KEY
- else:
- self['lanman'] = ''
- self['ntlm'] = ''
- if not self['host_name']:
- self['host_name'] = 'NULL'.encode('utf-16le') # for NULL session there must be a hostname
- def checkVersion(self, flags):
- if flags is not None:
- if flags & NTLMSSP_NEGOTIATE_VERSION == 0:
- return 0
- return 8
- def checkMIC(self, flags):
- # TODO: Find a proper way to check the MIC is in there
- if flags is not None:
- if flags & NTLMSSP_NEGOTIATE_VERSION == 0:
- return 0
- return 16
-
- def getData(self):
- self['domain_offset']=64+self.checkMIC(self["flags"])+self.checkVersion(self["flags"])
- self['user_offset']=64+self.checkMIC(self["flags"])+self.checkVersion(self["flags"])+len(self['domain_name'])
- self['host_offset']=self['user_offset']+len(self['user_name'])
- self['lanman_offset']=self['host_offset']+len(self['host_name'])
- self['ntlm_offset']=self['lanman_offset']+len(self['lanman'])
- self['session_key_offset']=self['ntlm_offset']+len(self['ntlm'])
- return Structure.getData(self)
- def fromString(self,data):
- Structure.fromString(self,data)
- # [MS-NLMP] page 27
- # Payload data can be present in any order within the Payload field,
- # with variable-length padding before or after the data
- domain_offset = self['domain_offset']
- domain_end = self['domain_len'] + domain_offset
- self['domain_name'] = data[ domain_offset : domain_end ]
- host_offset = self['host_offset']
- host_end = self['host_len'] + host_offset
- self['host_name'] = data[ host_offset: host_end ]
- user_offset = self['user_offset']
- user_end = self['user_len'] + user_offset
- self['user_name'] = data[ user_offset: user_end ]
- ntlm_offset = self['ntlm_offset']
- ntlm_end = self['ntlm_len'] + ntlm_offset
- self['ntlm'] = data[ ntlm_offset : ntlm_end ]
- lanman_offset = self['lanman_offset']
- lanman_end = self['lanman_len'] + lanman_offset
- self['lanman'] = data[ lanman_offset : lanman_end]
- #if len(data) >= 36:
- # self['os_version'] = data[32:36]
- #else:
- # self['os_version'] = ''
- class ImpacketStructure(Structure):
- def set_parent(self, other):
- self.parent = other
- def get_packet(self):
- return str(self)
- def get_size(self):
- return len(self)
- class ExtendedOrNotMessageSignature(Structure):
- def __init__(self, flags = 0, **kargs):
- if flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
- self.structure = self.extendedMessageSignature
- else:
- self.structure = self.MessageSignature
- return Structure.__init__(self, **kargs)
- class NTLMMessageSignature(ExtendedOrNotMessageSignature):
- extendedMessageSignature = (
- ('Version','<L=1'),
- ('Checksum','<q'),
- ('SeqNum','<i'),
- )
- MessageSignature = (
- ('Version','<L=1'),
- ('RandomPad','<i=0'),
- ('Checksum','<i'),
- ('SeqNum','<i'),
- )
- KNOWN_DES_INPUT = "KGS!@#$%"
- def __expand_DES_key( key):
- # Expand the key from a 7-byte password key into a 8-byte DES key
- key = key[:7]
- key += '\x00'*(7-len(key))
- s = chr(((ord(key[0]) >> 1) & 0x7f) << 1)
- s = s + chr(((ord(key[0]) & 0x01) << 6 | ((ord(key[1]) >> 2) & 0x3f)) << 1)
- s = s + chr(((ord(key[1]) & 0x03) << 5 | ((ord(key[2]) >> 3) & 0x1f)) << 1)
- s = s + chr(((ord(key[2]) & 0x07) << 4 | ((ord(key[3]) >> 4) & 0x0f)) << 1)
- s = s + chr(((ord(key[3]) & 0x0f) << 3 | ((ord(key[4]) >> 5) & 0x07)) << 1)
- s = s + chr(((ord(key[4]) & 0x1f) << 2 | ((ord(key[5]) >> 6) & 0x03)) << 1)
- s = s + chr(((ord(key[5]) & 0x3f) << 1 | ((ord(key[6]) >> 7) & 0x01)) << 1)
- s = s + chr((ord(key[6]) & 0x7f) << 1)
- return s
- def __DES_block(key, msg):
- if POW:
- cipher = POW.Symmetric(POW.DES_ECB)
- cipher.encryptInit(__expand_DES_key(key))
- return cipher.update(msg)
- else:
- cipher = DES.new(__expand_DES_key(key),DES.MODE_ECB)
- return cipher.encrypt(msg)
- def ntlmssp_DES_encrypt(key, challenge):
- answer = __DES_block(key[:7], challenge)
- answer += __DES_block(key[7:14], challenge)
- answer += __DES_block(key[14:], challenge)
- return answer
- # High level functions to use NTLMSSP
- def getNTLMSSPType1(workstation='', domain='', signingRequired = False, use_ntlmv2 = USE_NTLMv2):
- # Let's do some encoding checks before moving on. Kind of dirty, but found effective when dealing with
- # international characters.
- import sys
- encoding = sys.getfilesystemencoding()
- if encoding is not None:
- try:
- workstation.encode('utf-16le')
- except:
- workstation = workstation.decode(encoding)
- try:
- domain.encode('utf-16le')
- except:
- domain = domain.decode(encoding)
- # Let's prepare a Type 1 NTLMSSP Message
- auth = NTLMAuthNegotiate()
- auth['flags']=0
- if signingRequired:
- auth['flags'] = NTLMSSP_NEGOTIATE_KEY_EXCH | NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_SEAL
- if use_ntlmv2:
- auth['flags'] |= NTLMSSP_NEGOTIATE_TARGET_INFO
- auth['flags'] |= NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_56
- auth['domain_name'] = domain.encode('utf-16le')
- return auth
- def getNTLMSSPType3(type1, type2, user, password, domain, lmhash = '', nthash = '', use_ntlmv2 = USE_NTLMv2):
- # Let's do some encoding checks before moving on. Kind of dirty, but found effective when dealing with
- # international characters.
- import sys
- encoding = sys.getfilesystemencoding()
- if encoding is not None:
- try:
- user.encode('utf-16le')
- except:
- user = user.decode(encoding)
- try:
- password.encode('utf-16le')
- except:
- password = password.decode(encoding)
- try:
- domain.encode('utf-16le')
- except:
- domain = user.decode(encoding)
- ntlmChallenge = NTLMAuthChallenge(type2)
- # Let's start with the original flags sent in the type1 message
- responseFlags = type1['flags']
- # Token received and parsed. Depending on the authentication
- # method we will create a valid ChallengeResponse
- ntlmChallengeResponse = NTLMAuthChallengeResponse(user, password, ntlmChallenge['challenge'])
- clientChallenge = "".join([random.choice(string.digits+string.letters) for i in xrange(8)])
- serverName = ntlmChallenge['TargetInfoFields']
- ntResponse, lmResponse, sessionBaseKey = computeResponse(ntlmChallenge['flags'], ntlmChallenge['challenge'], clientChallenge, serverName, domain, user, password, lmhash, nthash, use_ntlmv2 )
- # Let's check the return flags
- if (ntlmChallenge['flags'] & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) == 0:
- # No extended session security, taking it out
- responseFlags &= 0xffffffff ^ NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
- if (ntlmChallenge['flags'] & NTLMSSP_NEGOTIATE_128 ) == 0:
- # No support for 128 key len, taking it out
- responseFlags &= 0xffffffff ^ NTLMSSP_NEGOTIATE_128
- if (ntlmChallenge['flags'] & NTLMSSP_NEGOTIATE_KEY_EXCH) == 0:
- # No key exchange supported, taking it out
- responseFlags &= 0xffffffff ^ NTLMSSP_NEGOTIATE_KEY_EXCH
- if (ntlmChallenge['flags'] & NTLMSSP_NEGOTIATE_SEAL) == 0:
- # No sign available, taking it out
- responseFlags &= 0xffffffff ^ NTLMSSP_NEGOTIATE_SEAL
- if (ntlmChallenge['flags'] & NTLMSSP_NEGOTIATE_SIGN) == 0:
- # No sign available, taking it out
- responseFlags &= 0xffffffff ^ NTLMSSP_NEGOTIATE_SIGN
- if (ntlmChallenge['flags'] & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) == 0:
- # No sign available, taking it out
- responseFlags &= 0xffffffff ^ NTLMSSP_NEGOTIATE_ALWAYS_SIGN
- keyExchangeKey = KXKEY(ntlmChallenge['flags'],sessionBaseKey, lmResponse, ntlmChallenge['challenge'], password, lmhash, nthash,use_ntlmv2)
- # Special case for anonymous login
- if user == '' and password == '' and lmhash == '' and nthash == '':
- keyExchangeKey = '\x00'*16
- # If we set up key exchange, let's fill the right variables
- if ntlmChallenge['flags'] & NTLMSSP_NEGOTIATE_KEY_EXCH:
- # not exactly what I call random tho :\
- # exportedSessionKey = this is the key we should use to sign
- exportedSessionKey = "".join([random.choice(string.digits+string.letters) for i in xrange(16)])
- #exportedSessionKey = "A"*16
- #print "keyExchangeKey %r" % keyExchangeKey
- # Let's generate the right session key based on the challenge flags
- #if responseFlags & NTLMSSP_NTLM2_KEY:
- # Extended session security enabled
- # if responseFlags & NTLMSSP_KEY_128:
- # Full key
- # exportedSessionKey = exportedSessionKey
- # elif responseFlags & NTLMSSP_KEY_56:
- # Only 56-bit key
- # exportedSessionKey = exportedSessionKey[:7]
- # else:
- # exportedSessionKey = exportedSessionKey[:5]
- #elif responseFlags & NTLMSSP_KEY_56:
- # No extended session security, just 56 bits key
- # exportedSessionKey = exportedSessionKey[:7] + '\xa0'
- #else:
- # exportedSessionKey = exportedSessionKey[:5] + '\xe5\x38\xb0'
- encryptedRandomSessionKey = generateEncryptedSessionKey(keyExchangeKey, exportedSessionKey)
- else:
- encryptedRandomSessionKey = None
- # [MS-NLMP] page 46
- exportedSessionKey = keyExchangeKey
- ntlmChallengeResponse['flags'] = responseFlags
- ntlmChallengeResponse['domain_name'] = domain.encode('utf-16le')
- ntlmChallengeResponse['lanman'] = lmResponse
- ntlmChallengeResponse['ntlm'] = ntResponse
- if encryptedRandomSessionKey is not None:
- ntlmChallengeResponse['session_key'] = encryptedRandomSessionKey
- return ntlmChallengeResponse, exportedSessionKey
- # NTLMv1 Algorithm
- def generateSessionKeyV1(password, lmhash, nthash):
- if POW:
- hash = POW.Digest(POW.MD4_DIGEST)
- else:
- hash = MD4.new()
- hash.update(NTOWFv1(password, lmhash, nthash))
- return hash.digest()
-
- def computeResponseNTLMv1(flags, serverChallenge, clientChallenge, serverName, domain, user, password, lmhash='', nthash='', use_ntlmv2 = USE_NTLMv2):
- if (user == '' and password == ''):
- # Special case for anonymous authentication
- lmResponse = ''
- ntResponse = ''
- else:
- lmhash = LMOWFv1(password, lmhash, nthash)
- nthash = NTOWFv1(password, lmhash, nthash)
- if flags & NTLMSSP_NEGOTIATE_LM_KEY:
- ntResponse = ''
- lmResponse = get_ntlmv1_response(lmhash, serverChallenge)
- elif flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
- md5 = hashlib.new('md5')
- chall = (serverChallenge + clientChallenge)
- md5.update(chall)
- ntResponse = ntlmssp_DES_encrypt(nthash, md5.digest()[:8])
- lmResponse = clientChallenge + '\x00'*16
- else:
- ntResponse = get_ntlmv1_response(nthash,serverChallenge)
- lmResponse = get_ntlmv1_response(lmhash, serverChallenge)
-
- sessionBaseKey = generateSessionKeyV1(password, lmhash, nthash)
- return ntResponse, lmResponse, sessionBaseKey
- def compute_lmhash(password):
- # This is done according to Samba's encryption specification (docs/html/ENCRYPTION.html)
- password = password.upper()
- lmhash = __DES_block(password[:7], KNOWN_DES_INPUT)
- lmhash += __DES_block(password[7:14], KNOWN_DES_INPUT)
- return lmhash
- def NTOWFv1(password, lmhash = '', nthash=''):
- if nthash != '':
- return nthash
- return compute_nthash(password)
- def LMOWFv1(password, lmhash = '', nthash=''):
- if lmhash != '':
- return lmhash
- return compute_lmhash(password)
- def compute_nthash(password):
- # This is done according to Samba's encryption specification (docs/html/ENCRYPTION.html)
- try:
- password = unicode(password).encode('utf_16le')
- except UnicodeDecodeError:
- import sys
- password = password.decode(sys.getfilesystemencoding()).encode('utf_16le')
- if POW:
- hash = POW.Digest(POW.MD4_DIGEST)
- else:
- hash = MD4.new()
- hash.update(password)
- return hash.digest()
- def get_ntlmv1_response(key, challenge):
- return ntlmssp_DES_encrypt(key, challenge)
- # NTLMv2 Algorithm - as described in MS-NLMP Section 3.3.2
- # Crypto Stuff
- def MAC(flags, handle, signingKey, seqNum, message):
- # [MS-NLMP] Section 3.4.4
- # Returns the right messageSignature depending on the flags
- messageSignature = NTLMMessageSignature(flags)
- if flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
- if flags & NTLMSSP_NEGOTIATE_KEY_EXCH:
- messageSignature['Version'] = 1
- messageSignature['Checksum'] = struct.unpack('<q',handle(hmac_md5(signingKey, struct.pack('<i',seqNum)+message)[:8]))[0]
- messageSignature['SeqNum'] = seqNum
- seqNum += 1
- else:
- messageSignature['Version'] = 1
- messageSignature['Checksum'] = struct.unpack('<q',hmac_md5(signingKey, struct.pack('<i',seqNum)+message)[:8])[0]
- messageSignature['SeqNum'] = seqNum
- seqNum += 1
- else:
- messageSignature['Version'] = 1
- messageSignature['Checksum'] = struct.pack('<i',binascii.crc32(message))
- messageSignature['RandomPad'] = 0
- messageSignature['RandomPad'] = handle(struct.pack('<i',messageSignature['RandomPad']))
- messageSignature['Checksum'] = struct.unpack('<i',handle(messageSignature['Checksum']))[0]
- messageSignature['SeqNum'] = handle('\x00\x00\x00\x00')
- messageSignature['SeqNum'] = struct.unpack('<i',messageSignature['SeqNum'])[0] ^ seqNum
- messageSignature['RandomPad'] = 0
-
- return messageSignature
- def SEAL(flags, signingKey, sealingKey, messageToSign, messageToEncrypt, seqNum, handle):
- sealedMessage = handle(messageToEncrypt)
- signature = MAC(flags, handle, signingKey, seqNum, messageToSign)
- return sealedMessage, signature
- def SIGN(flags, signingKey, message, seqNum, handle):
- return MAC(flags, handle, signingKey, seqNum, message)
- def SIGNKEY(flags, randomSessionKey, mode = 'Client'):
- if flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
- if mode == 'Client':
- md5 = hashlib.new('md5')
- md5.update(randomSessionKey + "session key to client-to-server signing key magic constant\x00")
- signKey = md5.digest()
- else:
- md5 = hashlib.new('md5')
- md5.update(randomSessionKey + "session key to server-to-client signing key magic constant\x00")
- signKey = md5.digest()
- else:
- signKey = None
- return signKey
- def SEALKEY(flags, randomSessionKey, mode = 'Client'):
- if flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
- if flags & NTLMSSP_NEGOTIATE_128:
- sealKey = randomSessionKey
- elif flags & NTLMSSP_NEGOTIATE_56:
- sealKey = randomSessionKey[:7]
- else:
- sealKey = randomSessionKey[:5]
- if mode == 'Client':
- md5 = hashlib.new('md5')
- md5.update(sealKey + 'session key to client-to-server sealing key magic constant\x00')
- sealKey = md5.digest()
- else:
- md5 = hashlib.new('md5')
- md5.update(sealKey + 'session key to server-to-client sealing key magic constant\x00')
- sealKey = md5.digest()
- elif flags & NTLMSSP_NEGOTIATE_56:
- sealKey = randomSessionKey[:7] + '\xa0'
- else:
- sealKey = randomSessionKey[:5] + '\xe5\x38\xb0'
- return sealKey
- def generateEncryptedSessionKey(keyExchangeKey, exportedSessionKey):
- if POW:
- cipher = POW.Symmetric(POW.RC4)
- cipher.encryptInit(keyExchangeKey)
- cipher_encrypt = cipher.update
- else:
- cipher = ARC4.new(keyExchangeKey)
- cipher_encrypt = cipher.encrypt
- sessionKey = cipher_encrypt(exportedSessionKey)
- return sessionKey
- def KXKEY(flags, sessionBaseKey, lmChallengeResponse, serverChallenge, password, lmhash, nthash, use_ntlmv2 = USE_NTLMv2):
- if use_ntlmv2:
- return sessionBaseKey
- if flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
- if flags & NTLMSSP_NEGOTIATE_NTLM:
- keyExchangeKey = hmac_md5(sessionBaseKey, serverChallenge + lmChallengeResponse[:8])
- else:
- keyExchangeKey = sessionBaseKey
- elif flags & NTLMSSP_NEGOTIATE_NTLM:
- if flags & NTLMSSP_NEGOTIATE_LM_KEY:
- keyExchangeKey = __DES_block(LMOWFv1(password,lmhash)[:7], lmChallengeResponse[:8]) + __DES_block(LMOWFv1(password,lmhash)[7] + '\xBD\xBD\xBD\xBD\xBD\xBD', lmChallengeResponse[:8])
- elif flags & NTLMSSP_REQUEST_NON_NT_SESSION_KEY:
- keyExchangeKey = LMOWFv1(password,lmhash)[:8] + '\x00'*8
- else:
- keyExchangeKey = sessionBaseKey
- else:
- raise "Can't create a valid KXKEY!"
- return keyExchangeKey
-
- def hmac_md5(key, data):
- if POW:
- h = POW.Hmac(POW.MD5_DIGEST, key)
- h.update(data)
- result = h.mac()
- else:
- import hmac
- h = hmac.new(key)
- h.update(data)
- result = h.digest()
- return result
- def NTOWFv2( user, password, domain, hash = ''):
- if hash != '':
- theHash = hash
- else:
- theHash = compute_nthash(password)
- return hmac_md5(theHash, user.upper().encode('utf-16le') + domain.encode('utf-16le'))
- def LMOWFv2( user, password, domain, lmhash = ''):
- return NTOWFv2( user, password, domain, lmhash)
- def computeResponseNTLMv2(flags, serverChallenge, clientChallenge, serverName, domain, user, password, lmhash = '', nthash = '', use_ntlmv2 = USE_NTLMv2):
- responseServerVersion = '\x01'
- hiResponseServerVersion = '\x01'
- responseKeyNT = NTOWFv2(user, password, domain, nthash)
- responseKeyLM = LMOWFv2(user, password, domain, lmhash)
- # 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!
- ######################
- av_pairs = AV_PAIRS(serverName)
- # In order to support SPN target name validation, we have to add this to the serverName av_pairs. Otherwise we will get access denied
- # This is set at Local Security Policy -> Local Policies -> Security Options -> Server SPN target name validation level
- av_pairs[NTLMSSP_AV_TARGET_NAME] = 'cifs/'.encode('utf-16le') + av_pairs[NTLMSSP_AV_HOSTNAME][1]
- if av_pairs[NTLMSSP_AV_TIME] is not None:
- aTime = av_pairs[NTLMSSP_AV_TIME][1]
- else:
- aTime = struct.pack('<q', (116444736000000000 + calendar.timegm(time.gmtime()) * 10000000) )
- #aTime = '\x00'*8
- av_pairs[NTLMSSP_AV_TIME] = aTime
- serverName = av_pairs.getData()
-
- ######################
- #aTime = '\x00'*8
- ######################
- temp = responseServerVersion + hiResponseServerVersion + '\x00' * 6 + aTime + clientChallenge + '\x00' * 4 + serverName + '\x00' * 4
- ntProofStr = hmac_md5(responseKeyNT, serverChallenge + temp)
- ntChallengeResponse = ntProofStr + temp
- lmChallengeResponse = hmac_md5(responseKeyNT, serverChallenge + clientChallenge) + clientChallenge
- sessionBaseKey = hmac_md5(responseKeyNT, ntProofStr)
- if (user == '' and password == ''):
- # Special case for anonymous authentication
- ntChallengeResponse = ''
- lmChallengeResponse = ''
- return ntChallengeResponse, lmChallengeResponse, sessionBaseKey
- class NTLM_HTTP(object):
- '''Parent class for NTLM HTTP classes.'''
- MSG_TYPE = None
- @classmethod
- def get_instace(cls,msg_64):
- msg = None
- msg_type = 0
- if msg_64 != '':
- msg = base64.b64decode(msg_64[5:]) # Remove the 'NTLM '
- msg_type = ord(msg[8])
-
- for _cls in NTLM_HTTP.__subclasses__():
- if msg_type == _cls.MSG_TYPE:
- instance = _cls()
- instance.fromString(msg)
- return instance
-
- class NTLM_HTTP_AuthRequired(NTLM_HTTP):
- commonHdr = ()
- # Message 0 means the first HTTP request e.g. 'GET /bla.png'
- MSG_TYPE = 0
- def fromString(self,data):
- pass
- class NTLM_HTTP_AuthNegotiate(NTLM_HTTP, NTLMAuthNegotiate):
- commonHdr = ()
- MSG_TYPE = 1
- def __init__(self):
- NTLMAuthNegotiate.__init__(self)
- class NTLM_HTTP_AuthChallengeResponse(NTLM_HTTP, NTLMAuthChallengeResponse):
- commonHdr = ()
- MSG_TYPE = 3
- def __init__(self):
- NTLMAuthChallengeResponse.__init__(self)
|