-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
2,504 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
# This file is part of 'NTLM Authorization Proxy Server' http://sourceforge.net/projects/ntlmaps/ | ||
# Copyright 2001 Dmitry A. Rozmanov <[email protected]> | ||
# | ||
# This library is free software: you can redistribute it and/or | ||
# modify it under the terms of the GNU Lesser General Public | ||
# License as published by the Free Software Foundation, either | ||
# version 3 of the License, or (at your option) any later version. | ||
|
||
# This library is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
# Lesser General Public License for more details. | ||
|
||
# You should have received a copy of the GNU Lesser General Public | ||
# License along with this library. If not, see <http://www.gnu.org/licenses/> or <http://www.gnu.org/licenses/lgpl.txt>. | ||
|
||
from __future__ import division | ||
import six | ||
|
||
C = 0x1000000000 | ||
|
||
|
||
def norm(n): | ||
return n & 0xFFFFFFFF | ||
|
||
|
||
class U32: | ||
v = 0 | ||
|
||
def __init__(self, value=0): | ||
if not isinstance(value, six.integer_types): | ||
value = six.byte2int(value) | ||
|
||
self.v = C + norm(abs(int(value))) | ||
|
||
def set(self, value=0): | ||
self.v = C + norm(abs(int(value))) | ||
|
||
def __repr__(self): | ||
return hex(norm(self.v)) | ||
|
||
def __long__(self): | ||
return int(norm(self.v)) | ||
|
||
def __int__(self): | ||
return int(norm(self.v)) | ||
|
||
def __chr__(self): | ||
return chr(norm(self.v)) | ||
|
||
def __add__(self, b): | ||
r = U32() | ||
r.v = C + norm(self.v + b.v) | ||
return r | ||
|
||
def __sub__(self, b): | ||
r = U32() | ||
if self.v < b.v: | ||
r.v = C + norm(0x100000000 - (b.v - self.v)) | ||
else: | ||
r.v = C + norm(self.v - b.v) | ||
return r | ||
|
||
def __mul__(self, b): | ||
r = U32() | ||
r.v = C + norm(self.v * b.v) | ||
return r | ||
|
||
def __div__(self, b): | ||
r = U32() | ||
r.v = C + (norm(self.v) // norm(b.v)) | ||
return r | ||
|
||
def __truediv__(self, b): | ||
r = U32() | ||
r.v = C + (norm(self.v) / norm(b.v)) | ||
return r | ||
|
||
def __mod__(self, b): | ||
r = U32() | ||
r.v = C + (norm(self.v) % norm(b.v)) | ||
return r | ||
|
||
def __neg__(self): | ||
return U32(self.v) | ||
|
||
def __pos__(self): | ||
return U32(self.v) | ||
|
||
def __abs__(self): | ||
return U32(self.v) | ||
|
||
def __invert__(self): | ||
r = U32() | ||
r.v = C + norm(~self.v) | ||
return r | ||
|
||
def __lshift__(self, b): | ||
r = U32() | ||
r.v = C + norm(self.v << b) | ||
return r | ||
|
||
def __rshift__(self, b): | ||
r = U32() | ||
r.v = C + (norm(self.v) >> b) | ||
return r | ||
|
||
def __and__(self, b): | ||
r = U32() | ||
r.v = C + norm(self.v & b.v) | ||
return r | ||
|
||
def __or__(self, b): | ||
r = U32() | ||
r.v = C + norm(self.v | b.v) | ||
return r | ||
|
||
def __xor__(self, b): | ||
r = U32() | ||
r.v = C + norm(self.v ^ b.v) | ||
return r | ||
|
||
def __not__(self): | ||
return U32(not norm(self.v)) | ||
|
||
def truth(self): | ||
return norm(self.v) | ||
|
||
def __cmp__(self, b): | ||
if norm(self.v) > norm(b.v): | ||
return 1 | ||
elif norm(self.v) < norm(b.v): | ||
return -1 | ||
else: | ||
return 0 | ||
|
||
def __lt__(self, other): | ||
return self.v < other.v | ||
|
||
def __gt__(self, other): | ||
return self.v > other.v | ||
|
||
def __eq__(self, other): | ||
return self.v == other.v | ||
|
||
def __le__(self, other): | ||
return self.v <= other.v | ||
|
||
def __ge__(self, other): | ||
return self.v >= other.v | ||
|
||
def __ne__(self, other): | ||
return self.v != other.v | ||
|
||
def __nonzero__(self): | ||
return norm(self.v) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from . import ntlm, session_security | ||
|
||
__all__ = ('ntlm', 'session_security') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
# This library is free software: you can redistribute it and/or | ||
# modify it under the terms of the GNU Lesser General Public | ||
# License as published by the Free Software Foundation, either | ||
# version 3 of the License, or (at your option) any later version. | ||
|
||
# This library is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
# Lesser General Public License for more details. | ||
|
||
# You should have received a copy of the GNU Lesser General Public | ||
# License along with this library. If not, see <http://www.gnu.org/licenses/> or <http://www.gnu.org/licenses/lgpl.txt>. | ||
|
||
import binascii | ||
import hashlib | ||
import hmac | ||
import re | ||
from ntlm_auth import des | ||
|
||
|
||
def _lmowfv1(password, lmhash): | ||
""" | ||
[MS-NLMP] v28.0 2016-07-14 | ||
3.3.1 NTLM v1 Authentication | ||
Same function as LMOWFv1 in document to create a one way hash of the password. Only | ||
used in NTLMv1 auth without session security | ||
:param password: The password of the user we are trying to authenticate with | ||
:return res: A Lan Manager hash of the password supplied | ||
""" | ||
|
||
# fix the password length to 14 bytes | ||
if lmhash is not None: | ||
return lmhash.decode('hex') | ||
|
||
password = password.upper() | ||
lm_pw = password[0:14] | ||
|
||
# do hash | ||
magic_str = b"KGS!@#$%" # page 56 in [MS-NLMP v28.0] | ||
|
||
res = b'' | ||
dobj = des.DES(lm_pw[0:7]) | ||
res = res + dobj.encrypt(magic_str) | ||
|
||
dobj = des.DES(lm_pw[7:14]) | ||
res = res + dobj.encrypt(magic_str) | ||
return res | ||
|
||
def _ntowfv1(password, nthash): | ||
""" | ||
[MS-NLMP] v28.0 2016-07-14 | ||
3.3.1 NTLM v1 Authentication | ||
Same function as NTOWFv1 in document to create a one way hash of the password. Only | ||
used in NTLMv1 auth without session security | ||
:param password: The password of the user we are trying to authenticate with | ||
:return digest: An NT hash of the password supplied | ||
""" | ||
if nthash is not None: | ||
return nthash.decode('hex') | ||
|
||
digest = hashlib.new('md4', password.encode('utf-16le')).digest() | ||
return digest | ||
|
||
def _ntowfv2(user_name, password, nthash, domain_name): | ||
""" | ||
[MS-NLMP] v28.0 2016-07-14 | ||
3.3.2 NTLM v2 Authentication | ||
Same function as NTOWFv2 (and LMOWFv2) in document to create a one way hash of the password. | ||
This combines some extra security features over the v1 calculations used in NTLMv2 auth. | ||
:param user_name: The user name of the user we are trying to authenticate with | ||
:param password: The password of the user we are trying to authenticate with | ||
:param domain_name: The domain name of the user account we are authenticated with | ||
:return digest: An NT hash of the parameters supplied | ||
""" | ||
digest = _ntowfv1(password, nthash) | ||
digest = hmac.new(digest, (user_name.upper() + domain_name).encode('utf-16le')).digest() | ||
|
||
|
||
return digest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
# This library is free software: you can redistribute it and/or | ||
# modify it under the terms of the GNU Lesser General Public | ||
# License as published by the Free Software Foundation, either | ||
# version 3 of the License, or (at your option) any later version. | ||
|
||
# This library is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
# Lesser General Public License for more details. | ||
|
||
# You should have received a copy of the GNU Lesser General Public | ||
# License along with this library. If not, see <http://www.gnu.org/licenses/> or <http://www.gnu.org/licenses/lgpl.txt>. | ||
|
||
import binascii | ||
import hashlib | ||
import hmac | ||
from ntlm_auth import des | ||
from ntlm_auth.constants import NegotiateFlags | ||
|
||
def _get_exchange_key_ntlm_v1(negotiate_flags, session_base_key, server_challenge, lm_challenge_response, lm_hash): | ||
""" | ||
[MS-NLMP] v28.0 2016-07-14 | ||
4.3.5.1 KXKEY | ||
Calculates the Key Exchange Key for NTLMv1 authentication. Used for signing and sealing messages | ||
@param negotiate_flags: | ||
@param session_base_key: A session key calculated from the user password challenge | ||
@param server_challenge: A random 8-byte response generated by the server in the CHALLENGE_MESSAGE | ||
@param lm_challenge_response: The LmChallengeResponse value computed in ComputeResponse | ||
@param lm_hash: The LMOWF computed in Compute Response | ||
@return key_exchange_key: The Key Exchange Key (KXKEY) used to sign and seal messages and compute the ExportedSessionKey | ||
""" | ||
if negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY: | ||
key_exchange_key = hmac.new(session_base_key, server_challenge + lm_challenge_response[:8]).digest() | ||
elif negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_LM_KEY: | ||
des_handler = des.DES(lm_hash[:7]) | ||
first_des = des_handler.encrypt(lm_challenge_response[:8]) | ||
des_handler = des.DES(lm_hash[7:8] + binascii.unhexlify('bdbdbdbdbdbdbd')) | ||
second_des = des_handler.encrypt(lm_challenge_response[:8]) | ||
|
||
key_exchange_key = first_des + second_des | ||
elif negotiate_flags & NegotiateFlags.NTLMSSP_REQUEST_NON_NT_SESSION_KEY: | ||
key_exchange_key = lm_hash[:8] + b'\0' * 8 | ||
else: | ||
key_exchange_key = session_base_key | ||
|
||
return key_exchange_key | ||
|
||
def _get_exchange_key_ntlm_v2(session_base_key): | ||
""" | ||
[MS-NLMP] v28.0 2016-07-14 | ||
4.3.5.1 KXKEY | ||
Calculates the Key Exchange Key for NTLMv2 authentication. Used for signing and sealing messages. | ||
According to docs, 'If NTLM v2 is used, KeyExchangeKey MUST be set to the given 128-bit SessionBaseKey | ||
@param session_base_key: A session key calculated from the user password challenge | ||
@return key_exchange_key: The Key Exchange Key (KXKEY) used to sign and seal messages | ||
""" | ||
return session_base_key | ||
|
||
def get_sign_key(exported_session_key, magic_constant): | ||
""" | ||
3.4.5.2 SIGNKEY | ||
@param exported_session_key: A 128-bit session key used to derive signing and sealing keys | ||
@param magic_constant: A constant value set in the MS-NLMP documentation (constants.SignSealConstants) | ||
@return sign_key: Key used to sign messages | ||
""" | ||
|
||
sign_key = hashlib.md5(exported_session_key + magic_constant).digest() | ||
|
||
return sign_key | ||
|
||
def get_seal_key(negotiate_flags, exported_session_key, magic_constant): | ||
""" | ||
3.4.5.3. SEALKEY | ||
Main method to use to calculate the seal_key used to seal (encrypt) messages. This will determine | ||
the correct method below to use based on the compatibility flags set and should be called instead | ||
of the others | ||
@param exported_session_key: A 128-bit session key used to derive signing and sealing keys | ||
@param negotiate_flags: The negotiate_flags structure sent by the server | ||
@param magic_constant: A constant value set in the MS-NLMP documentation (constants.SignSealConstants) | ||
@return seal_key: Key used to seal messages | ||
""" | ||
|
||
if negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY: | ||
seal_key = _get_seal_key_ntlm2(negotiate_flags, exported_session_key, magic_constant) | ||
elif negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_LM_KEY: | ||
seal_key = _get_seal_key_ntlm1(negotiate_flags, exported_session_key) | ||
else: | ||
seal_key = exported_session_key | ||
|
||
return seal_key | ||
|
||
def _get_seal_key_ntlm1(negotiate_flags, exported_session_key): | ||
""" | ||
3.4.5.3 SEALKEY | ||
Calculates the seal_key used to seal (encrypt) messages. This for authentication where | ||
NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY has not been negotiated. Will weaken the keys | ||
if NTLMSSP_NEGOTIATE_56 is not negotiated it will default to the 40-bit key | ||
@param negotiate_flags: The negotiate_flags structure sent by the server | ||
@param exported_session_key: A 128-bit session key used to derive signing and sealing keys | ||
@return seal_key: Key used to seal messages | ||
""" | ||
if negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_56: | ||
seal_key = exported_session_key[:7] + binascii.unhexlify('a0') | ||
else: | ||
seal_key = exported_session_key[:5] + binascii.unhexlify('e538b0') | ||
|
||
return seal_key | ||
|
||
def _get_seal_key_ntlm2(negotiate_flags, exported_session_key, magic_constant): | ||
""" | ||
3.4.5.3 SEALKEY | ||
Calculates the seal_key used to seal (encrypt) messages. This for authentication where | ||
NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY has been negotiated. Will weaken the keys | ||
if NTLMSSP_NEGOTIATE_128 is not negotiated, will try NEGOTIATE_56 and then will default | ||
to the 40-bit key | ||
@param negotiate_flags: The negotiate_flags structure sent by the server | ||
@param exported_session_key: A 128-bit session key used to derive signing and sealing keys | ||
@param magic_constant: A constant value set in the MS-NLMP documentation (constants.SignSealConstants) | ||
@return seal_key: Key used to seal messages | ||
""" | ||
if negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_128: | ||
seal_key = exported_session_key | ||
elif negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_56: | ||
seal_key = exported_session_key[:7] | ||
else: | ||
seal_key = exported_session_key[:5] | ||
|
||
seal_key = hashlib.md5(seal_key + magic_constant).digest() | ||
|
||
return seal_key |
Oops, something went wrong.