mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-06-29 04:50:58 +00:00
Update of tests
This commit is contained in:
parent
cb20a446ae
commit
2297c81309
32 changed files with 7177 additions and 7304 deletions
|
|
@ -11,7 +11,8 @@ be exercised against canned result rows without a live target, network, or DBMS.
|
|||
Each test sets conf.direct = True to drive the inband (union/error/query OR
|
||||
conf.direct) branch of the method under test, patches inject.getValue with rows
|
||||
matching the shape the method parses, then asserts the relevant kb.data.cached*
|
||||
container was populated.
|
||||
container was populated. Inference (blind) branches set conf.direct = False with a
|
||||
BOOLEAN technique present and follow the count-then-per-index contract.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
|
@ -24,11 +25,32 @@ from _testutils import bootstrap, set_dbms
|
|||
bootstrap()
|
||||
|
||||
from lib.core.data import conf, kb
|
||||
from lib.core.enums import EXPECTED, PAYLOAD
|
||||
import plugins.generic.users as umod
|
||||
from plugins.generic.users import Users
|
||||
from lib.core.settings import CURRENT_USER
|
||||
|
||||
|
||||
def _inference_gv(count, sequence):
|
||||
"""Build an inject.getValue stub for blind inference branches.
|
||||
|
||||
Returns `count` (as str) whenever the caller asks for EXPECTED.INT, otherwise
|
||||
yields the next item from `sequence` wrapped as a single-cell row ([value]),
|
||||
cycling if exhausted. This mirrors the count-then-per-row contract of every
|
||||
isInferenceAvailable() branch.
|
||||
"""
|
||||
state = {"i": 0}
|
||||
|
||||
def gv(query, *a, **k):
|
||||
if k.get("expected") == EXPECTED.INT:
|
||||
return str(count)
|
||||
val = sequence[state["i"] % len(sequence)]
|
||||
state["i"] += 1
|
||||
return [val]
|
||||
|
||||
return gv
|
||||
|
||||
|
||||
class TestUsersEnum(unittest.TestCase):
|
||||
def setUp(self):
|
||||
# Snapshot the global state these tests mutate so tearDown can restore it
|
||||
|
|
@ -252,5 +274,205 @@ class TestUsersEnum(unittest.TestCase):
|
|||
self.assertIn("root", areAdmins)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Privilege parsing / inference branches (relocated from test_generic_enum_more.py)
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
class _UsersBase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self._direct = conf.direct
|
||||
self._technique = conf.technique
|
||||
self._user = conf.user
|
||||
self._gv = umod.inject.getValue
|
||||
self._cbe = umod.inject.checkBooleanExpression
|
||||
self._store = umod.storeHashesToFile
|
||||
self._attack = umod.attackCachedUsersPasswords
|
||||
self._readInput = umod.readInput
|
||||
self._his = kb.data.get("has_information_schema")
|
||||
self._injection_data = kb.injection.data
|
||||
|
||||
set_dbms("MySQL")
|
||||
conf.direct = True
|
||||
conf.user = None
|
||||
kb.data.has_information_schema = True
|
||||
|
||||
umod.storeHashesToFile = lambda *a, **k: None
|
||||
umod.attackCachedUsersPasswords = lambda *a, **k: None
|
||||
umod.readInput = lambda *a, **k: "N"
|
||||
|
||||
def tearDown(self):
|
||||
conf.direct = self._direct
|
||||
conf.technique = self._technique
|
||||
conf.user = self._user
|
||||
umod.inject.getValue = self._gv
|
||||
umod.inject.checkBooleanExpression = self._cbe
|
||||
umod.storeHashesToFile = self._store
|
||||
umod.attackCachedUsersPasswords = self._attack
|
||||
umod.readInput = self._readInput
|
||||
kb.injection.data = self._injection_data
|
||||
if self._his is None:
|
||||
kb.data.pop("has_information_schema", None)
|
||||
else:
|
||||
kb.data.has_information_schema = self._his
|
||||
|
||||
def _inference(self):
|
||||
conf.direct = False
|
||||
conf.technique = None
|
||||
kb.injection.data = {PAYLOAD.TECHNIQUE.BOOLEAN: {"title": "AND boolean-based blind"}}
|
||||
|
||||
|
||||
class TestUsersPrivilegesInband(_UsersBase):
|
||||
def test_privileges_pgsql_multiple_digit_columns(self):
|
||||
# PostgreSQL: privilege columns are digit flags; a column index maps to
|
||||
# PGSQL_PRIVS only when its value is "1". Set createdb(1)=1 and super(2)=1,
|
||||
# leave the rest 0; assert exactly those two privileges are parsed and that
|
||||
# "super" makes the user an admin.
|
||||
set_dbms("PostgreSQL")
|
||||
from lib.core.dicts import PGSQL_PRIVS
|
||||
ncols = max(PGSQL_PRIVS.keys())
|
||||
row = ["pguser"] + ["0"] * ncols
|
||||
row[1] = "1" # createdb
|
||||
row[2] = "1" # super
|
||||
umod.inject.getValue = lambda query, *a, **k: [row]
|
||||
users = Users()
|
||||
kb.data.cachedUsersPrivileges = {}
|
||||
privileges, areAdmins = users.getPrivileges()
|
||||
self.assertEqual(set(privileges["pguser"]), {PGSQL_PRIVS[1], PGSQL_PRIVS[2]})
|
||||
self.assertIn("pguser", areAdmins)
|
||||
|
||||
def test_privileges_mysql_lt5_yn_flags(self):
|
||||
# MySQL < 5 (no information_schema): privilege columns are 'Y'/'N' flags
|
||||
# mapped to MYSQL_PRIVS by column position. Y in col 1 -> select_priv.
|
||||
set_dbms("MySQL")
|
||||
from lib.core.dicts import MYSQL_PRIVS
|
||||
kb.data.has_information_schema = False
|
||||
ncols = max(MYSQL_PRIVS.keys())
|
||||
row = ["root"] + ["N"] * ncols
|
||||
row[1] = "Y" # select_priv
|
||||
row[3] = "Y" # update_priv
|
||||
umod.inject.getValue = lambda query, *a, **k: [row]
|
||||
users = Users()
|
||||
kb.data.cachedUsersPrivileges = {}
|
||||
privileges, areAdmins = users.getPrivileges()
|
||||
self.assertIn(MYSQL_PRIVS[1], privileges["root"])
|
||||
self.assertIn(MYSQL_PRIVS[3], privileges["root"])
|
||||
self.assertNotIn(MYSQL_PRIVS[2], privileges["root"])
|
||||
|
||||
def test_privileges_firebird_letter_codes(self):
|
||||
# Firebird: each privilege is a single letter mapped via FIREBIRD_PRIVS.
|
||||
set_dbms("Firebird")
|
||||
from lib.core.dicts import FIREBIRD_PRIVS
|
||||
umod.inject.getValue = lambda query, *a, **k: [["fbuser", "S"], ["fbuser", "I"]]
|
||||
users = Users()
|
||||
kb.data.cachedUsersPrivileges = {}
|
||||
privileges, areAdmins = users.getPrivileges()
|
||||
self.assertEqual(set(privileges["fbuser"]),
|
||||
{FIREBIRD_PRIVS["S"], FIREBIRD_PRIVS["I"]})
|
||||
|
||||
def test_privileges_db2_grant_codes(self):
|
||||
# DB2: privilege string is "<name>,<grant-letters>"; each 'Y'/'G' letter at
|
||||
# position i appends the DB2_PRIVS[i] name to the privilege.
|
||||
set_dbms("DB2")
|
||||
from lib.core.dicts import DB2_PRIVS
|
||||
conf.user = "db2admin"
|
||||
# "DBADM" plus a grant string whose first letter (position 1) is 'Y' ->
|
||||
# DB2_PRIVS[1] ("CONTROLAUTH") is appended.
|
||||
umod.inject.getValue = lambda query, *a, **k: [["DB2ADMIN", "DBADM,Y"]]
|
||||
users = Users()
|
||||
kb.data.cachedUsersPrivileges = {}
|
||||
privileges, areAdmins = users.getPrivileges()
|
||||
joined = " ".join(privileges["DB2ADMIN"])
|
||||
self.assertIn("DBADM", joined)
|
||||
self.assertIn(DB2_PRIVS[1], joined)
|
||||
|
||||
|
||||
class TestUsersPrivilegesInference(_UsersBase):
|
||||
def test_privileges_inference_mysql(self):
|
||||
# Blind privilege enumeration for a named user: count, then one privilege
|
||||
# string per index. MySQL >= 5 adds each verbatim.
|
||||
set_dbms("MySQL")
|
||||
self._inference()
|
||||
conf.user = "root"
|
||||
privs = ["SELECT", "SUPER"]
|
||||
umod.inject.getValue = _inference_gv(2, privs)
|
||||
users = Users()
|
||||
kb.data.cachedUsersPrivileges = {}
|
||||
privileges, areAdmins = users.getPrivileges()
|
||||
# the user key is wildcard-wrapped for the MySQL information_schema LIKE
|
||||
key = [k for k in privileges if "root" in k][0]
|
||||
self.assertEqual(set(privileges[key]), {"SELECT", "SUPER"})
|
||||
self.assertTrue(areAdmins) # SUPER => admin
|
||||
|
||||
def test_privileges_inference_oracle(self):
|
||||
set_dbms("Oracle")
|
||||
self._inference()
|
||||
conf.user = "system"
|
||||
umod.inject.getValue = _inference_gv(1, ["DBA"])
|
||||
users = Users()
|
||||
kb.data.cachedUsersPrivileges = {}
|
||||
privileges, areAdmins = users.getPrivileges()
|
||||
self.assertIn("SYSTEM", privileges)
|
||||
self.assertEqual(privileges["SYSTEM"], ["DBA"])
|
||||
self.assertIn("SYSTEM", areAdmins)
|
||||
|
||||
|
||||
class TestUsersPasswordHashesInference(_UsersBase):
|
||||
def test_password_hashes_inference_grouping(self):
|
||||
# Blind password-hash enumeration for two users: per-user count, then one
|
||||
# hash per index. Assert each user maps to its own hash list.
|
||||
set_dbms("MySQL")
|
||||
self._inference()
|
||||
conf.user = "root,guest"
|
||||
|
||||
# per-user single hash; count is 1 for every user
|
||||
hashes = {"root": "*ROOTHASH", "guest": "*GUESTHASH"}
|
||||
|
||||
def gv(query, *a, **k):
|
||||
if k.get("expected") == EXPECTED.INT:
|
||||
return "1"
|
||||
for u, h in hashes.items():
|
||||
if u in query:
|
||||
return [h]
|
||||
return [None]
|
||||
|
||||
umod.inject.getValue = gv
|
||||
users = Users()
|
||||
kb.data.cachedUsersPasswords = {}
|
||||
res = users.getPasswordHashes()
|
||||
self.assertEqual(res["root"], ["*ROOTHASH"])
|
||||
self.assertEqual(res["guest"], ["*GUESTHASH"])
|
||||
|
||||
def test_password_hashes_inference_dedup(self):
|
||||
# The same hash returned twice for a user must be de-duplicated at the end
|
||||
# (kb.data.cachedUsersPasswords[user] = list(set(...))).
|
||||
set_dbms("MySQL")
|
||||
self._inference()
|
||||
conf.user = "root"
|
||||
umod.inject.getValue = _inference_gv(2, ["*DUP", "*DUP"])
|
||||
users = Users()
|
||||
kb.data.cachedUsersPasswords = {}
|
||||
res = users.getPasswordHashes()
|
||||
self.assertEqual(res["root"], ["*DUP"])
|
||||
|
||||
|
||||
class TestUsersGetUsersInference(_UsersBase):
|
||||
def test_get_users_inference(self):
|
||||
set_dbms("MySQL")
|
||||
self._inference()
|
||||
umod.inject.getValue = _inference_gv(2, ["root@localhost", "guest@%"])
|
||||
users = Users()
|
||||
kb.data.cachedUsers = []
|
||||
res = users.getUsers()
|
||||
self.assertEqual(sorted(res), ["guest@%", "root@localhost"])
|
||||
|
||||
def test_is_dba_mssql(self):
|
||||
# MSSQL isDba goes through the generic checkBooleanExpression branch.
|
||||
set_dbms("Microsoft SQL Server")
|
||||
umod.inject.checkBooleanExpression = lambda query, *a, **k: True
|
||||
users = Users()
|
||||
kb.data.isDba = None
|
||||
self.assertTrue(users.isDba())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue