From 84f0ef8232287152f9749270a495f705bb414600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0tampar?= Date: Tue, 28 Jul 2026 13:18:07 +0200 Subject: [PATCH] Minor corrected patch for MsSQL hash cracking --- lib/core/settings.py | 2 +- lib/utils/hash.py | 6 +++--- tests/test_hash.py | 25 +++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 3f363ee21..6e0df8735 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -20,7 +20,7 @@ from lib.core.enums import OS from thirdparty import six # sqlmap version (...) -VERSION = "1.10.7.218" +VERSION = "1.10.7.219" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 3386a322e..211738b99 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -183,7 +183,7 @@ def mssql_new_passwd(password, salt, uppercase=False): # since version '2012' """ binsalt = decodeHex(salt) - unistr = b"".join((_.encode(UNICODE_ENCODING) + b"\0") if ord(_) < 256 else _.encode(UNICODE_ENCODING) for _ in password) + unistr = getUnicode(password).encode("utf-16-le") # MSSQL hashes the password as UCS-2/UTF-16LE retVal = "0200%s%s" % (salt, sha512(unistr + binsalt).hexdigest()) @@ -200,7 +200,7 @@ def mssql_passwd(password, salt, uppercase=False): # versions '2005' and '2008' """ binsalt = decodeHex(salt) - unistr = b"".join((_.encode(UNICODE_ENCODING) + b"\0") if ord(_) < 256 else _.encode(UNICODE_ENCODING) for _ in password) + unistr = getUnicode(password).encode("utf-16-le") # MSSQL hashes the password as UCS-2/UTF-16LE retVal = "0100%s%s" % (salt, sha1(unistr + binsalt).hexdigest()) @@ -218,7 +218,7 @@ def mssql_old_passwd(password, salt, uppercase=True): # version '2000' and befo """ binsalt = decodeHex(salt) - unistr = b"".join((_.encode(UNICODE_ENCODING) + b"\0") if ord(_) < 256 else _.encode(UNICODE_ENCODING) for _ in password) + unistr = getUnicode(password).encode("utf-16-le") # MSSQL hashes the password as UCS-2/UTF-16LE retVal = "0100%s%s%s" % (salt, sha1(unistr + binsalt).hexdigest(), sha1(unistr.upper() + binsalt).hexdigest()) diff --git a/tests/test_hash.py b/tests/test_hash.py index 42db6995e..225a128cb 100644 --- a/tests/test_hash.py +++ b/tests/test_hash.py @@ -87,6 +87,31 @@ class TestDbmsSpecificVectors(unittest.TestCase): self.assertEqual(H.oracle_old_passwd("tiger", "scott", uppercase=True), "F894844C34402B67") +class TestMssqlUnicodePassword(unittest.TestCase): + """MSSQL hashes the password as UCS-2/UTF-16LE. A per-char 'utf-8 + NUL' approximation is only + correct for ASCII, so non-ASCII passwords (cafe, etc.) hashed WRONG and were uncrackable. The + 2012+ (SHA-512) ground truth is a live PWDENCRYPT(N'caf'+NCHAR(233)) from Azure SQL Edge.""" + + CAFE = u"caf\xe9" + + def test_mssql_new_matches_live_pwdencrypt(self): + real = ("0x0200a0d961e49fc45ec4922793c4f0b278587e977b281c10871a30a6e620ab0c24c" + "dce517f208252d6e5ca608d958c89aff5c69061cc6c788854e3e0788cb2510e227481990d") + self.assertEqual(H.mssql_new_passwd(self.CAFE, salt="a0d961e4", uppercase=False), real) + + def test_mssql_matches_documented_algorithm(self): + # 2005/2008: 0x0100 + salt + SHA1(UTF16LE(password) + salt) + salt = "4086ceb6" + expected = "0x0100%s%s" % (salt, hashlib.sha1(self.CAFE.encode("utf-16-le") + bytearray.fromhex(salt)).hexdigest()) + self.assertEqual(H.mssql_passwd(self.CAFE, salt=salt, uppercase=False), expected) + + def test_ascii_unchanged(self): + # the fix must leave the ASCII path identical (still the documented UTF-16LE form) + salt = "4086ceb6" + expected = "0x0100%s%s" % (salt, hashlib.sha1(u"testpass".encode("utf-16-le") + bytearray.fromhex(salt)).hexdigest()) + self.assertEqual(H.mssql_passwd("testpass", salt=salt, uppercase=False), expected) + + class TestHashRecognition(unittest.TestCase): def test_md5_generic(self): self.assertEqual(H.hashRecognition("179ad45c6ce2cb97cf1029e212046e81"), HASH.MD5_GENERIC)