diff --git a/readme/CHANGELOG b/readme/CHANGELOG index 142ed749b..3ba53f590 100644 --- a/readme/CHANGELOG +++ b/readme/CHANGELOG @@ -5,7 +5,10 @@ version 7.3.4 * fixed an issue with mssql bruter that would cause it to fail over to nmap scans even if host wasn't valid * fixed an issue that would cause UDP to not work properly when scanning subnet ranges * improved handling and descriptions in mssql -* fixed error in mssql bruter +* fixed error in mssql bruter error handling exception +* fixed an issue that would cause TDS to error out when directly connecting to MSSQL server +* removed impacket TDS from src.core and added impacket.tds +* updated requirements.txt for impacket ~~~~~~~~~~~~~~~~ version 7.3.3 diff --git a/requirements.txt b/requirements.txt index 67de274d1..667faa448 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ pycrypto requests pyopenssl pefile - +impacket # Generate QR Codes qrcode -pillow \ No newline at end of file +pillow diff --git a/src/core/tds.py b/src/core/tds.py deleted file mode 100644 index 025367355..000000000 --- a/src/core/tds.py +++ /dev/null @@ -1,1617 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2003-2012 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. -# -# $Id: tds.py 632 2012-07-26 22:18:33Z bethus@gmail.com $ -# -# Description: [MS-TDS] & [MC-SQLR] implementation. -# -# ToDo: -# [ ] Add all the tokens left -# [ ] parseRow should be rewritten and add support for all the SQL types in a -# good way. Right now it just supports a few types. -# [ ] printRows is crappy, just an easy way to print the rows. It should be -# rewritten to output like a normal SQL client -# -# Author: -# Alberto Solino (beto@coresecurity.com) -# - - -from impacket import ntlm, uuid -from impacket.structure import Structure -import random -import string -import struct -import socket, select -import random -import binascii -import math -import datetime - - -# MC-SQLR Constants and Structures -SQLR_PORT = 1434 -SQLR_CLNT_BCAST_EX = 0x02 -SQLR_CLNT_UCAST_EX = 0x03 -SQLR_CLNT_UCAST_INST= 0x04 -SQLR_CLNT_UCAST_DAC = 0x0f - - -class SQLR(Structure): - commonHdr = ( - ('OpCode','B'), - ) - -class SQLR_UCAST_INST(SQLR): - structure = ( - ('Instance',':') - ) - def __init__(self, data = None): - SQLR.__init__(self,data) - if data is not None: - self['OpCode'] = SQLR_CLNT_UCAST_INST - -class SQLR_UCAST_DAC(SQLR): - structure = ( - ('Protocol', 'B=1'), - ('Instance', ':'), - ) - def __init__(self, data = None): - SQLR.__init__(self,data) - if data is not None: - self['OpCode'] = SQLR_CLNT_UCAST_DAC - -class SQLR_Response(SQLR): - structure = ( - ('Size','H=8+len(Data)'), - ('SPID','>H=0'), - ('PacketID','B=0'), - ('VersionOffset','>H'), - ('VersionLength','>H=len(self["Version"])'), - ('EncryptionToken','>B=0x1'), - ('EncryptionOffset','>H'), - ('EncryptionLength','>H=1'), - ('InstanceToken','>B=2'), - ('InstanceOffset','>H'), - ('InstanceLength','>H=len(self["Instance"])'), - ('ThreadIDToken','>B=3'), - ('ThreadIDOffset','>H'), - ('ThreadIDLength','>H=4'), - ('EndToken','>B=0xff'), - ('_Version','_-Version','self["VersionLength"]'), - ('Version',':'), - ('Encryption','B'), - ('_Instance','_-Instance','self["InstanceLength"]-1'), - ('Instance',':'), - ('ThreadID',':'), - ) - - def __str__(self): - self['VersionOffset']=21 - self['EncryptionOffset']=self['VersionOffset'] + len(self['Version']) - self['InstanceOffset']=self['EncryptionOffset'] + 1 - self['ThreadIDOffset']=self['InstanceOffset'] + len(self['Instance']) - return Structure.__str__(self) - -class TDS_LOGIN(Structure): - structure = ( - ('Length','L=0x71'), - ('PacketSize','>L=32766'), - ('ClientProgVer','>L=7'), - ('ClientPID','> 4) ^ 0xa5) , password)) - - def connect(self): - af, socktype, proto, canonname, sa = socket.getaddrinfo(self.server, self.port, 0, socket.SOCK_STREAM)[0] - sock = socket.socket(af, socktype, proto) - sock.connect(sa) - self.socket = sock - return sock - - def disconnect(self): - return self.socket.close() - - def setPacketSize(self, packetSize): - self.packetSize = packetSize - - def getPacketSize(self): - return self.packetSize - - def sendTDS(self, packetType, data, packetID = 1): - if (len(data)-8) > self.packetSize: - remaining = data[self.packetSize-8:] - tds = TDSPacket() - tds['Type'] = packetType - tds['Status'] = TDS_STATUS_NORMAL - tds['PacketID'] = packetID - tds['Data'] = data[:self.packetSize-8] - self.socket.sendall(str(tds)) - while len(remaining) > (self.packetSize-8): - packetID += 1 - tds['PacketID'] = packetID - tds['Data'] = remaining[:self.packetSize-8] - self.socket.sendall(str(tds)) - remaining = remaining[self.packetSize-8:] - data = remaining - packetID+=1 - - tds = TDSPacket() - tds['Type'] = packetType - tds['Status'] = TDS_STATUS_EOM - tds['PacketID'] = packetID - tds['Data'] = data - self.socket.sendall(str(tds)) - - def recvTDS(self, packetSize = None): - # Do reassembly here - if packetSize is None: - packetSize = self.packetSize - packet = TDSPacket(self.socket.recv(packetSize)) - status = packet['Status'] - packetLen = packet['Length']-8 - while packetLen > len(packet['Data']): - data = self.socket.recv(packetSize) - packet['Data'] += data - - remaining = None - if packetLen < len(packet['Data']): - remaining = packet['Data'][packetLen:] - packet['Data'] = packet['Data'][:packetLen] - - #print "REMAINING ", - #if remaining is None: - # print None - #else: - # print len(remaining) - - while status != TDS_STATUS_EOM: - if remaining is not None: - tmpPacket = TDSPacket(remaining) - remaining = None - else: - tmpPacket = TDSPacket(self.socket.recv(packetSize)) - - packetLen = tmpPacket['Length'] - 8 - while packetLen > len(tmpPacket['Data']): - data = self.socket.recv(packetSize) - tmpPacket['Data'] += data - - remaining = None - if packetLen < len(tmpPacket['Data']): - remaining = tmpPacket['Data'][packetLen:] - tmpPacket['Data'] = tmpPacket['Data'][:packetLen] - - status = tmpPacket['Status'] - packet['Data'] += tmpPacket['Data'] - packet['Length'] += tmpPacket['Length'] - 8 - - #print packet['Length'] - return packet - - def login(self, database, username, password='', domain='', hashes = None, useWindowsAuth = False): - - if hashes is not None: - lmhash, nthash = hashes.split(':') - lmhash = binascii.a2b_hex(lmhash) - nthash = binascii.a2b_hex(nthash) - else: - lmhash = '' - nthash = '' - - resp = self.preLogin() - - # Test this! - if resp['Encryption'] != TDS_ENCRYPT_NOT_SUP: - print "Encryption not supported" - - login = TDS_LOGIN() - - login['HostName'] = (''.join([random.choice(string.letters) for i in range(8)])).encode('utf-16le') - login['AppName'] = (''.join([random.choice(string.letters) for i in range(8)])).encode('utf-16le') - login['ServerName'] = self.server.encode('utf-16le') - login['CltIntName'] = login['AppName'] - login['ClientPID'] = random.randint(0,1024) - if database is not None: - login['Database'] = database.encode('utf-16le') - login['OptionFlags2'] = TDS_INIT_LANG_FATAL | TDS_ODBC_ON - - if useWindowsAuth is True: - login['OptionFlags2'] |= TDS_INTEGRATED_SECURITY_ON - # NTLMSSP Negotiate - auth = ntlm.getNTLMSSPType1('WORKSTATION','') - login['SSPI'] = str(auth) - else: - login['UserName'] = username.encode('utf-16le') - login['Password'] = self.encryptPassword(password.encode('utf-16le')) - login['SSPI'] = '' - - login['Length'] = len(str(login)) - - self.sendTDS(TDS_LOGIN7, str(login)) - # Send the NTLMSSP Negotiate or SQL Auth Packet - tds = self.recvTDS() - - if useWindowsAuth is True: - serverChallenge = tds['Data'][3:] - - # Generate the NTLM ChallengeResponse AUTH - type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, serverChallenge, username, password, domain, lmhash, nthash) - - self.sendTDS(TDS_SSPI, str(type3)) - tds = self.recvTDS() - - self.replies = self.parseReply(tds['Data']) - - if self.replies.has_key(TDS_LOGINACK_TOKEN): - return True - else: - return False - - def processColMeta(self): - for col in self.colMeta: - if col['Type'] in [TDS_NVARCHARTYPE, TDS_NCHARTYPE, TDS_NTEXTTYPE]: - col['Length'] = col['TypeData']/2 - fmt = '%%-%ds' - elif col['Type'] in [TDS_GUIDTYPE]: - col['Length'] = 36 - fmt = '%%%ds' - elif col['Type'] in [TDS_DECIMALNTYPE,TDS_NUMERICNTYPE]: - col['Length'] = ord(col['TypeData'][0]) - fmt = '%%%ds' - elif col['Type'] in [TDS_DATETIMNTYPE]: - col['Length'] = 19 - fmt = '%%-%ds' - elif col['Type'] in [TDS_INT4TYPE, TDS_INTNTYPE]: - col['Length'] = 11 - fmt = '%%%ds' - elif col['Type'] in [TDS_FLTNTYPE, TDS_MONEYNTYPE]: - col['Length'] = 25 - fmt = '%%%ds' - elif col['Type'] in [TDS_BITNTYPE, TDS_BIGCHARTYPE]: - col['Length'] = col['TypeData'] - fmt = '%%%ds' - elif col['Type'] in [TDS_BIGBINARYTYPE, TDS_BIGVARBINTYPE]: - col['Length'] = col['TypeData'] * 2 - fmt = '%%%ds' - elif col['Type'] in [TDS_TEXTTYPE, TDS_BIGVARCHRTYPE]: - col['Length'] = col['TypeData'] - fmt = '%%-%ds' - else: - col['Length'] = 10 - fmt = '%%%ds' - - if len(col['Name']) > col['Length']: - col['Length'] = len(col['Name']) - elif col['Length'] > self.MAX_COL_LEN: - col['Length'] = self.MAX_COL_LEN - - col['Format'] = fmt % col['Length'] - - - def printColumnsHeader(self): - if len(self.colMeta) == 0: - return - for col in self.colMeta: - print col['Format'] % col['Name'] + self.COL_SEPARATOR, - print '' - for col in self.colMeta: - print '-'*col['Length'] + self.COL_SEPARATOR, - print '' - - def printRows(self): - if self.lastError is True: - return - self.processColMeta() - self.printColumnsHeader() - for row in self.rows: - for col in self.colMeta: - print col['Format'] % row[col['Name']] + self.COL_SEPARATOR, - print '' - - - def printReplies(self): - for keys in self.replies.keys(): - for i, key in enumerate(self.replies[keys]): - if key['TokenType'] == TDS_ERROR_TOKEN: - print "[!] ERROR(%s): Line %d: %s" % (key['ServerName'].decode('utf-16le'), key['LineNumber'], key['MsgText'].decode('utf-16le')) - self.lastError = True - - elif key['TokenType'] == TDS_INFO_TOKEN: - print "[*] INFO(%s): Line %d: %s" % (key['ServerName'].decode('utf-16le'), key['LineNumber'], key['MsgText'].decode('utf-16le')) - - elif key['TokenType'] == TDS_LOGINACK_TOKEN: - print "[*] ACK: Result: %s - %s (%d%d %d%d) " % (key['Interface'], key['ProgName'].decode('utf-16le'), key['MajorVer'], key['MinorVer'], key['BuildNumHi'], key['BuildNumLow']) - - elif key['TokenType'] == TDS_ENVCHANGE_TOKEN: - if key['Type'] in (TDS_ENVCHANGE_DATABASE, TDS_ENVCHANGE_LANGUAGE, TDS_ENVCHANGE_CHARSET, TDS_ENVCHANGE_PACKETSIZE): - record = TDS_ENVCHANGE_VARCHAR(key['Data']) - if record['OldValue'] == '': - record['OldValue'] = 'None'.encode('utf-16le') - elif record['NewValue'] == '': - record['NewValue'] = 'None'.encode('utf-16le') - if key['Type'] == TDS_ENVCHANGE_DATABASE: - type = 'DATABASE' - elif key['Type'] == TDS_ENVCHANGE_LANGUAGE: - type = 'LANGUAGE' - elif key['Type'] == TDS_ENVCHANGE_CHARSET: - type = 'CHARSET' - elif key['Type'] == TDS_ENVCHANGE_PACKETSIZE: - type = 'PACKETSIZE' - else: - type = "%d" % key['Type'] - - print "[*] ENVCHANGE(%s): Old Value: %s, New Value: %s" % (type,record['OldValue'].decode('utf-16le'), record['NewValue'].decode('utf-16le')) - - def parseRow(self,token): - # TODO: This REALLY needs to be improved. Right now we don't support correctly all the data types - # help would be appreciated ;) - if len(token) == 1: - return 0 - row = {} - origDataLen = len(token['Data']) - data = token['Data'] - for col in self.colMeta: - type = col['Type'] - if (type == TDS_NVARCHARTYPE) |\ - (type == TDS_NCHARTYPE): - #print "NVAR 0x%x" % type - charLen = struct.unpack(' 0: - uu = data[:uuidLen] - value = uuid.bin_to_string(uu) - data = data[uuidLen:] - else: - value = 'NULL' - - elif (type == TDS_NTEXTTYPE) |\ - (type == TDS_IMAGETYPE) : - # Skip the pointer data - charLen = ord(data[0]) - if charLen == 0: - value = 'NULL' - data = data[1:] - else: - data = data[1+charLen+8:] - charLen = struct.unpack(' 0: - value = struct.unpack(fmt,data[:valueSize])[0] - data = data[valueSize:] - else: - value = 'NULL' - - elif type == TDS_MONEYNTYPE: - valueSize = ord(data[:1]) - if valueSize == 4: - fmt = ' 0: - value = struct.unpack(fmt,data[:valueSize])[0] - if valueSize == 4: - value = float(value) / math.pow(10,4) - else: - value = float(value >> 32) / math.pow(10,4) - data = data[valueSize:] - else: - value = 'NULL' - - - elif type == TDS_BIGCHARTYPE: - #print "BIGC" - charLen = struct.unpack(' 0: - dateBytes = data[:valueSize] - dateValue = struct.unpack(' 0: - isPositiveSign = ord(value[0]) - if (valueLen-1) == 2: - fmt = ' 0: - if valueSize == 1: - value = ord(data[:valueSize]) - else: - value = data[:valueSize] - else: - value = 'NULL' - data = data[valueSize:] - - elif (type == TDS_INTNTYPE): - valueSize = ord(data[:1]) - if valueSize == 1: - fmt = ' 0: - value = struct.unpack(fmt,data[:valueSize])[0] - data = data[valueSize:] - else: - value = 'NULL' - elif (type == TDS_SSVARIANTTYPE): - print "ParseRow: SQL Variant type not yet supported :(" - raise - else: - print "ParseROW: Unsupported data type: 0%x" % type - raise - row[col['Name']] = value - - - self.rows.append(row) - - return (origDataLen - len(data)) - - def parseColMetaData(self, token): - # TODO Add support for more data types! - count = token['Count'] - if count == 0xFFFF: - return 0 - - self.colMeta = [] - origDataLen = len(token['Data']) - data = token['Data'] - for i in range(count): - column = {} - userType = struct.unpack(' 0: - tokenID = struct.unpack('B',tokens[0])[0] - if tokenID == TDS_ERROR_TOKEN: - token = TDS_INFO_ERROR(tokens) - elif tokenID == TDS_RETURNSTATUS_TOKEN: - token = TDS_RETURNSTATUS(tokens) - elif tokenID == TDS_INFO_TOKEN: - token = TDS_INFO_ERROR(tokens) - elif tokenID == TDS_LOGINACK_TOKEN: - token = TDS_LOGIN_ACK(tokens) - elif tokenID == TDS_ENVCHANGE_TOKEN: - token = TDS_ENVCHANGE(tokens) - if token['Type'] is TDS_ENVCHANGE_PACKETSIZE: - record = TDS_ENVCHANGE_VARCHAR(token['Data']) - self.packetSize = string.atoi( record['NewValue'].decode('utf-16le') ) - elif token['Type'] is TDS_ENVCHANGE_DATABASE: - record = TDS_ENVCHANGE_VARCHAR(token['Data']) - self.currentDB = record['NewValue'].decode('utf-16le') - - elif (tokenID == TDS_DONEINPROC_TOKEN) |\ - (tokenID == TDS_DONEPROC_TOKEN): - token = TDS_DONEINPROC(tokens) - elif tokenID == TDS_ORDER_TOKEN: - token = TDS_ORDER(tokens) - elif tokenID == TDS_ROW_TOKEN: - #print "ROW" - token = TDS_ROW(tokens) - tokenLen = self.parseRow(token) - token['Data'] = token['Data'][:tokenLen] - elif tokenID == TDS_COLMETADATA_TOKEN: - #print "COLMETA" - token = TDS_COLMETADATA(tokens) - tokenLen = self.parseColMetaData(token) - token['Data'] = token['Data'][:tokenLen] - elif tokenID == TDS_DONE_TOKEN: - token = TDS_DONE(tokens) - else: - print "Unknown Token %x" % tokenID - return replies - - if replies.has_key(tokenID) is not True: - replies[tokenID] = list() - - replies[tokenID].append(token) - tokens = tokens[len(token):] - #print "TYPE 0x%x, LEN: %d" %(tokenID, len(token)) - #print repr(tokens[:10]) - - return replies - - def batch(self, cmd): - # First of all we clear the rows, colMeta and lastError - self.rows = [] - self.colMeta = [] - self.lastError = False - self.sendTDS(TDS_SQL_BATCH, (cmd+'\r\n').encode('utf-16le')) - tds = self.recvTDS() - self.replies = self.parseReply(tds['Data']) - return self.rows - - # Handy alias - sql_query = batch - - def changeDB(self, db): - if db != self.currentDB: - self.batch('use %s' % db) - self.printReplies() - - def RunSQL(self,db,sql_query, **kwArgs): - self.changeDB(db) - self.printReplies() - ret = self.batch(sql_query) - self.printReplies() - - return ret#!/usr/bin/env python -# Copyright (c) 2003-2012 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. -# -# $Id: tds.py 632 2012-07-26 22:18:33Z bethus@gmail.com $ -# -# Description: [MS-TDS] & [MC-SQLR] implementation. -# -# ToDo: -# [ ] Add all the tokens left -# [ ] parseRow should be rewritten and add support for all the SQL types in a -# good way. Right now it just supports a few types. -# [ ] printRows is crappy, just an easy way to print the rows. It should be -# rewritten to output like a normal SQL client -# -# Author: -# Alberto Solino (beto@coresecurity.com) -# - - -from impacket import ntlm, uuid -from impacket.structure import Structure -import random -import string -import struct -import socket, select -import random -import binascii -import math -import datetime - - -# MC-SQLR Constants and Structures -SQLR_PORT = 1434 -SQLR_CLNT_BCAST_EX = 0x02 -SQLR_CLNT_UCAST_EX = 0x03 -SQLR_CLNT_UCAST_INST= 0x04 -SQLR_CLNT_UCAST_DAC = 0x0f - - -class SQLR(Structure): - commonHdr = ( - ('OpCode','B'), - ) - -class SQLR_UCAST_INST(SQLR): - structure = ( - ('Instance',':') - ) - def __init__(self, data = None): - SQLR.__init__(self,data) - if data is not None: - self['OpCode'] = SQLR_CLNT_UCAST_INST - -class SQLR_UCAST_DAC(SQLR): - structure = ( - ('Protocol', 'B=1'), - ('Instance', ':'), - ) - def __init__(self, data = None): - SQLR.__init__(self,data) - if data is not None: - self['OpCode'] = SQLR_CLNT_UCAST_DAC - -class SQLR_Response(SQLR): - structure = ( - ('Size','H=8+len(Data)'), - ('SPID','>H=0'), - ('PacketID','B=0'), - ('VersionOffset','>H'), - ('VersionLength','>H=len(self["Version"])'), - ('EncryptionToken','>B=0x1'), - ('EncryptionOffset','>H'), - ('EncryptionLength','>H=1'), - ('InstanceToken','>B=2'), - ('InstanceOffset','>H'), - ('InstanceLength','>H=len(self["Instance"])'), - ('ThreadIDToken','>B=3'), - ('ThreadIDOffset','>H'), - ('ThreadIDLength','>H=4'), - ('EndToken','>B=0xff'), - ('_Version','_-Version','self["VersionLength"]'), - ('Version',':'), - ('Encryption','B'), - ('_Instance','_-Instance','self["InstanceLength"]-1'), - ('Instance',':'), - ('ThreadID',':'), - ) - - def __str__(self): - self['VersionOffset']=21 - self['EncryptionOffset']=self['VersionOffset'] + len(self['Version']) - self['InstanceOffset']=self['EncryptionOffset'] + 1 - self['ThreadIDOffset']=self['InstanceOffset'] + len(self['Instance']) - return Structure.__str__(self) - -class TDS_LOGIN(Structure): - structure = ( - ('Length','L=0x71'), - ('PacketSize','>L=32766'), - ('ClientProgVer','>L=7'), - ('ClientPID','