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