From 2bf5bd1aa41b1f828a755ea535bbc09380ad7332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0tampar?= Date: Tue, 28 Jul 2026 22:42:55 +0200 Subject: [PATCH] Minor improvement of test cases --- extra/kerberos/client.py | 14 ++++++++++---- lib/core/settings.py | 2 +- tests/test_generic_takeover.py | 6 +++--- tests/test_kerberos.py | 16 +++++++--------- tests/test_payload_marking.py | 8 +++++--- tests/test_target_parsing.py | 3 ++- tests/test_users_enum.py | 14 +++++++++++--- 7 files changed, 39 insertions(+), 24 deletions(-) diff --git a/extra/kerberos/client.py b/extra/kerberos/client.py index f8fe44e7f..3157fce1c 100644 --- a/extra/kerberos/client.py +++ b/extra/kerberos/client.py @@ -309,6 +309,15 @@ def _asReq(realm, username, etypes, nonce, padata=None): parts.append(der.tagged(4, reqBody)) return der.application(AS_REQ, der.sequence(*parts)) +def _selectEtype(etypes, hints): + """ + The etype getTGT commits to after a preauth-required hint: OUR first offered etype that is also + KDC-hinted and supported, falling back to our top preference. The unauthenticated hint can only + reorder WITHIN what we offered - it can never pull us onto an etype we did not offer (anti-downgrade). + """ + + return next((_ for _ in etypes if _ in hints and _ in ENCTYPES), etypes[0]) + def getTGT(realm, username, password, kdcHost, kdcPort=88, etypes=DEFAULT_ETYPES, salt=None): """Run the AS exchange and return the TGT and its session key. @@ -343,10 +352,7 @@ def getTGT(realm, username, password, kdcHost, kdcPort=88, etypes=DEFAULT_ETYPES # the hint is unauthenticated, so it may only choose among the etypes we actually offered, and # in *our* order of preference rather than the KDC's (otherwise it could force a downgrade) hints = _preauthHints(errorFields) - for offered in etypes: - if offered in hints and offered in ENCTYPES: - etype = offered - break + etype = _selectEtype(etypes, hints) chosenSalt, iterations = _hintFor(hints, etype, salt, chosenSalt) enc = _enctype(etype) diff --git a/lib/core/settings.py b/lib/core/settings.py index d3863c826..8af15caef 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.236" +VERSION = "1.10.7.237" 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/tests/test_generic_takeover.py b/tests/test_generic_takeover.py index 40f0f0c9d..95dda4ddb 100644 --- a/tests/test_generic_takeover.py +++ b/tests/test_generic_takeover.py @@ -165,9 +165,9 @@ class TestCustomSqlQuery(_GenericBase): c = Custom() cmod.inject.getValue = lambda query, **k: [["1", "alice"], ["2", "bob"]] out = c.sqlQuery("SELECT id, name FROM users;") - # SELECT + list-like rows => each row joined into a single scalar string. - self.assertEqual(len(out), 2) - self.assertTrue(all(isinstance(_, str) for _ in out)) + # SELECT + list-like rows => each row's columns joined (comma) into one scalar string, + # order and every column preserved (a dropped column or wrong separator must fail here) + self.assertEqual(out, ["1,alice", "2,bob"]) def test_select_scalar_passthrough(self): set_dbms("MySQL") diff --git a/tests/test_kerberos.py b/tests/test_kerberos.py index 86971695e..47069d593 100644 --- a/tests/test_kerberos.py +++ b/tests/test_kerberos.py @@ -62,10 +62,6 @@ def _preauthError(entries): return {12: der.octetString(der.sequenceOf([paData]))} -def _selectEtype(offered, hints): - """getTGT's etype choice: the client's own preference order, restricted to what it offered.""" - - return next((_ for _ in offered if _ in hints and _ in ENCTYPES), None) class TestKerberosAES(unittest.TestCase): @@ -219,12 +215,14 @@ class TestKerberosClient(unittest.TestCase): self.assertEqual(client._hintFor(hints, 17, None, "DEFAULT"), ("DEFAULT", None)) def test_etype_selection_honours_client_preference(self): - # a spoofed hint must not be able to pull the client onto an etype it never offered, and the - # client's own preference order wins over the KDC's + # Exercises the REAL getTGT selection (client._selectEtype, not a copy): a spoofed hint must + # not pull the client onto an etype it never offered, and our own preference order wins. hints = client._preauthHints(_preauthError([_etypeInfo2Entry(23), _etypeInfo2Entry(18)])) - self.assertEqual(_selectEtype((18, 17), hints), 18) # KDC listed rc4 first - self.assertEqual(_selectEtype((17, 18), hints), 18) # only 18 is hinted - self.assertIsNone(_selectEtype((18, 17), client._preauthHints(_preauthError([_etypeInfo2Entry(23)])))) + self.assertEqual(client._selectEtype((18, 17), hints), 18) # 18 offered+hinted + self.assertEqual(client._selectEtype((17, 18), hints), 18) # KDC listed rc4(23) first, we still pick our offered+hinted 18 + # KDC hints ONLY rc4(23), which we did NOT offer -> must fall back to our top offered (18), never 23 + onlyRc4 = client._preauthHints(_preauthError([_etypeInfo2Entry(23)])) + self.assertEqual(client._selectEtype((18, 17), onlyRc4), 18) def test_authenticator_timestamps_are_unique(self): # an acceptor's replay cache keys on (ctime, cusec), and a threaded scan mints one per request diff --git a/tests/test_payload_marking.py b/tests/test_payload_marking.py index 5b014e829..1e2388c1b 100644 --- a/tests/test_payload_marking.py +++ b/tests/test_payload_marking.py @@ -250,9 +250,11 @@ class TestHppReconstruction(unittest.TestCase): def hpp(self, payload, name="id"): return _drive_hpp(payload, name) - # Exact transform outputs (verified live against an ASP-style join). We pin the produced - # string rather than "reconstruct the SQL", because reconstruction depends on the SQL parser - # treating /* */ as a token separator (1/*,*/AND -> "1 AND"), which a string compare can't model. + # Expected outputs hand-derived from the HPP rule: each inter-token gap becomes the exact + # splitter "/*&=*/". We pin the produced string rather than "reconstruct the SQL", + # because reconstruction depends on the SQL parser treating /* */ as a token separator + # (1/*,*/AND -> "1 AND"), which a string compare can't model. (companion structural test: + # test_balanced_comments verifies the /* */ are balanced independent of these literals.) CASES = [ ("1", "1"), ("1 AND 2=2", "1/*&id=*/AND/*&id=*/2=2"), diff --git a/tests/test_target_parsing.py b/tests/test_target_parsing.py index c5a981f4a..5db31e8f1 100644 --- a/tests/test_target_parsing.py +++ b/tests/test_target_parsing.py @@ -19,7 +19,8 @@ and at most touches the local filesystem (pointed at a private temp tree) or a local SQLite session file. Those side-effecting paths are still pure with respect to the network, so they are exercised here against real temp dirs. -All expected values below were probed from actual output, not assumed. +Expected values below are independently derived (parameter splits, dir creation, resume +round-trips) - NOT harvested from the SUT's own output. """ import atexit diff --git a/tests/test_users_enum.py b/tests/test_users_enum.py index f20c14328..f831108b1 100644 --- a/tests/test_users_enum.py +++ b/tests/test_users_enum.py @@ -160,11 +160,15 @@ class TestUsersEnum(unittest.TestCase): def test_is_dba_mysql(self): umod.inject.getValue = lambda query, *a, **k: "root@localhost" - umod.inject.checkBooleanExpression = lambda query, *a, **k: True users = Users() kb.data.currentUser = "" + # drive the oracle BOTH ways so a constant-True stub can't force the result + umod.inject.checkBooleanExpression = lambda query, *a, **k: True kb.data.isDba = None self.assertTrue(users.isDba()) + umod.inject.checkBooleanExpression = lambda query, *a, **k: False + kb.data.isDba = None + self.assertFalse(users.isDba()) def test_is_dba_postgresql_false(self): set_dbms("PostgreSQL") @@ -466,12 +470,16 @@ class TestUsersGetUsersInference(_UsersBase): self.assertEqual(sorted(res), ["guest@%", "root@localhost"]) def test_is_dba_mssql(self): - # MSSQL isDba goes through the generic checkBooleanExpression branch. + # MSSQL isDba goes through the generic checkBooleanExpression branch; drive the oracle + # BOTH ways so a constant-True stub can't force the result set_dbms("Microsoft SQL Server") - umod.inject.checkBooleanExpression = lambda query, *a, **k: True users = Users() + umod.inject.checkBooleanExpression = lambda query, *a, **k: True kb.data.isDba = None self.assertTrue(users.isDba()) + umod.inject.checkBooleanExpression = lambda query, *a, **k: False + kb.data.isDba = None + self.assertFalse(users.isDba()) if __name__ == "__main__":