text: #!/usr/bin/env python
  # Copyright (c) 2003-2016 CORE Security Technologies
  #
  # This software is provided under under a slightly modified version
  # of the Apache Software License. See the accompanying LICENSE file
  # for more information.
  #
  # Author:
  #   Dirk-jan Mollema / Fox-IT (https://www.fox-it.com)
  #
  # Description: 
  # MSSQL client for relaying NTLMSSP authentication to MSSQL servers
  #
  import logging
  import random
  import string
  
  from impacket import tds
  from impacket.tds import DummyPrint, TDS_ENCRYPT_REQ, TDS_ENCRYPT_OFF, TDS_PRE_LOGIN, TDS_LOGIN, TDS_INIT_LANG_FATAL, \
      TDS_ODBC_ON, TDS_INTEGRATED_SECURITY_ON, TDS_LOGIN7, TDS_SSPI, TDS_LOGINACK_TOKEN
  
  try:
      import OpenSSL
      from OpenSSL import SSL, crypto
  except Exception:
      logging.critical("pyOpenSSL is not installed, can't continue")
  
  class MSSQLRelayClient(tds.MSSQL):
      def __init__(self, address, port=1433, rowsPrinter=DummyPrint()):
          tds.MSSQL.__init__(self,address, port, rowsPrinter)
          self.resp = None
  
      def init_connection(self):
          self.connect()
          #This is copied from tds.py
          resp = self.preLogin()
          if resp['Encryption'] == TDS_ENCRYPT_REQ or resp['Encryption'] == TDS_ENCRYPT_OFF:
              logging.info("Encryption required, switching to TLS")
  
              # Switching to TLS now
              ctx = SSL.Context(SSL.TLSv1_METHOD)
              ctx.set_cipher_list('RC4, AES256')
              tls = SSL.Connection(ctx,None)
              tls.set_connect_state()
              while True:
                  try:
                      tls.do_handshake()
                  except SSL.WantReadError:
                      data = tls.bio_read(4096)
                      self.sendTDS(TDS_PRE_LOGIN, data,0)
                      tds = self.recvTDS()
                      tls.bio_write(tds['Data'])
                  else:
                      break
  
              # SSL and TLS limitation: Secure Socket Layer (SSL) and its replacement, 
              # Transport Layer Security(TLS), limit data fragments to 16k in size.
              self.packetSize = 16*1024-1
              self.tlsSocket = tls
          self.resp = resp
  
      def sendNegotiate(self,negotiateMessage):
          self.init_connection()
          #Also partly copied from tds.py
          login = TDS_LOGIN()
  
          login['HostName'] = (''.join([random.choice(string.letters) for _ in range(8)])).encode('utf-16le')
          login['AppName']  = (''.join([random.choice(string.letters) for _ in range(8)])).encode('utf-16le')
          login['ServerName'] = self.server.encode('utf-16le')
          login['CltIntName']  = login['AppName']
          login['ClientPID'] = random.randint(0,1024)
          login['PacketSize'] = self.packetSize
          login['OptionFlags2'] = TDS_INIT_LANG_FATAL | TDS_ODBC_ON    
          login['OptionFlags2'] |= TDS_INTEGRATED_SECURITY_ON
          # NTLMSSP Negotiate
          auth = negotiateMessage
          login['SSPI'] = str(auth)
  
          login['Length'] = len(str(login))
  
          # Send the NTLMSSP Negotiate
          self.sendTDS(TDS_LOGIN7, str(login))
  
          # According to the spects, if encryption is not required, we must encrypt just 
          # the first Login packet :-o 
          if self.resp['Encryption'] == TDS_ENCRYPT_OFF:
              self.tlsSocket = None
  
          tds = self.recvTDS()
          return tds['Data'][3:]
  
      def sendAuth(self,authenticateMessageBlob, serverChallenge=None):
          #Also partly copied from tds.py
          self.sendTDS(TDS_SSPI, str(authenticateMessageBlob))
          tds = self.recvTDS()
          self.replies = self.parseReply(tds['Data'])
          if self.replies.has_key(TDS_LOGINACK_TOKEN):
              #Once we are here, there is a full connection and we can
              #do whatever the current user has rights to do
              return True
          else:
              return False
  
      #SMB Relay server needs this
      @staticmethod
      def get_encryption_key():
          return None