mirror of
https://github.com/fortra/impacket
synced 2026-06-08 14:15:13 +00:00
76ee87746d
* Add fake CBT value for NXC * Add fake CBT value for NXC * Add checkMSSQLstatus.py * Fixed required modifications --------- Co-authored-by: Aurélien CHALOT <aurelien.chalot@orangecyberdefense.com>
156 lines
5.4 KiB
Python
156 lines
5.4 KiB
Python
#!/usr/bin/env python3
|
|
# coding: utf-8
|
|
"""
|
|
mssql_cbt_check.py
|
|
Check whether Channel Binding Token (CBT) is enforced on a MSSQL server.
|
|
|
|
Usage:
|
|
mssql_cbt_check.py [domain/]username[:password]@target [-port PORT] [-debug]
|
|
|
|
Writen by @Defte_
|
|
"""
|
|
from __future__ import print_function
|
|
|
|
import sys
|
|
import logging
|
|
import argparse
|
|
from getpass import getpass
|
|
|
|
from impacket import version
|
|
from impacket.examples import logger
|
|
from impacket.examples.utils import parse_target
|
|
from impacket.tds import MSSQL, TDS_ENCRYPT_REQ, TDS_ENCRYPT_OFF
|
|
|
|
|
|
class MSQLCBTCheck:
|
|
def __init__(self, options, username, password, domain, target):
|
|
self.username = username
|
|
self.password = password
|
|
self.domain = domain
|
|
self.target = target
|
|
self.port = int(options.port)
|
|
self.options = options
|
|
|
|
def _new_conn(self):
|
|
conn = MSSQL(self.target, self.port, "")
|
|
conn.connect()
|
|
return conn
|
|
|
|
def _login(self, conn, cbt):
|
|
opts = self.options
|
|
if opts.k:
|
|
return conn.kerberosLogin(
|
|
None,
|
|
self.username,
|
|
self.password,
|
|
self.domain,
|
|
opts.hashes,
|
|
opts.aesKey,
|
|
opts.dc_ip,
|
|
None,
|
|
None,
|
|
useCache=True,
|
|
cbt_fake_value=cbt,
|
|
)
|
|
else:
|
|
return conn.login(
|
|
None,
|
|
self.username,
|
|
self.password,
|
|
self.domain,
|
|
opts.hashes,
|
|
useWindowsAuth=True,
|
|
cbt_fake_value=cbt,
|
|
)
|
|
|
|
def run(self):
|
|
print(f"[*] Checking Channel Binding status on: {self.target}:{self.port}")
|
|
|
|
try:
|
|
conn = self._new_conn()
|
|
prelogin_resp = conn.preLogin()
|
|
enc = prelogin_resp["Encryption"]
|
|
if not enc == TDS_ENCRYPT_REQ and not enc == TDS_ENCRYPT_OFF:
|
|
print("[!] Encryption not activated nor required. Channel Binding off.")
|
|
conn.disconnect()
|
|
return
|
|
except Exception as e:
|
|
logging.debug(f"preLogin failed: {e}")
|
|
print("[-] Prelogin failed, cannot check MSSQL status.")
|
|
return
|
|
|
|
print("\n[*] First try: TDS computes the real Channel Binding Token (cbt=None)")
|
|
try:
|
|
conn = self._new_conn()
|
|
first_ok = self._login(conn, cbt=None)
|
|
conn.disconnect()
|
|
except Exception as e:
|
|
logging.debug(f"First try exception: {e}")
|
|
first_ok = False
|
|
print(f" Result: {'Success' if first_ok else 'Failure'}")
|
|
|
|
print("\n[*] Second try: invalid Channel Binding Token (cbt='')")
|
|
try:
|
|
conn = self._new_conn()
|
|
second_ok = self._login(conn, cbt=b'')
|
|
conn.disconnect()
|
|
except Exception as e:
|
|
logging.debug(f"Second try exception: {e}")
|
|
second_ok = False
|
|
print(f" Result: {'Success' if second_ok else 'Failure'}")
|
|
|
|
if first_ok and second_ok:
|
|
print("\n[+] The two authentications succeded. Channel Binding not required (CBT not enforced).")
|
|
elif first_ok and not second_ok:
|
|
print("\n[!] First authentication succeded, second failed. Channel Binding required (CBT enforced).")
|
|
elif not first_ok and not second_ok:
|
|
print("\n[!] The two authentications failed, invalid credentials.")
|
|
|
|
if __name__ == '__main__':
|
|
print(version.BANNER)
|
|
|
|
parser = argparse.ArgumentParser(add_help=True)
|
|
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
|
|
parser.add_argument('-port', default=1433, help='Port MSSQL (default: 1433)')
|
|
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
|
|
parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output', dest='timestamp')
|
|
|
|
group = parser.add_argument_group('authentication')
|
|
group.add_argument('-hashes', metavar='LMHASH:NTHASH', help='NTLM hashes')
|
|
group.add_argument('-no-pass', action='store_true', help="Don't ask for password (useful with -k)")
|
|
group.add_argument('-k', action='store_true', help='Use Kerberos authentication (ccache via KRB5CCNAME)')
|
|
group.add_argument('-aesKey', metavar='hex key', help='AES key for Kerberos (128 or 256 bits)')
|
|
|
|
group = parser.add_argument_group('connection')
|
|
group.add_argument('-dc-ip', metavar='ip address', help='IP of the domain controller')
|
|
group.add_argument('-target-ip', metavar='ip address', help='IP of the target (overrides target name resolution)')
|
|
|
|
if len(sys.argv) == 1:
|
|
parser.print_help()
|
|
sys.exit(1)
|
|
|
|
options = parser.parse_args()
|
|
logger.init(options.timestamp, options.debug)
|
|
|
|
domain, username, password, target = parse_target(options.target)
|
|
|
|
if domain is None:
|
|
domain = ''
|
|
|
|
if options.target_ip:
|
|
target = options.target_ip
|
|
|
|
if options.aesKey:
|
|
options.k = True
|
|
|
|
if password == '' and username != '' and not options.hashes and not options.no_pass and not options.aesKey:
|
|
password = getpass("Password: ")
|
|
|
|
try:
|
|
MSQLCBTCheck(options, username, password, domain, target).run()
|
|
except Exception as e:
|
|
if logging.getLogger().level == logging.DEBUG:
|
|
import traceback
|
|
traceback.print_exc()
|
|
logging.error(str(e))
|