From b2f40a1f422dfd70d9bbbce8889030febdc37daa Mon Sep 17 00:00:00 2001 From: artkond Date: Thu, 16 Mar 2017 19:56:58 +0300 Subject: [PATCH] new repo --- .gitignore | 2 + README.md | 59 ++ client.py | 497 +++++++++++++++++ ntlm_auth/U32.py | 156 ++++++ ntlm_auth/__init__.py | 3 + ntlm_auth/compute_hash.py | 85 +++ ntlm_auth/compute_keys.py | 138 +++++ ntlm_auth/compute_response.py | 399 ++++++++++++++ ntlm_auth/constants.py | 92 ++++ ntlm_auth/des.py | 88 +++ ntlm_auth/des_c.py | 254 +++++++++ ntlm_auth/des_data.py | 348 ++++++++++++ ntlm_auth/gss_channel_bindings.py | 67 +++ ntlm_auth/messages.py | 359 ++++++++++++ ntlm_auth/ntlm.py | 146 +++++ ntlm_auth/rc4.py | 51 ++ ntlm_auth/session_security.py | 250 +++++++++ ntlm_auth/target_info.py | 68 +++ ordereddict.py | 127 +++++ relay.py | 56 ++ server.py | 425 +++++++++++++++ six.py | 868 ++++++++++++++++++++++++++++++ 22 files changed, 4538 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 client.py create mode 100644 ntlm_auth/U32.py create mode 100644 ntlm_auth/__init__.py create mode 100644 ntlm_auth/compute_hash.py create mode 100644 ntlm_auth/compute_keys.py create mode 100644 ntlm_auth/compute_response.py create mode 100644 ntlm_auth/constants.py create mode 100644 ntlm_auth/des.py create mode 100644 ntlm_auth/des_c.py create mode 100644 ntlm_auth/des_data.py create mode 100644 ntlm_auth/gss_channel_bindings.py create mode 100644 ntlm_auth/messages.py create mode 100644 ntlm_auth/ntlm.py create mode 100644 ntlm_auth/rc4.py create mode 100644 ntlm_auth/session_security.py create mode 100644 ntlm_auth/target_info.py create mode 100644 ordereddict.py create mode 100644 relay.py create mode 100644 server.py create mode 100644 six.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..92260b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +venv +build diff --git a/README.md b/README.md new file mode 100644 index 0000000..9df8fbb --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +RPIVOT - reverse socks 4 proxy for penetration tests +=================== + + +RPIVOT allows to tunnel traffic into internal network via socks 4. It works like ssh dynamic port forwarding but in the opposite direction. + + +---------- + + +Description +------------- + +This tool is Python 2.6-2.7 compatible and has no dependencies beyond the standard library. It has client-server architecture. Just run the client on the machine you want to tunnel the traffic through. Server should be started on pentester's machine and listen to incoming connections from the client. + +Works on Kali Linux, Solaris 10, Windows, Mac OS. + + +Usage example +------------- + +Start server listener on port 9999, which creates a socks 4 proxy on 127.0.0.1:1080 upon connection from client: + +`python server.py --server-port 9999 --server-ip 0.0.0.0 --proxy-ip 127.0.0.1 --proxy-port 1080` + +Connect to the server: + +`python client.py --server-ip --server-port 9999` + +To pivot through an NTLM proxy: + +`python client.py --server-ip --server-port 9999 --ntlm-proxy-ip --ntlm-proxy-port 8080 --domain CONTOSO.COM --username Alice --password P@ssw0rd` + +Pass-the-hash is supported: + +`python client.py --server-ip --server-port 9999 --ntlm-proxy-ip --ntlm-proxy-port 8080 --domain CONTOSO.COM --username Alice --hashes 9b9850751be2515c8231e5189015bbe6:49ef7638d69a01f26d96ed673bf50c45` + +You can use `proxychains` to tunnel traffic through socks proxy. + +Edit /etc/proxychains.conf: + +``` +[ProxyList] +# add proxy here ... +# meanwile +# defaults set to "tor" +socks4 127.0.0.1 1080 +``` + +Pivot and have fun: + +`proxychains ` + +Pre-built Windows client binary available at the `dist` folder + +Author +------ + +Artem Kondratenko https://twitter.com/artkond diff --git a/client.py b/client.py new file mode 100644 index 0000000..b68dd2d --- /dev/null +++ b/client.py @@ -0,0 +1,497 @@ +import logging +import logging.handlers +import socket +import sys +import time +from struct import pack, unpack +import select +import optparse +import errno +import relay +import threading + +from ntlm_auth.ntlm import Ntlm +import re + +logger = None + + +def key_by_value(my_dict, value): + for k, v in my_dict.iteritems(): + if v == value: + return k + return None + + +class SocksRelay: + STATUS_SUCCESS = 0 + STATUS_REFUSED = 1 + STATUS_TIMEOUT = 2 + + def __init__(self, bc_sock): + self.channel = {} + self.id_by_socket = {} + self.bc_sock = bc_sock + self.input_list = [self.bc_sock] + self.establishing_dict = {} + self.forward_socket = None + self.data = None + self.last_ping_time = time.time() + + logger.debug('Starting ping thread') + + + self.ping_thread = threading.Thread(target=self.ping_worker) + + self.ping_thread.start() + self.remote_side_down = False + + def ping_worker(self): + while True: + time.sleep(10) + current_time = time.time() + logger.debug('In ping worker') + if self.remote_side_down: + logger.debug('Remote side down. Exiting ping worker') + return + if current_time - self.last_ping_time > relay.relay_timeout: + logger.info('No response from remote side for {0} seconds. Restarting relay'.format(relay.relay_timeout)) + self.bc_sock.close() + return + + def shutdown(self): + self.remote_side_down = True + relay.close_sockets(self.input_list) + sys.exit(1) + + def run(self): + inputready = None + outputready = None + exceptready = None + while True: + + try: + time.sleep(relay.delay) + logger.debug("Active channels: {0}. Pending Channels {1}".format(self.channel.keys(), self.establishing_dict.values())) + inputready, outputready, exceptready = select.select(self.input_list, self.establishing_dict.keys(), [], 15) + except KeyboardInterrupt: + logger.info('SIGINT received. Closing relay and exiting') + self.send_remote_cmd(self.bc_sock, relay.CLOSE_RELAY) + self.shutdown() + except select.error as (code, msg): + logger.debug('Select error on select. Errno: {0} Msg: {1}'.format(errno.errorcode[code], msg)) + self.shutdown() + except socket.error as (code, msg): + logger.debug('Socket error on select. Errno: {0} Msg: {1}'.format(errno.errorcode[code], msg)) + self.shutdown() + + for sock in outputready: + channel_id = self.establishing_dict[sock] + logger.debug('Establishing connection with channel id {0}'.format(channel_id)) + try: + sock.recv(0) + except socket.error as (code, err_msg): + if code == errno.ECONNREFUSED or code == errno.ETIMEDOUT: + logger.debug('Connection {0}'.format(errno.errorcode[code])) + + if sock in inputready: + inputready.remove(sock) + + del self.establishing_dict[sock] + + self.send_remote_cmd(self.bc_sock, relay.FORWARD_CONNECTION_FAILURE, channel_id) + sock.close() + continue + elif code == errno.EAGAIN: + logger.debug('Recv(0) return errno.EAGAIN for socket {0} on channel {1}. Connection established.'.format(sock, channel_id)) + elif code == 10035: + logger.debug('Recv(0) raised windows-specific exception 10035. Connection established.') + else: + raise + + logger.debug('Connection established on channel {0}'.format(channel_id)) + sock.setblocking(1) + + self.send_remote_cmd(self.bc_sock, relay.FORWARD_CONNECTION_SUCCESS, self.establishing_dict[sock]) + del self.establishing_dict[sock] + self.input_list.append(sock) + self.set_channel(sock, channel_id) + + for self.selected_input_socket in inputready: + if self.selected_input_socket == self.bc_sock: + try: + self.manage_remote_socket(self.bc_sock) + except relay.RelayError: + logger.debug('Remote side closed socket') + relay.close_sockets(self.input_list) + return + else: + self.manage_forward_socket(self.selected_input_socket) + + def handle_remote_cmd(self, data): + cmd = data[0] + logger.debug('Received cmd data from remote side. Cmd: {0}'.format(relay.cmd_names[cmd])) + if cmd == relay.CHANNEL_CLOSE_CMD: + channel_id = unpack(' +# +# 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 or . + +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) diff --git a/ntlm_auth/__init__.py b/ntlm_auth/__init__.py new file mode 100644 index 0000000..d2730e4 --- /dev/null +++ b/ntlm_auth/__init__.py @@ -0,0 +1,3 @@ +from . import ntlm, session_security + +__all__ = ('ntlm', 'session_security') \ No newline at end of file diff --git a/ntlm_auth/compute_hash.py b/ntlm_auth/compute_hash.py new file mode 100644 index 0000000..ed1cdba --- /dev/null +++ b/ntlm_auth/compute_hash.py @@ -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 or . + +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 \ No newline at end of file diff --git a/ntlm_auth/compute_keys.py b/ntlm_auth/compute_keys.py new file mode 100644 index 0000000..6da04a4 --- /dev/null +++ b/ntlm_auth/compute_keys.py @@ -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 or . + +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 \ No newline at end of file diff --git a/ntlm_auth/compute_response.py b/ntlm_auth/compute_response.py new file mode 100644 index 0000000..ffc4456 --- /dev/null +++ b/ntlm_auth/compute_response.py @@ -0,0 +1,399 @@ +# 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 or . + +import base64 +import calendar +import hashlib +import hmac +import os +import struct +import time +import ntlm_auth.compute_hash as comphash +import ntlm_auth.compute_keys as compkeys +from ntlm_auth import des +from ntlm_auth.constants import NegotiateFlags, AvFlags +from ntlm_auth.gss_channel_bindings import GssChannelBindingsStruct +from ntlm_auth.target_info import TargetInfo + +class ComputeResponse(): + """ + Constructor for the response computations. This class will compute the various + nt and lm challenge responses. + + :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, default is None + :param challenge_message: A ChallengeMessage object that was received from the server after the negotiate_message + :param ntlm_compatibility: The Lan Manager Compatibility Level, used to determine what NTLM auth version to use, see Ntlm in ntlm.py for more details + """ + def __init__(self, user_name, password, nthash, lmhash, domain_name, challenge_message, ntlm_compatibility): + self._user_name = user_name + self._password = password + self._nthash = nthash + self._lmhash = lmhash + self._domain_name = domain_name + self._challenge_message = challenge_message + self._negotiate_flags = challenge_message.negotiate_flags + self._server_challenge = challenge_message.server_challenge + self._server_target_info = challenge_message.target_info + self._ntlm_compatibility = ntlm_compatibility + self._client_challenge = os.urandom(8) + + def get_lm_challenge_response(self): + """ + [MS-NLMP] v28.0 2016-07-14 + + 3.3.1 - NTLM v1 Authentication + 3.3.2 - NTLM v2 Authentication + + This method returns the LmChallengeResponse key based on the ntlm_compatibility chosen + and the target_info supplied by the CHALLENGE_MESSAGE. It is quite different from what + is set in the document as it combines the NTLMv1, NTLM2 and NTLMv2 methods into one + and calls separate methods based on the ntlm_compatibility flag chosen. + + :return: response (LmChallengeResponse) - The LM response to the server challenge. Computed by the client + """ + if self._negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY and self._ntlm_compatibility < 3: + response = ComputeResponse._get_LMv1_with_session_security_response(self._client_challenge) + + elif 0 <= self._ntlm_compatibility <= 1: + response = ComputeResponse._get_LMv1_response(self._password, self._lmhash, self._server_challenge) + elif self._ntlm_compatibility == 2: + # Based on the compatibility level we don't want to use LM responses, ignore the session_base_key as it is returned in nt + response, ignore_key = ComputeResponse._get_NTLMv1_response(self._password, self._nthash, self._server_challenge) + else: + """ + [MS-NLMP] v28.0 page 45 - 2016-07-14 + + 3.1.5.12 Client Received a CHALLENGE_MESSAGE from the Server + If NTLMv2 authentication is used and the CHALLENGE_MESSAGE TargetInfo field has an MsvAvTimestamp present, + the client SHOULD NOT send the LmChallengeResponse and SHOULD send Z(24) instead. + """ + + response = ComputeResponse._get_LMv2_response(self._user_name, self._password, self._nthash, self._domain_name, + self._server_challenge, + self._client_challenge) + if self._server_target_info is not None: + timestamp = self._server_target_info[TargetInfo.MSV_AV_TIMESTAMP] + if timestamp is not None: + response = b'\0' * 24 + + return response + + def get_nt_challenge_response(self, lm_challenge_response, server_certificate_hash): + """ + [MS-NLMP] v28.0 2016-07-14 + + 3.3.1 - NTLM v1 Authentication + 3.3.2 - NTLM v2 Authentication + + This method returns the NtChallengeResponse key based on the ntlm_compatibility chosen + and the target_info supplied by the CHALLENGE_MESSAGE. It is quite different from what + is set in the document as it combines the NTLMv1, NTLM2 and NTLMv2 methods into one + and calls separate methods based on the ntlm_compatibility value chosen. + + :param lm_challenge_response: The LmChallengeResponse calculated beforeand, used to get the key_exchange_key value + :param server_certificate_hash: The SHA256 hash of the server certificate (DER encoded) NTLM is authenticated to. + Used in Channel Binding Tokens if present, default value is None. See + AuthenticateMessage in messages.py for more details + :return response: (NtChallengeResponse) - The NT response to the server challenge. Computed by the client + :return session_base_key: (SessionBaseKey) - A session key calculated from the user password challenge + :return target_info: (AV_PAIR) - The AV_PAIR structure used in the nt_challenge calculations + """ + if self._negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY and self._ntlm_compatibility < 3: + # The compatibility level is less than 3 which means it doesn't support NTLMv2 but we want extended security so use NTLM2 which is different from NTLMv2 + # [MS-NLMP] - 3.3.1 NTLMv1 Authentication + response, session_base_key = ComputeResponse._get_NTLM2_response(self._password, self._nthash, self._server_challenge, self._client_challenge) + key_exchange_key = compkeys._get_exchange_key_ntlm_v1(self._negotiate_flags, session_base_key, + self._server_challenge, lm_challenge_response, + comphash._lmowfv1(self._password, self._lmhash)) + target_info = None + + elif 0 <= self._ntlm_compatibility < 3: + response, session_base_key = ComputeResponse._get_NTLMv1_response(self._password, self._nthash, self._server_challenge) + key_exchange_key = compkeys._get_exchange_key_ntlm_v1(self._negotiate_flags, session_base_key, + self._server_challenge, lm_challenge_response, + comphash._lmowfv1(self._password, self._lmhash)) + target_info = None + else: + if self._server_target_info is None: + target_info = TargetInfo() + else: + target_info = self._server_target_info + + if target_info[TargetInfo.MSV_AV_TIMESTAMP] is None: + timestamp = get_windows_timestamp() + else: + timestamp = target_info[TargetInfo.MSV_AV_TIMESTAMP][1] + + # [MS-NLMP] If the CHALLENGE_MESSAGE TargetInfo field has an MsvAvTimestamp present, the client SHOULD provide a MIC + target_info[TargetInfo.MSV_AV_FLAGS] = struct.pack(" or . + +""" + [MS-NLMP] v28.0 2016-07-14 + + 2.2 Message Syntax + The signature field used in NTLM messages +""" +NTLM_SIGNATURE = b'NTLMSSP\0' + +""" + [MS-NLMP] v28.0 2016-07-14 + + 2.2 Message Syntax + The 3 message type options you can have in a message. +""" +class MessageTypes(object): + NTLM_NEGOTIATE = 0x1 + NTLM_CHALLENGE = 0x2 + NTLM_AUTHENTICATE = 0x3 + +""" + [MS-NLMP] v28.0 2016-07-14 + + 2.2.2.1 AV_PAIR (MsvAvFlags) + A 32-bit value indicated server or client configuration +""" +class AvFlags(object): + AUTHENTICATION_CONSTRAINED = 0x1 + MIC_PROVIDED = 0x2 + UNTRUSTED_SPN_SOURCE = 0x4 + +""" + [MS-NLMP] v28.0 2016-07-14 + + 2.2.2.5 NEGOTIATE + During NTLM authentication, each of the following flags is a possible value of the + NegotiateFlags field of the NEGOTIATE_MESSAGE, CHALLENGE_MESSAGE and AUTHENTICATE_MESSAGE, + unless otherwise noted. These flags define client or server NTLM capabilities + supported by the sender. +""" +class NegotiateFlags(object): + NTLMSSP_NEGOTIATE_56 = 0x80000000 + NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000 + NTLMSSP_NEGOTIATE_128 = 0x20000000 + NTLMSSP_RESERVED_R1 = 0x10000000 + NTLMSSP_RESERVED_R2 = 0x08000000 + NTLMSSP_RESERVED_R3 = 0x04000000 + NTLMSSP_NEGOTIATE_VERSION = 0x02000000 + NTLMSSP_RESERVED_R4 = 0x01000000 + NTLMSSP_NEGOTIATE_TARGET_INFO = 0x00800000 + NTLMSSP_REQUEST_NON_NT_SESSION_KEY = 0x00400000 + NTLMSSP_RESERVED_R5 = 0x00200000 + NTLMSSP_NEGOTIATE_IDENTITY = 0x00100000 + NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000 + NTLMSSP_RESERVED_R6 = 0x00040000 + NTLMSSP_TARGET_TYPE_SERVER = 0x00020000 + NTLMSSP_TARGET_TYPE_DOMAIN = 0x00010000 + NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x00008000 + NTLMSSP_RESERVED_R7 = 0x00004000 + NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x00002000 + NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED = 0x00001000 + NTLMSSP_ANOYNMOUS = 0x00000800 + NTLMSSP_RESERVED_R8 = 0x00000400 + NTLMSSP_NEGOTIATE_NTLM = 0x00000200 + NTLMSSP_RESERVED_R9 = 0x00000100 + NTLMSSP_NEGOTIATE_LM_KEY = 0x00000080 + NTLMSSP_NEGOTIATE_DATAGRAM = 0x00000040 + NTLMSSP_NEGOTIATE_SEAL = 0x00000020 + NTLMSSP_NEGOTIATE_SIGN = 0x00000010 + NTLMSSP_RESERVED_R10 = 0x00000008 + NTLMSSP_REQUEST_TARGET = 0x00000004 + NTLMSSP_NEGOTIATE_OEM = 0x00000002 + NTLMSSP_NEGOTIATE_UNICODE = 0x00000001 + +class SignSealConstants(object): + # Magic Contants used to get the signing and sealing key for Extended Session Security + CLIENT_SIGNING = b"session key to client-to-server signing key magic constant\0" + SERVER_SIGNING = b"session key to server-to-client signing key magic constant\0" + CLIENT_SEALING = b"session key to client-to-server sealing key magic constant\0" + SERVER_SEALING = b"session key to server-to-client sealing key magic constant\0" diff --git a/ntlm_auth/des.py b/ntlm_auth/des.py new file mode 100644 index 0000000..03b71fa --- /dev/null +++ b/ntlm_auth/des.py @@ -0,0 +1,88 @@ +# This file is part of 'NTLM Authorization Proxy Server' http://sourceforge.net/projects/ntlmaps/ +# Copyright 2001 Dmitry A. Rozmanov +# +# 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 or . +import logging +import six +from ntlm_auth import des_c + +log = logging.getLogger(__name__) + + +class DES: + des_c_obj = None + + def __init__(self, key_str): + k = str_to_key56(key_str) + k = key56_to_key64(k) + + key_str = b'' + for i in k: + key_str += six.int2byte(i & 0xFF) + + self.des_c_obj = des_c.DES(key_str) + + def encrypt(self, plain_text): + return self.des_c_obj.encrypt(plain_text) + + def decrypt(self, crypted_text): + return self.des_c_obj.decrypt(crypted_text) + + +DESException = 'DESException' + + +def str_to_key56(key_str): + + if not type(key_str) == six.binary_type: + # TODO rsanders high - figure out how to make this not necessary + key_str = key_str.encode('ascii') + + if len(key_str) < 7: + key_str = key_str + b'\000\000\000\000\000\000\000'[:(7 - len(key_str))] + key_56 = [] + for i in six.iterbytes(key_str[:7]): + key_56.append(i) + + return key_56 + + +def key56_to_key64(key_56): + key = [] + for i in range(8): + key.append(0) + + key[0] = key_56[0] + key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1) + key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2) + key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3) + key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4) + key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5) + key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6) + key[7] = (key_56[6] << 1) & 0xFF + + key = set_key_odd_parity(key) + + return key + + +def set_key_odd_parity(key): + for i in range(len(key)): + for k in range(7): + bit = 0 + t = key[i] >> k + bit = (t ^ bit) & 0x1 + key[i] = (key[i] & 0xFE) | bit + + return key diff --git a/ntlm_auth/des_c.py b/ntlm_auth/des_c.py new file mode 100644 index 0000000..5dfd413 --- /dev/null +++ b/ntlm_auth/des_c.py @@ -0,0 +1,254 @@ +# This file is part of 'NTLM Authorization Proxy Server' http://sourceforge.net/projects/ntlmaps/ +# Copyright 2001 Dmitry A. Rozmanov +# +# 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 or . +import six + +from ntlm_auth.U32 import U32 +from ntlm_auth.des_data import des_SPtrans, des_skb + +def c2l(c): + "char[4] to unsigned long" + l = U32(c[0]) + l = l | (U32(c[1]) << 8) + l = l | (U32(c[2]) << 16) + l = l | (U32(c[3]) << 24) + return l + + +def l2c(l): + "unsigned long to char[4]" + c = [] + c.append(int(l & U32(0xFF))) + c.append(int((l >> 8) & U32(0xFF))) + c.append(int((l >> 16) & U32(0xFF))) + c.append(int((l >> 24) & U32(0xFF))) + return c + + +def D_ENCRYPT(tup, u, t, s): + L, R, S = tup + # print 'LRS1', L, R, S, u, t, '-->', + u = (R ^ s[S]) + t = R ^ s[S + 1] + t = ((t >> 4) + (t << 28)) + L = L ^ (des_SPtrans[1][int((t) & U32(0x3f))] | + des_SPtrans[3][int((t >> 8) & U32(0x3f))] | + des_SPtrans[5][int((t >> 16) & U32(0x3f))] | + des_SPtrans[7][int((t >> 24) & U32(0x3f))] | + des_SPtrans[0][int((u) & U32(0x3f))] | + des_SPtrans[2][int((u >> 8) & U32(0x3f))] | + des_SPtrans[4][int((u >> 16) & U32(0x3f))] | + des_SPtrans[6][int((u >> 24) & U32(0x3f))]) + # print 'LRS:', L, R, S, u, t + return (L, R, S), u, t, s + + +def PERM_OP(tup, n, m): + "tup - (a, b, t)" + a, b, t = tup + t = ((a >> n) ^ b) & m + b = b ^ t + a = a ^ (t << n) + return (a, b, t) + + +def HPERM_OP(tup, n, m): + "tup - (a, t)" + a, t = tup + t = ((a << (16 - n)) ^ a) & m + a = a ^ t ^ (t >> (16 - n)) + return a, t + + +shifts2 = [0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0] + + +class DES: + KeySched = None # des_key_schedule + + def __init__(self, key_str): + self.KeySched = des_set_key(key_str) + + def decrypt(self, str): + # block - UChar[] + block = [] + + for i in six.iterbytes(str): + block.append(i) + + # print block + block = des_ecb_encrypt(block, self.KeySched, 0) + + res = b'' + for i in block: + res = res + six.int2byte(i) + + return res + + def encrypt(self, plaintext): + # block - UChar[] + + block = [] + for i in plaintext: + block.append(i) + + block = des_ecb_encrypt(block, self.KeySched, 1) + + res = b'' + + for i in block: + res += six.int2byte(i) + + return res + + +def des_encript(input, ks, encrypt): + # input - U32[] + # output - U32[] + # ks - des_key_shedule - U32[2][16] + # encrypt - int + # l, r, t, u - U32 + # i - int + # s - U32[] + + l = input[0] + r = input[1] + t = U32(0) + u = U32(0) + + r, l, t = PERM_OP((r, l, t), 4, U32(0x0f0f0f0f)) + l, r, t = PERM_OP((l, r, t), 16, U32(0x0000ffff)) + r, l, t = PERM_OP((r, l, t), 2, U32(0x33333333)) + l, r, t = PERM_OP((l, r, t), 8, U32(0x00ff00ff)) + r, l, t = PERM_OP((r, l, t), 1, U32(0x55555555)) + + t = (r << 1) | (r >> 31) + r = (l << 1) | (l >> 31) + l = t + + s = ks # ??????????????? + # print l, r + if encrypt: + for i in range(0, 32, 4): + rtup, u, t, s = D_ENCRYPT((l, r, i + 0), u, t, s) + l = rtup[0] + r = rtup[1] + rtup, u, t, s = D_ENCRYPT((r, l, i + 2), u, t, s) + r = rtup[0] + l = rtup[1] + else: + for i in range(30, 0, -4): + rtup, u, t, s = D_ENCRYPT((l, r, i - 0), u, t, s) + l = rtup[0] + r = rtup[1] + rtup, u, t, s = D_ENCRYPT((r, l, i - 2), u, t, s) + r = rtup[0] + l = rtup[1] + # print l, r + l = (l >> 1) | (l << 31) + r = (r >> 1) | (r << 31) + + r, l, t = PERM_OP((r, l, t), 1, U32(0x55555555)) + l, r, t = PERM_OP((l, r, t), 8, U32(0x00ff00ff)) + r, l, t = PERM_OP((r, l, t), 2, U32(0x33333333)) + l, r, t = PERM_OP((l, r, t), 16, U32(0x0000ffff)) + r, l, t = PERM_OP((r, l, t), 4, U32(0x0f0f0f0f)) + + output = [l] + output.append(r) + l, r, t, u = U32(0), U32(0), U32(0), U32(0) + return output + + +def des_ecb_encrypt(input, ks, encrypt): + # input - des_cblock - UChar[8] + # output - des_cblock - UChar[8] + # ks - des_key_shedule - U32[2][16] + # encrypt - int + + # print input + l0 = c2l(input[0:4]) + l1 = c2l(input[4:8]) + ll = [l0] + ll.append(l1) + # print ll + ll = des_encript(ll, ks, encrypt) + # print ll + l0 = ll[0] + l1 = ll[1] + output = l2c(l0) + output = output + l2c(l1) + # print output + l0, l1, ll[0], ll[1] = U32(0), U32(0), U32(0), U32(0) + return output + + +def des_set_key(key): + # key - des_cblock - UChar[8] + # schedule - des_key_schedule + + # register unsigned long c,d,t,s; + # register unsigned char *in; + # register unsigned long *k; + # register int i; + + # k = schedule + # in = key + + k = [] + c = c2l(key[0:4]) + d = c2l(key[4:8]) + t = U32(0) + + d, c, t = PERM_OP((d, c, t), 4, U32(0x0f0f0f0f)) + c, t = HPERM_OP((c, t), -2, U32(0xcccc0000)) + d, t = HPERM_OP((d, t), -2, U32(0xcccc0000)) + d, c, t = PERM_OP((d, c, t), 1, U32(0x55555555)) + c, d, t = PERM_OP((c, d, t), 8, U32(0x00ff00ff)) + d, c, t = PERM_OP((d, c, t), 1, U32(0x55555555)) + + d = (((d & U32(0x000000ff)) << 16) | (d & U32(0x0000ff00)) | ((d & U32(0x00ff0000)) >> 16) | ( + (c & U32(0xf0000000)) >> 4)) + c = c & U32(0x0fffffff) + + for i in range(16): + if (shifts2[i]): + c = ((c >> 2) | (c << 26)) + d = ((d >> 2) | (d << 26)) + else: + c = ((c >> 1) | (c << 27)) + d = ((d >> 1) | (d << 27)) + c = c & U32(0x0fffffff) + d = d & U32(0x0fffffff) + + s = des_skb[0][int((c) & U32(0x3f))] | \ + des_skb[1][int(((c >> 6) & U32(0x03)) | ((c >> 7) & U32(0x3c)))] | \ + des_skb[2][int(((c >> 13) & U32(0x0f)) | ((c >> 14) & U32(0x30)))] | \ + des_skb[3][int(((c >> 20) & U32(0x01)) | ((c >> 21) & U32(0x06)) | ((c >> 22) & U32(0x38)))] + + t = des_skb[4][int((d) & U32(0x3f))] | \ + des_skb[5][int(((d >> 7) & U32(0x03)) | ((d >> 8) & U32(0x3c)))] | \ + des_skb[6][int((d >> 15) & U32(0x3f))] | \ + des_skb[7][int(((d >> 21) & U32(0x0f)) | ((d >> 22) & U32(0x30)))] + # print s, t + + k.append(((t << 16) | (s & U32(0x0000ffff))) & U32(0xffffffff)) + s = ((s >> 16) | (t & U32(0xffff0000))) + s = (s << 4) | (s >> 28) + k.append(s & U32(0xffffffff)) + + schedule = k + + return schedule diff --git a/ntlm_auth/des_data.py b/ntlm_auth/des_data.py new file mode 100644 index 0000000..91ea70a --- /dev/null +++ b/ntlm_auth/des_data.py @@ -0,0 +1,348 @@ +# This file is part of 'NTLM Authorization Proxy Server' http://sourceforge.net/projects/ntlmaps/ +# Copyright 2001 Dmitry A. Rozmanov +# +# 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 or . + +from ntlm_auth.U32 import U32 + +# static unsigned long des_SPtrans[8][64]={ + +des_SPtrans = \ + [ + # nibble 0 + [ + U32(0x00820200), U32(0x00020000), U32(0x80800000), U32(0x80820200), + U32(0x00800000), U32(0x80020200), U32(0x80020000), U32(0x80800000), + U32(0x80020200), U32(0x00820200), U32(0x00820000), U32(0x80000200), + U32(0x80800200), U32(0x00800000), U32(0x00000000), U32(0x80020000), + U32(0x00020000), U32(0x80000000), U32(0x00800200), U32(0x00020200), + U32(0x80820200), U32(0x00820000), U32(0x80000200), U32(0x00800200), + U32(0x80000000), U32(0x00000200), U32(0x00020200), U32(0x80820000), + U32(0x00000200), U32(0x80800200), U32(0x80820000), U32(0x00000000), + U32(0x00000000), U32(0x80820200), U32(0x00800200), U32(0x80020000), + U32(0x00820200), U32(0x00020000), U32(0x80000200), U32(0x00800200), + U32(0x80820000), U32(0x00000200), U32(0x00020200), U32(0x80800000), + U32(0x80020200), U32(0x80000000), U32(0x80800000), U32(0x00820000), + U32(0x80820200), U32(0x00020200), U32(0x00820000), U32(0x80800200), + U32(0x00800000), U32(0x80000200), U32(0x80020000), U32(0x00000000), + U32(0x00020000), U32(0x00800000), U32(0x80800200), U32(0x00820200), + U32(0x80000000), U32(0x80820000), U32(0x00000200), U32(0x80020200), + ], + + # nibble 1 + [ + U32(0x10042004), U32(0x00000000), U32(0x00042000), U32(0x10040000), + U32(0x10000004), U32(0x00002004), U32(0x10002000), U32(0x00042000), + U32(0x00002000), U32(0x10040004), U32(0x00000004), U32(0x10002000), + U32(0x00040004), U32(0x10042000), U32(0x10040000), U32(0x00000004), + U32(0x00040000), U32(0x10002004), U32(0x10040004), U32(0x00002000), + U32(0x00042004), U32(0x10000000), U32(0x00000000), U32(0x00040004), + U32(0x10002004), U32(0x00042004), U32(0x10042000), U32(0x10000004), + U32(0x10000000), U32(0x00040000), U32(0x00002004), U32(0x10042004), + U32(0x00040004), U32(0x10042000), U32(0x10002000), U32(0x00042004), + U32(0x10042004), U32(0x00040004), U32(0x10000004), U32(0x00000000), + U32(0x10000000), U32(0x00002004), U32(0x00040000), U32(0x10040004), + U32(0x00002000), U32(0x10000000), U32(0x00042004), U32(0x10002004), + U32(0x10042000), U32(0x00002000), U32(0x00000000), U32(0x10000004), + U32(0x00000004), U32(0x10042004), U32(0x00042000), U32(0x10040000), + U32(0x10040004), U32(0x00040000), U32(0x00002004), U32(0x10002000), + U32(0x10002004), U32(0x00000004), U32(0x10040000), U32(0x00042000), + ], + + # nibble 2 + [ + U32(0x41000000), U32(0x01010040), U32(0x00000040), U32(0x41000040), + U32(0x40010000), U32(0x01000000), U32(0x41000040), U32(0x00010040), + U32(0x01000040), U32(0x00010000), U32(0x01010000), U32(0x40000000), + U32(0x41010040), U32(0x40000040), U32(0x40000000), U32(0x41010000), + U32(0x00000000), U32(0x40010000), U32(0x01010040), U32(0x00000040), + U32(0x40000040), U32(0x41010040), U32(0x00010000), U32(0x41000000), + U32(0x41010000), U32(0x01000040), U32(0x40010040), U32(0x01010000), + U32(0x00010040), U32(0x00000000), U32(0x01000000), U32(0x40010040), + U32(0x01010040), U32(0x00000040), U32(0x40000000), U32(0x00010000), + U32(0x40000040), U32(0x40010000), U32(0x01010000), U32(0x41000040), + U32(0x00000000), U32(0x01010040), U32(0x00010040), U32(0x41010000), + U32(0x40010000), U32(0x01000000), U32(0x41010040), U32(0x40000000), + U32(0x40010040), U32(0x41000000), U32(0x01000000), U32(0x41010040), + U32(0x00010000), U32(0x01000040), U32(0x41000040), U32(0x00010040), + U32(0x01000040), U32(0x00000000), U32(0x41010000), U32(0x40000040), + U32(0x41000000), U32(0x40010040), U32(0x00000040), U32(0x01010000), + ], + + # nibble 3 + [ + U32(0x00100402), U32(0x04000400), U32(0x00000002), U32(0x04100402), + U32(0x00000000), U32(0x04100000), U32(0x04000402), U32(0x00100002), + U32(0x04100400), U32(0x04000002), U32(0x04000000), U32(0x00000402), + U32(0x04000002), U32(0x00100402), U32(0x00100000), U32(0x04000000), + U32(0x04100002), U32(0x00100400), U32(0x00000400), U32(0x00000002), + U32(0x00100400), U32(0x04000402), U32(0x04100000), U32(0x00000400), + U32(0x00000402), U32(0x00000000), U32(0x00100002), U32(0x04100400), + U32(0x04000400), U32(0x04100002), U32(0x04100402), U32(0x00100000), + U32(0x04100002), U32(0x00000402), U32(0x00100000), U32(0x04000002), + U32(0x00100400), U32(0x04000400), U32(0x00000002), U32(0x04100000), + U32(0x04000402), U32(0x00000000), U32(0x00000400), U32(0x00100002), + U32(0x00000000), U32(0x04100002), U32(0x04100400), U32(0x00000400), + U32(0x04000000), U32(0x04100402), U32(0x00100402), U32(0x00100000), + U32(0x04100402), U32(0x00000002), U32(0x04000400), U32(0x00100402), + U32(0x00100002), U32(0x00100400), U32(0x04100000), U32(0x04000402), + U32(0x00000402), U32(0x04000000), U32(0x04000002), U32(0x04100400), + ], + + # nibble 4 + [ + U32(0x02000000), U32(0x00004000), U32(0x00000100), U32(0x02004108), + U32(0x02004008), U32(0x02000100), U32(0x00004108), U32(0x02004000), + U32(0x00004000), U32(0x00000008), U32(0x02000008), U32(0x00004100), + U32(0x02000108), U32(0x02004008), U32(0x02004100), U32(0x00000000), + U32(0x00004100), U32(0x02000000), U32(0x00004008), U32(0x00000108), + U32(0x02000100), U32(0x00004108), U32(0x00000000), U32(0x02000008), + U32(0x00000008), U32(0x02000108), U32(0x02004108), U32(0x00004008), + U32(0x02004000), U32(0x00000100), U32(0x00000108), U32(0x02004100), + U32(0x02004100), U32(0x02000108), U32(0x00004008), U32(0x02004000), + U32(0x00004000), U32(0x00000008), U32(0x02000008), U32(0x02000100), + U32(0x02000000), U32(0x00004100), U32(0x02004108), U32(0x00000000), + U32(0x00004108), U32(0x02000000), U32(0x00000100), U32(0x00004008), + U32(0x02000108), U32(0x00000100), U32(0x00000000), U32(0x02004108), + U32(0x02004008), U32(0x02004100), U32(0x00000108), U32(0x00004000), + U32(0x00004100), U32(0x02004008), U32(0x02000100), U32(0x00000108), + U32(0x00000008), U32(0x00004108), U32(0x02004000), U32(0x02000008), + ], + + # nibble 5 + [ + U32(0x20000010), U32(0x00080010), U32(0x00000000), U32(0x20080800), + U32(0x00080010), U32(0x00000800), U32(0x20000810), U32(0x00080000), + U32(0x00000810), U32(0x20080810), U32(0x00080800), U32(0x20000000), + U32(0x20000800), U32(0x20000010), U32(0x20080000), U32(0x00080810), + U32(0x00080000), U32(0x20000810), U32(0x20080010), U32(0x00000000), + U32(0x00000800), U32(0x00000010), U32(0x20080800), U32(0x20080010), + U32(0x20080810), U32(0x20080000), U32(0x20000000), U32(0x00000810), + U32(0x00000010), U32(0x00080800), U32(0x00080810), U32(0x20000800), + U32(0x00000810), U32(0x20000000), U32(0x20000800), U32(0x00080810), + U32(0x20080800), U32(0x00080010), U32(0x00000000), U32(0x20000800), + U32(0x20000000), U32(0x00000800), U32(0x20080010), U32(0x00080000), + U32(0x00080010), U32(0x20080810), U32(0x00080800), U32(0x00000010), + U32(0x20080810), U32(0x00080800), U32(0x00080000), U32(0x20000810), + U32(0x20000010), U32(0x20080000), U32(0x00080810), U32(0x00000000), + U32(0x00000800), U32(0x20000010), U32(0x20000810), U32(0x20080800), + U32(0x20080000), U32(0x00000810), U32(0x00000010), U32(0x20080010), + ], + + # nibble 6 + [ + U32(0x00001000), U32(0x00000080), U32(0x00400080), U32(0x00400001), + U32(0x00401081), U32(0x00001001), U32(0x00001080), U32(0x00000000), + U32(0x00400000), U32(0x00400081), U32(0x00000081), U32(0x00401000), + U32(0x00000001), U32(0x00401080), U32(0x00401000), U32(0x00000081), + U32(0x00400081), U32(0x00001000), U32(0x00001001), U32(0x00401081), + U32(0x00000000), U32(0x00400080), U32(0x00400001), U32(0x00001080), + U32(0x00401001), U32(0x00001081), U32(0x00401080), U32(0x00000001), + U32(0x00001081), U32(0x00401001), U32(0x00000080), U32(0x00400000), + U32(0x00001081), U32(0x00401000), U32(0x00401001), U32(0x00000081), + U32(0x00001000), U32(0x00000080), U32(0x00400000), U32(0x00401001), + U32(0x00400081), U32(0x00001081), U32(0x00001080), U32(0x00000000), + U32(0x00000080), U32(0x00400001), U32(0x00000001), U32(0x00400080), + U32(0x00000000), U32(0x00400081), U32(0x00400080), U32(0x00001080), + U32(0x00000081), U32(0x00001000), U32(0x00401081), U32(0x00400000), + U32(0x00401080), U32(0x00000001), U32(0x00001001), U32(0x00401081), + U32(0x00400001), U32(0x00401080), U32(0x00401000), U32(0x00001001), + ], + + # nibble 7 + [ + U32(0x08200020), U32(0x08208000), U32(0x00008020), U32(0x00000000), + U32(0x08008000), U32(0x00200020), U32(0x08200000), U32(0x08208020), + U32(0x00000020), U32(0x08000000), U32(0x00208000), U32(0x00008020), + U32(0x00208020), U32(0x08008020), U32(0x08000020), U32(0x08200000), + U32(0x00008000), U32(0x00208020), U32(0x00200020), U32(0x08008000), + U32(0x08208020), U32(0x08000020), U32(0x00000000), U32(0x00208000), + U32(0x08000000), U32(0x00200000), U32(0x08008020), U32(0x08200020), + U32(0x00200000), U32(0x00008000), U32(0x08208000), U32(0x00000020), + U32(0x00200000), U32(0x00008000), U32(0x08000020), U32(0x08208020), + U32(0x00008020), U32(0x08000000), U32(0x00000000), U32(0x00208000), + U32(0x08200020), U32(0x08008020), U32(0x08008000), U32(0x00200020), + U32(0x08208000), U32(0x00000020), U32(0x00200020), U32(0x08008000), + U32(0x08208020), U32(0x00200000), U32(0x08200000), U32(0x08000020), + U32(0x00208000), U32(0x00008020), U32(0x08008020), U32(0x08200000), + U32(0x00000020), U32(0x08208000), U32(0x00208020), U32(0x00000000), + U32(0x08000000), U32(0x08200020), U32(0x00008000), U32(0x00208020), + ], + ] + +# static unsigned long des_skb[8][64]={ + +des_skb = \ + [ + # for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 + [ + U32(0x00000000), U32(0x00000010), U32(0x20000000), U32(0x20000010), + U32(0x00010000), U32(0x00010010), U32(0x20010000), U32(0x20010010), + U32(0x00000800), U32(0x00000810), U32(0x20000800), U32(0x20000810), + U32(0x00010800), U32(0x00010810), U32(0x20010800), U32(0x20010810), + U32(0x00000020), U32(0x00000030), U32(0x20000020), U32(0x20000030), + U32(0x00010020), U32(0x00010030), U32(0x20010020), U32(0x20010030), + U32(0x00000820), U32(0x00000830), U32(0x20000820), U32(0x20000830), + U32(0x00010820), U32(0x00010830), U32(0x20010820), U32(0x20010830), + U32(0x00080000), U32(0x00080010), U32(0x20080000), U32(0x20080010), + U32(0x00090000), U32(0x00090010), U32(0x20090000), U32(0x20090010), + U32(0x00080800), U32(0x00080810), U32(0x20080800), U32(0x20080810), + U32(0x00090800), U32(0x00090810), U32(0x20090800), U32(0x20090810), + U32(0x00080020), U32(0x00080030), U32(0x20080020), U32(0x20080030), + U32(0x00090020), U32(0x00090030), U32(0x20090020), U32(0x20090030), + U32(0x00080820), U32(0x00080830), U32(0x20080820), U32(0x20080830), + U32(0x00090820), U32(0x00090830), U32(0x20090820), U32(0x20090830), + ], + + # for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 + [ + U32(0x00000000), U32(0x02000000), U32(0x00002000), U32(0x02002000), + U32(0x00200000), U32(0x02200000), U32(0x00202000), U32(0x02202000), + U32(0x00000004), U32(0x02000004), U32(0x00002004), U32(0x02002004), + U32(0x00200004), U32(0x02200004), U32(0x00202004), U32(0x02202004), + U32(0x00000400), U32(0x02000400), U32(0x00002400), U32(0x02002400), + U32(0x00200400), U32(0x02200400), U32(0x00202400), U32(0x02202400), + U32(0x00000404), U32(0x02000404), U32(0x00002404), U32(0x02002404), + U32(0x00200404), U32(0x02200404), U32(0x00202404), U32(0x02202404), + U32(0x10000000), U32(0x12000000), U32(0x10002000), U32(0x12002000), + U32(0x10200000), U32(0x12200000), U32(0x10202000), U32(0x12202000), + U32(0x10000004), U32(0x12000004), U32(0x10002004), U32(0x12002004), + U32(0x10200004), U32(0x12200004), U32(0x10202004), U32(0x12202004), + U32(0x10000400), U32(0x12000400), U32(0x10002400), U32(0x12002400), + U32(0x10200400), U32(0x12200400), U32(0x10202400), U32(0x12202400), + U32(0x10000404), U32(0x12000404), U32(0x10002404), U32(0x12002404), + U32(0x10200404), U32(0x12200404), U32(0x10202404), U32(0x12202404), + ], + + # for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 + [ + U32(0x00000000), U32(0x00000001), U32(0x00040000), U32(0x00040001), + U32(0x01000000), U32(0x01000001), U32(0x01040000), U32(0x01040001), + U32(0x00000002), U32(0x00000003), U32(0x00040002), U32(0x00040003), + U32(0x01000002), U32(0x01000003), U32(0x01040002), U32(0x01040003), + U32(0x00000200), U32(0x00000201), U32(0x00040200), U32(0x00040201), + U32(0x01000200), U32(0x01000201), U32(0x01040200), U32(0x01040201), + U32(0x00000202), U32(0x00000203), U32(0x00040202), U32(0x00040203), + U32(0x01000202), U32(0x01000203), U32(0x01040202), U32(0x01040203), + U32(0x08000000), U32(0x08000001), U32(0x08040000), U32(0x08040001), + U32(0x09000000), U32(0x09000001), U32(0x09040000), U32(0x09040001), + U32(0x08000002), U32(0x08000003), U32(0x08040002), U32(0x08040003), + U32(0x09000002), U32(0x09000003), U32(0x09040002), U32(0x09040003), + U32(0x08000200), U32(0x08000201), U32(0x08040200), U32(0x08040201), + U32(0x09000200), U32(0x09000201), U32(0x09040200), U32(0x09040201), + U32(0x08000202), U32(0x08000203), U32(0x08040202), U32(0x08040203), + U32(0x09000202), U32(0x09000203), U32(0x09040202), U32(0x09040203), + ], + + # for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 + [ + U32(0x00000000), U32(0x00100000), U32(0x00000100), U32(0x00100100), + U32(0x00000008), U32(0x00100008), U32(0x00000108), U32(0x00100108), + U32(0x00001000), U32(0x00101000), U32(0x00001100), U32(0x00101100), + U32(0x00001008), U32(0x00101008), U32(0x00001108), U32(0x00101108), + U32(0x04000000), U32(0x04100000), U32(0x04000100), U32(0x04100100), + U32(0x04000008), U32(0x04100008), U32(0x04000108), U32(0x04100108), + U32(0x04001000), U32(0x04101000), U32(0x04001100), U32(0x04101100), + U32(0x04001008), U32(0x04101008), U32(0x04001108), U32(0x04101108), + U32(0x00020000), U32(0x00120000), U32(0x00020100), U32(0x00120100), + U32(0x00020008), U32(0x00120008), U32(0x00020108), U32(0x00120108), + U32(0x00021000), U32(0x00121000), U32(0x00021100), U32(0x00121100), + U32(0x00021008), U32(0x00121008), U32(0x00021108), U32(0x00121108), + U32(0x04020000), U32(0x04120000), U32(0x04020100), U32(0x04120100), + U32(0x04020008), U32(0x04120008), U32(0x04020108), U32(0x04120108), + U32(0x04021000), U32(0x04121000), U32(0x04021100), U32(0x04121100), + U32(0x04021008), U32(0x04121008), U32(0x04021108), U32(0x04121108), + ], + + # for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 + [ + U32(0x00000000), U32(0x10000000), U32(0x00010000), U32(0x10010000), + U32(0x00000004), U32(0x10000004), U32(0x00010004), U32(0x10010004), + U32(0x20000000), U32(0x30000000), U32(0x20010000), U32(0x30010000), + U32(0x20000004), U32(0x30000004), U32(0x20010004), U32(0x30010004), + U32(0x00100000), U32(0x10100000), U32(0x00110000), U32(0x10110000), + U32(0x00100004), U32(0x10100004), U32(0x00110004), U32(0x10110004), + U32(0x20100000), U32(0x30100000), U32(0x20110000), U32(0x30110000), + U32(0x20100004), U32(0x30100004), U32(0x20110004), U32(0x30110004), + U32(0x00001000), U32(0x10001000), U32(0x00011000), U32(0x10011000), + U32(0x00001004), U32(0x10001004), U32(0x00011004), U32(0x10011004), + U32(0x20001000), U32(0x30001000), U32(0x20011000), U32(0x30011000), + U32(0x20001004), U32(0x30001004), U32(0x20011004), U32(0x30011004), + U32(0x00101000), U32(0x10101000), U32(0x00111000), U32(0x10111000), + U32(0x00101004), U32(0x10101004), U32(0x00111004), U32(0x10111004), + U32(0x20101000), U32(0x30101000), U32(0x20111000), U32(0x30111000), + U32(0x20101004), U32(0x30101004), U32(0x20111004), U32(0x30111004), + ], + + # for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 + [ + U32(0x00000000), U32(0x08000000), U32(0x00000008), U32(0x08000008), + U32(0x00000400), U32(0x08000400), U32(0x00000408), U32(0x08000408), + U32(0x00020000), U32(0x08020000), U32(0x00020008), U32(0x08020008), + U32(0x00020400), U32(0x08020400), U32(0x00020408), U32(0x08020408), + U32(0x00000001), U32(0x08000001), U32(0x00000009), U32(0x08000009), + U32(0x00000401), U32(0x08000401), U32(0x00000409), U32(0x08000409), + U32(0x00020001), U32(0x08020001), U32(0x00020009), U32(0x08020009), + U32(0x00020401), U32(0x08020401), U32(0x00020409), U32(0x08020409), + U32(0x02000000), U32(0x0A000000), U32(0x02000008), U32(0x0A000008), + U32(0x02000400), U32(0x0A000400), U32(0x02000408), U32(0x0A000408), + U32(0x02020000), U32(0x0A020000), U32(0x02020008), U32(0x0A020008), + U32(0x02020400), U32(0x0A020400), U32(0x02020408), U32(0x0A020408), + U32(0x02000001), U32(0x0A000001), U32(0x02000009), U32(0x0A000009), + U32(0x02000401), U32(0x0A000401), U32(0x02000409), U32(0x0A000409), + U32(0x02020001), U32(0x0A020001), U32(0x02020009), U32(0x0A020009), + U32(0x02020401), U32(0x0A020401), U32(0x02020409), U32(0x0A020409), + ], + + # for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 + [ + U32(0x00000000), U32(0x00000100), U32(0x00080000), U32(0x00080100), + U32(0x01000000), U32(0x01000100), U32(0x01080000), U32(0x01080100), + U32(0x00000010), U32(0x00000110), U32(0x00080010), U32(0x00080110), + U32(0x01000010), U32(0x01000110), U32(0x01080010), U32(0x01080110), + U32(0x00200000), U32(0x00200100), U32(0x00280000), U32(0x00280100), + U32(0x01200000), U32(0x01200100), U32(0x01280000), U32(0x01280100), + U32(0x00200010), U32(0x00200110), U32(0x00280010), U32(0x00280110), + U32(0x01200010), U32(0x01200110), U32(0x01280010), U32(0x01280110), + U32(0x00000200), U32(0x00000300), U32(0x00080200), U32(0x00080300), + U32(0x01000200), U32(0x01000300), U32(0x01080200), U32(0x01080300), + U32(0x00000210), U32(0x00000310), U32(0x00080210), U32(0x00080310), + U32(0x01000210), U32(0x01000310), U32(0x01080210), U32(0x01080310), + U32(0x00200200), U32(0x00200300), U32(0x00280200), U32(0x00280300), + U32(0x01200200), U32(0x01200300), U32(0x01280200), U32(0x01280300), + U32(0x00200210), U32(0x00200310), U32(0x00280210), U32(0x00280310), + U32(0x01200210), U32(0x01200310), U32(0x01280210), U32(0x01280310), + ], + + # for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 + [ + U32(0x00000000), U32(0x04000000), U32(0x00040000), U32(0x04040000), + U32(0x00000002), U32(0x04000002), U32(0x00040002), U32(0x04040002), + U32(0x00002000), U32(0x04002000), U32(0x00042000), U32(0x04042000), + U32(0x00002002), U32(0x04002002), U32(0x00042002), U32(0x04042002), + U32(0x00000020), U32(0x04000020), U32(0x00040020), U32(0x04040020), + U32(0x00000022), U32(0x04000022), U32(0x00040022), U32(0x04040022), + U32(0x00002020), U32(0x04002020), U32(0x00042020), U32(0x04042020), + U32(0x00002022), U32(0x04002022), U32(0x00042022), U32(0x04042022), + U32(0x00000800), U32(0x04000800), U32(0x00040800), U32(0x04040800), + U32(0x00000802), U32(0x04000802), U32(0x00040802), U32(0x04040802), + U32(0x00002800), U32(0x04002800), U32(0x00042800), U32(0x04042800), + U32(0x00002802), U32(0x04002802), U32(0x00042802), U32(0x04042802), + U32(0x00000820), U32(0x04000820), U32(0x00040820), U32(0x04040820), + U32(0x00000822), U32(0x04000822), U32(0x00040822), U32(0x04040822), + U32(0x00002820), U32(0x04002820), U32(0x00042820), U32(0x04042820), + U32(0x00002822), U32(0x04002822), U32(0x00042822), U32(0x04042822), + ] + + ] diff --git a/ntlm_auth/gss_channel_bindings.py b/ntlm_auth/gss_channel_bindings.py new file mode 100644 index 0000000..c4b4e18 --- /dev/null +++ b/ntlm_auth/gss_channel_bindings.py @@ -0,0 +1,67 @@ +# 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 or . + +import struct + +""" + This is not the easiest structure to understand, ultimately this is a set structure + as defined by Microsoft. Channel Binding Tokens set the SHA256 hash of the server + certificate to the application_data field and then ultimately creates the MD5 hash + to include in the NTLM auth from there. This class is just designed to create the + bindings structure which is then used by compute_response.py to do the rest of the + work. + + For more infor on how this works and how it is derived, this is a great link; + https://blogs.msdn.microsoft.com/openspecification/2013/03/26/ntlm-and-channel-binding-hash-aka-extended-protection-for-authentication/ +""" +class GssChannelBindingsStruct(object): + INITIATOR_ADDTYPE = 'initiator_addtype' + INITIATOR_ADDRESS_LENGTH = 'initiator_address_length' + ACCEPTOR_ADDRTYPE = 'acceptor_addrtype' + ACCEPTOR_ADDRESS_LENGTH = 'acceptor_address_length' + APPLICATION_DATA_LENGTH = 'application_data_length' + INITIATOR_ADDRESS = 'initiator_address' + ACCEPTOR_ADDRESS = 'acceptor_address' + APPLICATION_DATA = 'application_data' + + def __init__(self): + self.fields = {} + self.fields[self.INITIATOR_ADDTYPE] = 0 + self.fields[self.INITIATOR_ADDRESS_LENGTH] = 0 + self.fields[self.ACCEPTOR_ADDRTYPE] = 0 + self.fields[self.ACCEPTOR_ADDRESS_LENGTH] = 0 + self.fields[self.APPLICATION_DATA_LENGTH] = 0 + self.fields[self.INITIATOR_ADDRESS] = b'' + self.fields[self.ACCEPTOR_ADDRESS] = b'' + self.fields[self.APPLICATION_DATA] = b'' + + def __setitem__(self, key, value): + self.fields[key] = value + + def get_data(self): + # Set the lengths of each len field in case they have changed + self.fields[self.INITIATOR_ADDRESS_LENGTH] = len(self.fields[self.INITIATOR_ADDRESS]) + self.fields[self.ACCEPTOR_ADDRESS_LENGTH] = len(self.fields[self.ACCEPTOR_ADDRESS]) + self.fields[self.APPLICATION_DATA_LENGTH] = len(self.fields[self.APPLICATION_DATA]) + + # Add all the values together to create the gss_channel_bindings_struct + data = struct.pack(' or . + +import hmac +import os +import struct +from ntlm_auth.compute_response import ComputeResponse +from ntlm_auth.constants import NegotiateFlags, MessageTypes, NTLM_SIGNATURE, AvFlags +from ntlm_auth.rc4 import ARC4 +from ntlm_auth.target_info import TargetInfo + +class NegotiateMessage(object): + EXPECTED_BODY_LENGTH = 40 + + """ + [MS-NLMP] v28.0 2016-07-14 + + 2.2.1.1 NEGOTIATE_MESSAGE + The NEGOTIATE_MESSAGE defines an NTLM Negotiate message that is sent from the client to + the server. This message allows the client to specify its supported NTLM options to + the server. + + :param negotiate_flags: A NEGOTIATE structure that contains a set of bit flags. These flags are the options the client supports + :param domain_name: The domain name of the user to authenticate with, default is None + :param workstation: The worksation of the client machine, default is None + + Attributes: + signature: An 8-byte character array that MUST contain the ASCII string 'NTLMSSP\0' + message_type: A 32-bit unsigned integer that indicates the message type. This field must be set to 0x00000001 + negotiate_flags: A NEGOTIATE structure that contains a set of bit flags. These flags are the options the client supports + version: Contains the windows version info of the client. It is used only debugging purposes and are only set when NTLMSSP_NEGOTIATE_VERSION flag is set + domain_name: A byte-array that contains the name of the client authentication domain that MUST Be encoded in the negotiated character set + workstation: A byte-array that contains the name of the client machine that MUST Be encoded in the negotiated character set + """ + def __init__(self, negotiate_flags, domain_name, workstation): + self.signature = NTLM_SIGNATURE + self.message_type = struct.pack(' or . + +import base64 +import socket +import struct +from ntlm_auth.constants import NegotiateFlags +from ntlm_auth.messages import NegotiateMessage, ChallengeMessage, AuthenticateMessage +from ntlm_auth.session_security import SessionSecurity + + +""" +utility functions for Microsoft NTLM authentication + +References: +[MS-NLMP]: NT LAN Manager (NTLM) Authentication Protocol Specification +http://download.microsoft.com/download/a/e/6/ae6e4142-aa58-45c6-8dcf-a657e5900cd3/%5BMS-NLMP%5D.pdf + +[MS-NTHT]: NTLM Over HTTP Protocol Specification +http://download.microsoft.com/download/a/e/6/ae6e4142-aa58-45c6-8dcf-a657e5900cd3/%5BMS-NTHT%5D.pdf + +Cntlm Authentication Proxy +http://cntlm.awk.cz/ + +NTLM Authorization Proxy Server +http://sourceforge.net/projects/ntlmaps/ + +Optimized Attack for NTLM2 Session Response +http://www.blackhat.com/presentations/bh-asia-04/bh-jp-04-pdfs/bh-jp-04-seki.pdf +""" + +class Ntlm(object): + """ + Initialises the NTLM context to use when sending and receiving messages to and from the server. You should be + using this object as it supports NTLMv2 authenticate and it easier to use than before. It also brings in the + ability to use signing and sealing with session_security and generate a MIC structure. + + :param ntlm_compatibility: The Lan Manager Compatibility Level to use withe the auth message - Default 3 + This is set by an Administrator in the registry key + 'HKLM\SYSTEM\CurrentControlSet\Control\Lsa\LmCompatibilityLevel' + The values correspond to the following; + 0 : LM and NTLMv1 + 1 : LM, NTLMv1 and NTLMv1 with Extended Session Security + 2 : NTLMv1 and NTLMv1 with Extended Session Security + 3-5 : NTLMv2 Only + Note: Values 3 to 5 are no different as the client supports the same types + + Attributes: + negotiate_flags: A NEGOTIATE structure that contains a set of bit flags. These flags are the options the client supports and are sent in the negotiate_message + ntlm_compatibility: The Lan Manager Compatibility Level, same as the input if supplied + negotiate_message: A NegotiateMessage object that is sent to the server + challenge_message: A ChallengeMessage object that has been created from the server response + authenticate_message: An AuthenticateMessage object that is sent to the server based on the ChallengeMessage + session_security: A SessionSecurity structure that can be used to sign and seal messages sent after the authentication challenge + """ + def __init__(self, ntlm_compatibility=3): + self.ntlm_compatibility = ntlm_compatibility + + # Setting up our flags so the challenge message returns the target info block if supported + self.negotiate_flags = NegotiateFlags.NTLMSSP_NEGOTIATE_TARGET_INFO | \ + NegotiateFlags.NTLMSSP_NEGOTIATE_128 | \ + NegotiateFlags.NTLMSSP_NEGOTIATE_56 | \ + NegotiateFlags.NTLMSSP_NEGOTIATE_UNICODE | \ + NegotiateFlags.NTLMSSP_NEGOTIATE_VERSION | \ + NegotiateFlags.NTLMSSP_NEGOTIATE_KEY_EXCH | \ + NegotiateFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN | \ + NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN | \ + NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL + + # Setting the message types based on the ntlm_compatibility level + self._set_ntlm_compatibility_flags(self.ntlm_compatibility) + + self.negotiate_message = None + self.challenge_message = None + self.authenticate_message = None + self.session_security = None + + + def create_negotiate_message(self, domain_name=None, workstation=None): + """ + Create an NTLM NEGOTIATE_MESSAGE + + :param domain_name: The domain name of the user account we are authenticating with, default is None + :param worksation: The workstation we are using to authenticate with, default is None + :return: A base64 encoded string of the NEGOTIATE_MESSAGE + """ + self.negotiate_message = NegotiateMessage(self.negotiate_flags, domain_name, workstation) + + return base64.b64encode(self.negotiate_message.get_data()) + + def parse_challenge_message(self, msg2): + """ + Parse the NTLM CHALLENGE_MESSAGE from the server and add it to the Ntlm context fields + + :param msg2: A base64 encoded string of the CHALLENGE_MESSAGE + """ + msg2 = base64.b64decode(msg2) + self.challenge_message = ChallengeMessage(msg2) + + def create_authenticate_message(self, user_name, password=None, nthash=None, lmhash=None, domain_name=None, workstation=None, server_certificate_hash=None): + """ + Create an NTLM AUTHENTICATE_MESSAGE based on the Ntlm context and the previous messages sent and received + + :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, default is None + :param workstation: The workstation we are using to authenticate with, default is None + :param server_certificate_hash: The SHA256 hash string of the server certificate (DER encoded) NTLM is authenticating to. Used for Channel + Binding Tokens. If nothing is supplied then the CBT hash will not be sent. See messages.py AuthenticateMessage + for more details + :return: A base64 encoded string of the AUTHENTICATE_MESSAGE + """ + self.authenticate_message = AuthenticateMessage(user_name, password, nthash, lmhash, domain_name, workstation, + self.challenge_message, self.ntlm_compatibility, + server_certificate_hash) + self.authenticate_message.add_mic(self.negotiate_message, self.challenge_message) + + # Setups up the session_security context used to sign and seal messages if wanted + if self.negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL or self.negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN: + self.session_security = SessionSecurity(struct.unpack("= 0) and (ntlm_compatibility <= 5): + if ntlm_compatibility == 0: + self.negotiate_flags |= NegotiateFlags.NTLMSSP_NEGOTIATE_NTLM | \ + NegotiateFlags.NTLMSSP_NEGOTIATE_LM_KEY + elif ntlm_compatibility == 1: + self.negotiate_flags |= NegotiateFlags.NTLMSSP_NEGOTIATE_NTLM | \ + NegotiateFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY + else: + self.negotiate_flags |= NegotiateFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY + else: + raise Exception("Unknown ntlm_compatibility level - expecting value between 0 and 5") diff --git a/ntlm_auth/rc4.py b/ntlm_auth/rc4.py new file mode 100644 index 0000000..d2fde52 --- /dev/null +++ b/ntlm_auth/rc4.py @@ -0,0 +1,51 @@ +# 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 or . + +class ARC4(object): + state = None + i = 0 + j = 0 + + def __init__(self, key): + # Split up the key into a list + if isinstance(key, str): + key = [ord(c) for c in key] + else: + key = [c for c in key] + + #Key-scheduling algorithm (KSA) + self.state = [n for n in range(256)] + j = 0 + for i in range(256): + j = (j + self.state[i] + key[i % len(key)]) % 256 + self.state[i], self.state[j] = self.state[j], self.state[i] + + def update(self, value): + chars = [] + random_gen = self._random_generator() + for char in value: + if isinstance(value, str): + byte = ord(char) + else: + byte = char + updated_byte = byte ^ next(random_gen) + chars.append(updated_byte) + return bytes(bytearray(chars)) + + def _random_generator(self): + #Pseudo-Random Generation Algorithm (PRGA) + while True: + self.i = (self.i + 1) % 256 + self.j = (self.j + self.state[self.i]) % 256 + self.state[self.i], self.state[self.j] = self.state[self.j], self.state[self.i] + yield self.state[(self.state[self.i] + self.state[self.j]) % 256] diff --git a/ntlm_auth/session_security.py b/ntlm_auth/session_security.py new file mode 100644 index 0000000..2ad6e6d --- /dev/null +++ b/ntlm_auth/session_security.py @@ -0,0 +1,250 @@ +# 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 or . + +import binascii +import hmac +import struct +import ntlm_auth.compute_keys as compkeys +from ntlm_auth.constants import NegotiateFlags, SignSealConstants +from ntlm_auth.rc4 import ARC4 + + +class _NtlmMessageSignature1(object): + EXPECTED_BODY_LENGTH = 16 + + """ + [MS-NLMP] v28.0 2016-07-14 + + 2.2.2.9.1 NTLMSSP_MESSAGE_SIGNATURE + This version of the NTLMSSP_MESSAGE_SIGNATURE structure MUST be used when the + NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY flag is not negotiated. + + :param random_pad: A 4-byte array that contains the random pad for the emssage + :param checksum: A 4-byte array that contains the checksum for the message + :param seq_num: A 32-bit unsigned integer that contains the NTLM sequence number for this application message + """ + def __init__(self, random_pad, checksum, seq_num): + self.version = struct.pack(" 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__end + except AttributeError: + self.clear() + self.update(*args, **kwds) + + def clear(self): + self.__end = end = [] + end += [None, end, end] # sentinel node for doubly linked list + self.__map = {} # key --> [key, prev, next] + dict.clear(self) + + def __setitem__(self, key, value): + if key not in self: + end = self.__end + curr = end[1] + curr[2] = end[1] = self.__map[key] = [key, curr, end] + dict.__setitem__(self, key, value) + + def __delitem__(self, key): + dict.__delitem__(self, key) + key, prev, next = self.__map.pop(key) + prev[2] = next + next[1] = prev + + def __iter__(self): + end = self.__end + curr = end[2] + while curr is not end: + yield curr[0] + curr = curr[2] + + def __reversed__(self): + end = self.__end + curr = end[1] + while curr is not end: + yield curr[0] + curr = curr[1] + + def popitem(self, last=True): + if not self: + raise KeyError('dictionary is empty') + if last: + key = reversed(self).next() + else: + key = iter(self).next() + value = self.pop(key) + return key, value + + def __reduce__(self): + items = [[k, self[k]] for k in self] + tmp = self.__map, self.__end + del self.__map, self.__end + inst_dict = vars(self).copy() + self.__map, self.__end = tmp + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def keys(self): + return list(self) + + setdefault = DictMixin.setdefault + update = DictMixin.update + pop = DictMixin.pop + values = DictMixin.values + items = DictMixin.items + iterkeys = DictMixin.iterkeys + itervalues = DictMixin.itervalues + iteritems = DictMixin.iteritems + + def __repr__(self): + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + + def copy(self): + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + if isinstance(other, OrderedDict): + if len(self) != len(other): + return False + for p, q in zip(self.items(), other.items()): + if p != q: + return False + return True + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other diff --git a/relay.py b/relay.py new file mode 100644 index 0000000..ab19fe6 --- /dev/null +++ b/relay.py @@ -0,0 +1,56 @@ +import time +import socket + + +buffer_size = 4096 +delay = 0.0001 +socks_server_reply_success = '\x00\x5a\xff\xff\xff\xff\xff\xff' +socks_server_reply_fail = '\x00\x5b\xff\xff\xff\xff\xff\xff' +relay_timeout = 60 +banner = 'RPIVOT' +banner_response = 'TUNNELRDY' + +COMMAND_CHANNEL = 0 + +CHANNEL_CLOSE_CMD = '\xcc' +CHANNEL_OPEN_CMD = '\xdd' +FORWARD_CONNECTION_SUCCESS = '\xee' +FORWARD_CONNECTION_FAILURE = '\xff' +CLOSE_RELAY = '\xc4' +PING_CMD = '\x70' + +cmd_names = { + '\xcc': 'CHANNEL_CLOSE_CMD', + '\xdd': 'CHANNEL_OPEN_CMD', + '\xee': 'FORWARD_CONNECTION_SUCCESS', + '\xff': 'FORWARD_CONNECTION_FAILURE', + '\xc4': 'CLOSE_RELAY', + '\x70': 'PING_CMD' +} + + +class ClosedSocket(Exception): + pass + + +class RelayError(Exception): + pass + + +def recvall(sock, data_len): + buf = '' + while True: + buf += sock.recv(data_len - len(buf)) + if len(buf) == data_len: + break + time.sleep(delay) + assert(data_len == len(buf)) + return buf + + +def close_sockets(sockets): + for s in sockets: + try: + s.close() + except socket.error: + pass diff --git a/server.py b/server.py new file mode 100644 index 0000000..d22dbc5 --- /dev/null +++ b/server.py @@ -0,0 +1,425 @@ +import logging +import logging.handlers +import socket +import select +import sys +import time +from struct import pack, unpack +import struct +import random +import errno +import relay +import threading +import optparse + +class RelayServer: + def __init__(self, host, port, socket_with_server): + self.input_list = [] + self.channel = {} + self.last_ping_time = time.time() + self.id_by_socket = {} + self.pending_socks_clients = [] + self.socket_with_server = socket_with_server + self.input_list.append(self.socket_with_server) + self.remote_side_down = False + + logger.debug('Starting ping thread') + + self.ping_thread = threading.Thread(target=self.ping_worker) + self.ping_thread.start() + + self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + try: + self.server.bind((host, port)) + self.server.listen(2000) + except socket.error as (code, msg): + logger.error('Error binding socks proxy. {0}'.format(msg)) + logger.error('Closing relay') + socket_with_server.close() + raise + self.socks_client_socket = None + + def ping_worker(self): + while True: + time.sleep(10) + current_time = time.time() + if self.remote_side_down: + logger.debug('Remote side down. Ping worker exiting') + return + if current_time - self.last_ping_time > relay.relay_timeout: + logger.info('No response from remote side for {0} seconds. Restarting relay'.format(relay.relay_timeout)) + self.socket_with_server.close() + return + logger.debug('Sending ping') + try: + self.send_remote_cmd(self.socket_with_server, relay.PING_CMD) + except socket.error as (code, msg): + logger.debug('Ping thread got socket exception {0} {1}. Closing socket with remote side'.format(code, msg)) + self.socket_with_server.close() + return + except relay.RelayError: + logger.debug('Ping worker caught RelayError. Exiting') + self.shutdown() + return + + def shutdown(self): + relay.close_sockets(self.input_list) + self.remote_side_down = True + + + def main_loop(self): + self.input_list.append(self.server) + while True: + time.sleep(relay.delay) + + try: + logger.debug("Active channels: {0}".format(self.channel.keys())) + inputready, outputready, exceptready = select.select(self.input_list, [], []) + except socket.error as (code, msg): + logger.debug('Socket error on select. Errno: {0} Msg: {1}'.format(errno.errorcode[code], msg)) + return + except KeyboardInterrupt: + logger.info('SIGINT received. Closing relay and exiting') + self.shutdown() + sys.exit(1) + for self.selected_input_socket in inputready: + if self.selected_input_socket == self.server: + self.on_accept() + break + + if self.selected_input_socket == self.socket_with_server: + try: + self.manage_remote_socket(self.selected_input_socket) + except relay.RelayError: + logger.debug('Main loop: got RelayError. Closing connection with remote side and exiting loop') + self.shutdown() + return + elif self.selected_input_socket in self.pending_socks_clients: + self.pending_socks_clients.remove(self.selected_input_socket) + try: + ip, port = self.handle_new_socks_connection(self.selected_input_socket) + except relay.RelayError: + logger.debug("Closing socks client socket {0}".format(self.selected_input_socket)) + self.input_list.remove(self.selected_input_socket) + self.selected_input_socket.close() + continue + #self.input_list.append(self.selected_input_socket) + new_channel_id = self.set_channel(self.selected_input_socket) + logger.debug("Sending command to open channel {0}".format(new_channel_id)) + self.send_remote_cmd(self.socket_with_server, relay.CHANNEL_OPEN_CMD, new_channel_id, ip, port) + + + elif self.selected_input_socket in self.id_by_socket: + self.manage_socks_client_socket(self.selected_input_socket) + else: + logger.debug("Active socket {0} does not belong to channel. Closing it".format(self.selected_input_socket)) + self.selected_input_socket.close() + + + def parse_socks_header(self, data): + try: + (vn, cd, dstport, dstip) = unpack('>BBHI', data[:8]) + except struct.error: + logger.debug('Invalid socks header! Got data: {0}'.format(repr(data))) + raise relay.RelayError + if vn != 4: + logger.debug('Invalid socks header! Got data: {0}'.format(repr(data))) + raise relay.RelayError + str_ip = socket.inet_ntoa(pack(">L", dstip)) + logger.debug('Parsing socks header. Socks version: {0} Socks command: {1} Dstport: {2} Dstip: {3}'.format(vn, cd, dstport, str_ip)) + return str_ip, dstport + + def get_channel_data(self, sock): + try: + tlv_header = relay.recvall(sock, 4) + channel_id, tlv_data_len = unpack('= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + raise tp, value, tb +""") + + +if sys.version_info[:2] == (3, 2): + exec_("""def raise_from(value, from_value): + if from_value is None: + raise value + raise value from from_value +""") +elif sys.version_info[:2] > (3, 2): + exec_("""def raise_from(value, from_value): + raise value from from_value +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + return wrapper +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer)