Package pike :: Module ntlm
[hide private]
[frames] | no frames]

Source Code for Module pike.ntlm

  1  # 
  2  # Copyright (c) 2013, EMC Corporation 
  3  # All rights reserved. 
  4  # 
  5  # Redistribution and use in source and binary forms, with or without 
  6  # modification, are permitted provided that the following conditions are met: 
  7  # 
  8  # 1. Redistributions of source code must retain the above copyright notice, 
  9  # this list of conditions and the following disclaimer. 
 10  # 2. Redistributions in binary form must reproduce the above copyright notice, 
 11  # this list of conditions and the following disclaimer in the documentation 
 12  # and/or other materials provided with the distribution. 
 13  # 
 14  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 15  # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 16  # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 17  # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
 18  # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 19  # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 20  # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 21  # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 22  # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 23  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 24  # POSSIBILITY OF SUCH DAMAGE. 
 25  # 
 26  # Module Name: 
 27  # 
 28  #        ntlm.py 
 29  # 
 30  # Abstract: 
 31  # 
 32  #        Pure python NTLM authentication provider 
 33  # 
 34  # Authors: Masen Furer (masen.furer@emc.com) 
 35  # 
 36   
 37  import array 
 38  import random 
 39  import struct 
 40  from socket import gethostname 
 41  import Cryptodome.Cipher.DES as DES 
 42  import Cryptodome.Cipher.ARC4 as RC4 
 43  import Cryptodome.Hash.HMAC as HMAC 
 44  import Cryptodome.Hash.MD4 as MD4 
 45  import Cryptodome.Hash.MD5 as MD5 
 46  import core 
 47  import model 
 48  import nttime 
 49   
50 -def des_key_64(K):
51 """ 52 Return an 64-bit des key by adding a zero to the least significant bit 53 of each character. 54 K should be a 7 char string 55 """ 56 in_key = K + "\0" 57 out_key = [K[0]] 58 for ix in xrange(1,len(in_key)): 59 out_key.append(chr( ((ord(in_key[ix-1]) << (8-ix)) & 0xFF) | (ord(in_key[ix]) >> ix)) ) 60 return "".join(out_key)
61
62 -def DES(K, D):
63 d1 = DES.new(des_key_64(K)) 64 return d1.encrypt(D)
65
66 -def DESL(K, D):
67 return DES(K[:7], D) + DES(K[7:14], D) + DES(K[14:16] + "\0"*5, D)
68
69 -def nonce(length):
70 return array.array("B", [random.getrandbits(8) for x in xrange(length) ])
71
72 -def encode_frame(frame):
73 buffer = array.array('B') 74 frame.encode(core.Cursor(buffer, 0)) 75 return buffer
76 77 78 NTLM_SIGNATURE = "NTLMSSP\x00" 79
80 -class MessageType(core.ValueEnum):
81 NtLmNegotiate = 0x1 82 NtLmChallenge = 0x2 83 NtLmAuthenticate = 0x3
84 85 MessageType.import_items(globals()) 86
87 -class Ntlm(core.Frame):
88
89 - def __init__(self, parent=None):
90 core.Frame.__init__(self, parent) 91 self.message = None
92
93 - def _encode(self, cur):
94 cur.encode_bytes(NTLM_SIGNATURE) 95 if self.message is not None: 96 self.message.encode(cur)
97
98 - def _decode(self, cur):
99 signature = cur.decode_bytes(8) 100 if signature.tostring() != NTLM_SIGNATURE: 101 raise core.BadPacket("Packet signature does not match") 102 # determine the message type to pass off decoding 103 message_type = cur.decode_uint32le() 104 if message_type == NtLmChallenge: 105 self.message = NtLmChallengeMessage(self) 106 self.message.decode(cur) 107 else: 108 raise core.BadPacket("Unknown response message type: {0}".format(message_type))
109
110 -class NegotiateFlags(core.FlagEnum):
111 NTLMSSP_NEGOTIATE_UNICODE = 0x00000001 112 NTLM_NEGOTIATE_OEM = 0x00000002 113 NTLMSSP_REQUEST_TARGET = 0x00000004 114 NTLM_NEGOTIATE_SIGN = 0x00000010 115 NTLM_NEGOTIATE_SEAL = 0x00000020 116 NTLMSSP_NEGOTIATE_DATAGRAM = 0x00000040 117 NTLMSSP_NEGOTIATE_LM_KEY = 0x00000080 118 NTLMSSP_NEGOTIATE_NTLM = 0x00000200 119 ANONYMOUS = 0x00000800 120 NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED = 0x00001000 121 NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x00002000 122 NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x00008000 123 NTLMSSP_TARGET_TYPE_DOMAIN = 0x00010000 124 NTLMSSP_TARGET_TYPE_SERVER = 0x00020000 125 NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000 126 NTLMSSP_NEGOTIATE_IDENTIFY = 0x00100000 127 NTLMSSP_REQUEST_NON_NT_SESSION_KEY = 0x00400000 128 NTLMSSP_NEGOTIATE_TARGET_INFO = 0x00800000 129 NTLMSSP_NEGOTIATE_VERSION = 0x02000000 130 NTLMSSP_NEGOTIATE_128 = 0x20000000 131 NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000 132 NTLMSSP_NEGOTIATE_56 = 0x80000000
133 134 NegotiateFlags.import_items(globals()) 135
136 -class ProductMajorVersionFlags(core.ValueEnum):
137 WINDOWS_MAJOR_VERSION_5 = 0x5 138 WINDOWS_MAJOR_VERSION_6 = 0x6 139 WINDOWS_MAJOR_VERSION_10 = 0xA
140 141 ProductMajorVersionFlags.import_items(globals()) 142
143 -class ProductMinorVersionFlags(core.ValueEnum):
144 WINDOWS_MINOR_VERSION_0 = 0x0 145 WINDOWS_MINOR_VERSION_1 = 0x1 146 WINDOWS_MINOR_VERSION_2 = 0x2 147 WINDOWS_MINOR_VERSION_3 = 0x3
148 149 ProductMinorVersionFlags.import_items(globals()) 150
151 -class NTLMRevisionCurrentFlags(core.ValueEnum):
152 UNKNOWN = 0x0 153 NTLMSSP_REVISION_W2K3 = 0x0F
154 155 NTLMRevisionCurrentFlags.import_items(globals()) 156
157 -class Version(core.Frame):
158 - def __init__(self, parent=None):
159 core.Frame.__init__(self, parent) 160 if parent is not None: 161 parent.version = self 162 self.product_major_version = WINDOWS_MAJOR_VERSION_5 163 self.product_minor_version = WINDOWS_MINOR_VERSION_0 164 self.product_build = 0 165 self.ntlm_revision_current = NTLMSSP_REVISION_W2K3
166 - def _encode(self, cur):
167 cur.encode_uint8le(self.product_major_version) 168 cur.encode_uint8le(self.product_minor_version) 169 cur.encode_uint16le(self.product_build) 170 cur.encode_uint16le(0) # reserved 171 cur.encode_uint8le(0) # reserved 172 cur.encode_uint8le(self.ntlm_revision_current)
173 - def _decode(self, cur):
174 self.product_major_version = ProductMajorVersionFlags(cur.decode_uint8le()) 175 self.product_minor_version = ProductMinorVersionFlags(cur.decode_uint8le()) 176 self.product_build = cur.decode_uint16le() 177 reserved = cur.decode_uint16le() 178 reserved = cur.decode_uint8le() 179 self.ntlm_revision_current = NTLMRevisionCurrentFlags(cur.decode_uint8le())
180
181 -class NtLmNegotiateMessage(core.Frame):
182 message_type = NtLmNegotiate
183 - def __init__(self, parent=None):
184 core.Frame.__init__(self, parent) 185 if parent is not None: 186 parent.message = self 187 self.negotiate_flags = 0 188 self.domain_name = "" 189 self.workstation_name = gethostname() 190 self.version = None
191
192 - def _encode(self, cur):
193 is_unicode = self.negotiate_flags & NTLMSSP_NEGOTIATE_UNICODE 194 195 message_start = cur - 8 196 cur.encode_uint32le(self.message_type) 197 cur.encode_uint32le(self.negotiate_flags) 198 199 if self.negotiate_flags & NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED: 200 domain_name_len = len(self.domain_name) 201 else: 202 domain_name_len = 0 203 if is_unicode: 204 domain_name_len = domain_name_len * 2 205 cur.encode_uint16le(domain_name_len) 206 cur.encode_uint16le(domain_name_len) 207 domain_name_offset_hole = cur.hole.encode_uint32le(0) 208 209 if self.negotiate_flags & NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED: 210 workstation_name_len = len(self.workstation_name) 211 else: 212 workstation_name_len = 0 213 if is_unicode: 214 workstation_name_len = workstation_name_len * 2 215 cur.encode_uint16le(workstation_name_len) 216 cur.encode_uint16le(workstation_name_len) 217 workstation_name_offset_hole = cur.hole.encode_uint32le(0) 218 219 if self.negotiate_flags & NTLMSSP_NEGOTIATE_VERSION and \ 220 self.version is not None: 221 self.version.encode(cur) 222 else: 223 cur.encode_uint64le(0) 224 225 domain_name_offset_hole(cur - message_start) 226 if domain_name_len > 0: 227 if is_unicode: 228 cur.encode_utf16le(self.domain_name) 229 else: 230 cur.encode_bytes(self.domain_name) 231 232 workstation_name_offset_hole(cur - message_start) 233 if workstation_name_len > 0: 234 if is_unicode: 235 cur.encode_utf16le(self.workstation_name) 236 else: 237 cur.encode_bytes(self.workstation_name)
238
239 -class AvId(core.ValueEnum):
240 MsvAvEOL = 0x0 241 MsvAvNbComputerName = 0x1 242 MsvAvNbDomainName = 0x2 243 MsvAvDnsComputerName = 0x3 244 MsvAvDnsDomainName = 0x4 245 MsvAvDnsTreeName = 0x5 246 MsvAvFlags = 0x6 247 MsvAvTimestamp = 0x7 248 MsvAvSingleHost = 0x8 249 MsvAvTargetName = 0x9 250 MsvChannelBindings = 0xA
251 252 AvId.import_items(globals()) 253
254 -class AvPair(core.Frame):
255 text_fields = [ MsvAvNbComputerName, MsvAvNbDomainName, 256 MsvAvDnsComputerName, MsvAvDnsDomainName, MsvAvDnsTreeName, 257 MsvAvTargetName ]
258 - def __init__(self, parent=None):
259 core.Frame.__init__(self, parent) 260 if parent is not None: 261 parent.target_info.append(self) 262 self.av_id = None 263 self.value = None
264
265 - def _encode(self, cur):
266 cur.encode_uint16le(self.av_id) 267 av_len_hole = cur.hole.encode_uint16le(0) 268 av_start = cur.copy() 269 if self.value is not None: 270 if self.av_id in self.text_fields: 271 cur.encode_utf16le(self.value) 272 else: 273 cur.encode_bytes(self.value) 274 av_len_hole(cur - av_start)
275
276 - def _decode(self, cur):
277 self.av_id = AvId(cur.decode_uint16le()) 278 av_len = cur.decode_uint16le() 279 if av_len > 0: 280 if self.av_id in self.text_fields: 281 self.value = cur.decode_utf16le(av_len) 282 else: 283 self.value = cur.decode_bytes(av_len)
284
285 -def extract_pair(av_pairs, av_id):
286 for p in av_pairs: 287 if p.av_id == av_id: 288 return p
289
290 -class NtLmChallengeMessage(core.Frame):
291 message_type = NtLmChallenge
292 - def __init__(self, parent=None):
293 core.Frame.__init__(self, parent) 294 if parent is not None: 295 parent.message = self 296 self.target_name = None 297 self.negotiate_flags = 0 298 self.server_challenge = array.array('B', [0]*8) 299 self.target_info = [] 300 self.version = None
301 - def _decode(self, cur):
302 message_start = cur.copy() - 12 303 target_name_len = cur.decode_uint16le() 304 target_name_max_len = cur.decode_uint16le() 305 target_name_offset = cur.decode_uint32le() 306 self.negotiate_flags = NegotiateFlags(cur.decode_uint32le()) 307 self.server_challenge = cur.decode_bytes(8) 308 reserved = cur.decode_bytes(8) 309 target_info_len = cur.decode_uint16le() 310 target_info_max_len = cur.decode_uint16le() 311 target_info_offset = cur.decode_uint32le() 312 if self.negotiate_flags & NTLMSSP_NEGOTIATE_VERSION: 313 with cur.bounded(cur, cur+8): 314 self.version = Version(self) 315 self.version.decode(cur) 316 else: 317 cur.decode_uint64le() 318 319 is_unicode = self.negotiate_flags & NTLMSSP_NEGOTIATE_UNICODE 320 321 cur.seekto(message_start + target_name_offset) 322 if target_name_len > 0: 323 if is_unicode: 324 self.target_name = cur.decode_utf16le(target_name_len) 325 else: 326 self.target_name = cur.decode_bytes(target_name_len) 327 328 cur.seekto(message_start + target_info_offset) 329 end = cur + target_info_len 330 if target_info_len > 0: 331 while cur < end: 332 this_av = AvPair(self) 333 with cur.bounded(cur,end): 334 this_av.decode(cur) 335 if this_av.av_id == MsvAvEOL: 336 break
337
338 -class NtLmAuthenticateMessage(core.Frame):
339 message_type = NtLmAuthenticate
340 - def __init__(self, parent=None):
341 core.Frame.__init__(self, parent) 342 if parent is not None: 343 parent.message = self 344 self.lm_challenge_response = None 345 self.nt_challenge_response = None 346 self.domain_name = None 347 self.user_name = None 348 self.workstation_name = gethostname() 349 self.encrypted_random_session_key = None 350 self.negotiate_flags = None 351 self.version = None 352 self.mic = None
353
354 - def _encode(self, cur):
355 is_unicode = self.negotiate_flags & NTLMSSP_NEGOTIATE_UNICODE 356 357 message_start = cur - 8 358 cur.encode_uint32le(self.message_type) 359 360 if self.lm_challenge_response is not None: 361 lm_challenge_response_len = len(self.lm_challenge_response) 362 else: 363 lm_challenge_response_len = 0 364 cur.encode_uint16le(lm_challenge_response_len) 365 cur.encode_uint16le(lm_challenge_response_len) 366 lm_challenge_response_offset_hole = cur.hole.encode_uint32le(0) 367 368 if self.nt_challenge_response is not None: 369 nt_challenge_response_len = len(self.nt_challenge_response) 370 else: 371 nt_challenge_response_len = 0 372 cur.encode_uint16le(nt_challenge_response_len) 373 cur.encode_uint16le(nt_challenge_response_len) 374 nt_challenge_response_offset_hole = cur.hole.encode_uint32le(0) 375 376 if self.domain_name is not None: 377 domain_name_len = len(self.domain_name) 378 else: 379 domain_name_len = 0 380 if is_unicode: 381 domain_name_len = domain_name_len * 2 382 cur.encode_uint16le(domain_name_len) 383 cur.encode_uint16le(domain_name_len) 384 domain_name_offset_hole = cur.hole.encode_uint32le(0) 385 386 if self.user_name is not None: 387 user_name_len = len(self.user_name) 388 else: 389 user_name_len = 0 390 if is_unicode: 391 user_name_len = user_name_len * 2 392 cur.encode_uint16le(user_name_len) 393 cur.encode_uint16le(user_name_len) 394 user_name_offset_hole = cur.hole.encode_uint32le(0) 395 396 if self.workstation_name is not None: 397 workstation_name_len = len(self.workstation_name) 398 else: 399 workstation_name_len = 0 400 if is_unicode: 401 workstation_name_len = workstation_name_len * 2 402 cur.encode_uint16le(workstation_name_len) 403 cur.encode_uint16le(workstation_name_len) 404 workstation_name_offset_hole = cur.hole.encode_uint32le(0) 405 406 if self.negotiate_flags & NTLMSSP_NEGOTIATE_KEY_EXCH and \ 407 self.encrypted_random_session_key is not None: 408 encrypted_random_session_key_len = len(self.encrypted_random_session_key) 409 else: 410 encrypted_random_session_key_len = 0 411 cur.encode_uint16le(encrypted_random_session_key_len) 412 cur.encode_uint16le(encrypted_random_session_key_len) 413 encrypted_random_session_key_offset_hole = cur.hole.encode_uint32le(0) 414 415 cur.encode_uint32le(self.negotiate_flags) 416 417 if self.negotiate_flags & NTLMSSP_NEGOTIATE_VERSION and \ 418 self.version is not None: 419 self.version.encode(cur) 420 else: 421 cur.encode_uint64le(0) 422 423 if self.mic is not None: 424 cur.encode_bytes(self.mic[:16]) 425 426 lm_challenge_response_offset_hole(cur - message_start) 427 if lm_challenge_response_len > 0: 428 cur.encode_bytes(self.lm_challenge_response) 429 430 nt_challenge_response_offset_hole(cur - message_start) 431 if nt_challenge_response_len > 0: 432 cur.encode_bytes(self.nt_challenge_response) 433 434 domain_name_offset_hole(cur - message_start) 435 if domain_name_len > 0: 436 if is_unicode: 437 cur.encode_utf16le(self.domain_name) 438 else: 439 cur.encode_bytes(self.domain_name) 440 441 user_name_offset_hole(cur - message_start) 442 if user_name_len > 0: 443 if is_unicode: 444 cur.encode_utf16le(self.user_name) 445 else: 446 cur.encode_bytes(self.user_name) 447 448 workstation_name_offset_hole(cur - message_start) 449 if workstation_name_len > 0: 450 if is_unicode: 451 cur.encode_utf16le(self.workstation_name) 452 else: 453 cur.encode_bytes(self.workstation_name) 454 455 encrypted_random_session_key_offset_hole(cur - message_start) 456 if encrypted_random_session_key_len > 0: 457 cur.encode_bytes(self.encrypted_random_session_key)
458
459 -class NtlmVersion(core.ValueEnum):
460 NTLMv1 = 0x1 461 NTLMv2 = 0x2
462 463 NtlmVersion.import_items(globals()) 464 465 ######################## 466 ## NTLMv1 Implementation 467 ######################## 468
469 -def LMOWFv1(password):
470 lm_passwd = (password.upper() + "\0"*14) 471 magic = "KGS!@#$%" 472 return DES(lm_passwd[:7], magic) + \ 473 DES(lm_passwd[7:14], magic)
474
475 -def NTOWFv1(password):
476 nt_passwd = password.encode("utf-16-le") 477 return MD4.new(nt_passwd).digest()
478
479 -def ComputeResponsev1(NegFlg, ResponseKeyNT, ResponseKeyLM, ServerChallenge, 480 ClientChallenge, Time=None, ServerName=None):
481 if NegFlg & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY: 482 NtChallengeResponse = DESL(ResponseKeyNT, 483 MD5.new(ServerChallenge + \ 484 ClientChallenge).digest()[:8]) 485 LmChallengeResponse = ClientChallenge + "\0"*16 486 else: 487 NtChallengeResponse = DESL(ResponseKeyNT, ServerChallenge) 488 LmChallengeResponse = DESL(ResponseKeyLM, ServerChallenge) 489 SessionBaseKey = MD4.new(ResponseKeyNT).digest() 490 return NtChallengeResponse, LmChallengeResponse, SessionBaseKey
491
492 -def KXKEY(NegFlg, SessionBaseKey, LmChallengeResponse, 493 ServerChallenge, ResponseKeyLM):
494 if NegFlg & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY: 495 hm = HMAC.new(SessionBaseKey, 496 ServerChallenge +\ 497 LmChallengeResponse[:8], 498 MD5) 499 KeyExchangeKey = hm.digest() 500 else: 501 LMOWF = ResponseKeyLM 502 if NegFlg & NTLMSSP_NEGOTIATE_LMKEY: 503 data = LmChallengeResponse[:8] 504 KeyExchangeKey = DES(LMOWF[:7], data) + \ 505 DES(LMOWF[8] + "\xbd"*6, data) 506 else: 507 if NegFlg & NTLMSSP_REQUEST_NON_NT_SESSION_KEY: 508 KeyExchangeKey = LMOWF[:8] + "\0"*8 509 else: 510 KeyExchangeKey = SessionBaseKey 511 return KeyExchangeKey
512 513 ######################## 514 ## NTLMv2 Implementation 515 ######################## 516
517 -class NTLMv2Response(core.Frame):
518 - def __init__(self, parent=None):
519 core.Frame.__init__(self, parent) 520 self.response = None 521 self.challenge = None
522 - def _encode(self, cur):
523 cur.encode_bytes(self.response) 524 self.challenge.encode(cur)
525
526 -class NTLMv2ClientChallenge(core.Frame):
527 - def __init__(self, parent=None):
528 core.Frame.__init__(self, parent) 529 if parent is not None: 530 parent.challenge = self 531 self.time_stamp = array.array("B", "\0"*8) 532 self.challenge_from_client = array.array("B", "\0"*8) 533 self.av_pairs = []
534 - def _encode(self, cur):
535 cur.encode_uint8le(1) # RespType 536 cur.encode_uint8le(1) # HiRespType 537 cur.encode_uint16le(0) # Reserved 538 cur.encode_uint32le(0) # Reserved 539 cur.encode_uint64le(self.time_stamp) 540 cur.encode_bytes(self.challenge_from_client) 541 cur.encode_uint32le(0) # Reserved 542 for p in self.av_pairs: 543 p.encode(cur)
544
545 -def NTOWFv2(password, user, userdom):
546 nt_passwd = password.encode("utf-16-le") 547 nt_hash_passwd = MD4.new(nt_passwd).digest() 548 nt_userdom = (user.upper() + userdom).encode("utf-16-le") 549 return HMAC.new(nt_hash_passwd, 550 nt_userdom, 551 MD5).digest()
552
553 -def ComputeResponsev2(NegFlg, ResponseKeyNT, ResponseKeyLM, ServerChallenge, 554 ClientChallenge, Time=None, ServerName=None, av_pairs=None):
555 if Time is None: 556 Time = nttime.NtTime(nttime.datetime.now()) 557 if ServerName is None: 558 ServerName = "SERVER" 559 ServerName = ServerName.encode("utf-16-le") 560 561 TimeBuf = array.array("B") 562 cur = core.Cursor(TimeBuf,0) 563 cur.encode_uint64le(Time) 564 565 Responseversion = "\x01" 566 HiResponseversion = "\x01" 567 568 ntlmv2_client_challenge = NTLMv2ClientChallenge() 569 ntlmv2_client_challenge.time_stamp = Time 570 ntlmv2_client_challenge.challenge_from_client = ClientChallenge 571 if av_pairs is not None: 572 ntlmv2_client_challenge.av_pairs = av_pairs 573 temp = encode_frame(ntlmv2_client_challenge).tostring() 574 NTProofStr = HMAC.new(ResponseKeyNT, 575 ServerChallenge + temp, 576 MD5).digest() 577 NtChallengeResponse = NTProofStr + temp 578 LmChallengeResponse = HMAC.new(ResponseKeyLM, 579 ServerChallenge + ClientChallenge, 580 MD5).digest() +\ 581 ClientChallenge 582 SessionBaseKey = HMAC.new(ResponseKeyNT, NTProofStr, MD5).digest() 583 return NtChallengeResponse, LmChallengeResponse, SessionBaseKey
584
585 -class NtlmAuthenticator(object):
586 """ 587 State machine for conducting ntlm authentication 588 """ 589 neg_flags = NTLMSSP_NEGOTIATE_UNICODE |\ 590 NTLM_NEGOTIATE_OEM |\ 591 NTLMSSP_REQUEST_TARGET |\ 592 NTLMSSP_NEGOTIATE_NTLM |\ 593 NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED |\ 594 NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY |\ 595 NTLM_NEGOTIATE_SIGN |\ 596 NTLM_NEGOTIATE_SEAL |\ 597 NTLMSSP_NEGOTIATE_128 |\ 598 NTLMSSP_NEGOTIATE_56 |\ 599 NTLMSSP_NEGOTIATE_KEY_EXCH 600 601 auth_flags = NTLMSSP_NEGOTIATE_UNICODE |\ 602 NTLMSSP_REQUEST_TARGET |\ 603 NTLMSSP_NEGOTIATE_NTLM |\ 604 NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY |\ 605 NTLM_NEGOTIATE_SIGN |\ 606 NTLM_NEGOTIATE_SEAL |\ 607 NTLMSSP_NEGOTIATE_TARGET_INFO |\ 608 NTLMSSP_NEGOTIATE_128 |\ 609 NTLMSSP_NEGOTIATE_56 |\ 610 NTLMSSP_TARGET_TYPE_DOMAIN |\ 611 NTLMSSP_NEGOTIATE_KEY_EXCH 612
613 - def __init__(self, domain, username, password):
614 self.messages = [] 615 self.domain = domain 616 self.username = username 617 self.password = password 618 self.version = Version() 619 self.version.product_build = 9999 620 self.ntlm_version = NTLMv2 621 622 self.client_challenge = nonce(8) 623 self.session_base_key = nonce(16) 624 625 self.negotiate_message = None 626 self.negotiate_buffer = None 627 self.challenge_message = None 628 self.challenge_buffer = None 629 self.authenticate_message = None 630 self.authenticate_buffer = None
631
632 - def ntlmv1(self):
633 self.lm_hash = LMOWFv1(self.password) 634 self.nt_hash = NTOWFv1(self.password) 635 (self.nt_challenge_response, 636 self.lm_challenge_response, 637 self.session_base_key) = ComputeResponsev1(self.auth_flags, 638 self.nt_hash, 639 self.lm_hash, 640 self.server_challenge.tostring(), 641 self.client_challenge.tostring()) 642 self.key_exchange_key = KXKEY(self.auth_flags, 643 self.session_base_key, 644 self.lm_challenge_response, 645 self.server_challenge.tostring(), 646 self.lm_hash)
647
648 - def ntlmv2(self):
649 ctarget_info = self.challenge_message.message.target_info 650 server_time = extract_pair(ctarget_info, MsvAvTimestamp) 651 if server_time is not None: 652 time = nttime.NtTime(struct.unpack("<Q", server_time.value)[0]) 653 else: 654 time = nttime.NtTime(nttime.datetime.now()) 655 server_name = extract_pair(ctarget_info, 656 MsvAvNbComputerName) 657 if server_name is not None: 658 server_name = server_name.value 659 660 self.nt_hash = NTOWFv2(self.password, self.username, self.domain) 661 (self.nt_challenge_response, 662 self.lm_challenge_response, 663 self.session_base_key) = ComputeResponsev2(self.auth_flags, 664 self.nt_hash, 665 self.nt_hash, 666 self.server_challenge.tostring(), 667 self.client_challenge.tostring(), 668 time, 669 server_name, 670 ctarget_info) 671 self.key_exchange_key = self.session_base_key 672 673 if extract_pair(ctarget_info, MsvAvTimestamp) is not None: 674 self.lm_challenge_response = "\0"*24
675
676 - def session_key(self):
677 if self.auth_flags & NTLMSSP_NEGOTIATE_KEY_EXCH: 678 r = RC4.new(self.key_exchange_key) 679 self.exported_session_key = nonce(16) 680 return r.encrypt(self.exported_session_key.tostring()) 681 else: 682 self.exported_session_key = self.key_exchange_key
683
684 - def negotiate(self):
685 ntlm = Ntlm() 686 neg = NtLmNegotiateMessage(ntlm) 687 neg.negotiate_flags = self.neg_flags 688 neg.domain_name = self.domain 689 neg.version = self.version 690 self.negotiate_buffer = array.array('B') 691 ntlm.encode(core.Cursor(self.negotiate_buffer, 0)) 692 self.negotiate_message = ntlm 693 return self.negotiate_buffer
694
695 - def authenticate(self, challenge_buf):
696 # parse the challenge message 697 self.challenge_buffer = challenge_buf 698 ntlm_challenge = Ntlm() 699 ntlm_challenge.decode(core.Cursor(challenge_buf, 0)) 700 self.server_challenge = ntlm_challenge.message.server_challenge 701 self.challenge_message = ntlm_challenge 702 703 # build the auth message 704 ntlm = Ntlm() 705 auth = NtLmAuthenticateMessage(ntlm) 706 auth.negotiate_flags = self.auth_flags 707 auth.version = self.version 708 auth.domain_name = self.domain 709 auth.user_name = self.username 710 711 # perform the NTLM 712 if self.ntlm_version == NTLMv1: 713 self.ntlmv1() 714 elif self.ntlm_version == NTLMv2: 715 self.ntlmv2() 716 else: 717 raise NotImplementedError("Unknown NTLM version {0}".format(self.ntlm_version)) 718 719 # fill in the challenge responses 720 auth.nt_challenge_response = self.nt_challenge_response 721 auth.lm_challenge_response = self.lm_challenge_response 722 723 # generate the session key if requested 724 session_key = self.session_key() 725 if session_key is not None: 726 auth.encrypted_random_session_key = session_key 727 728 self.authenticate_buffer = array.array('B') 729 ntlm.encode(core.Cursor(self.authenticate_buffer, 0)) 730 self.authenticate_message = ntlm 731 return self.authenticate_buffer
732