mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-06-28 12:31:00 +00:00
215 lines
7.7 KiB
Python
215 lines
7.7 KiB
Python
#!/usr/bin/env python
|
|
|
|
"""
|
|
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
|
|
See the file 'LICENSE' for copying permission
|
|
|
|
DBMS-specific enumeration overrides for Oracle, PostgreSQL, MySQL and SQLite
|
|
(plugins/dbms/<dbms>/enumeration.py), driven through each full DBMS handler with
|
|
the injection layer mocked, so the dialect-specific discovery paths run without a
|
|
live target. The in-band (UNION/error/direct) branch is taken via conf.direct=True
|
|
and inject.getValue is stubbed with canned result rows.
|
|
|
|
Companion to tests/test_dbms_enum.py (which covers Microsoft SQL Server).
|
|
"""
|
|
|
|
import importlib
|
|
import os
|
|
import sys
|
|
import unittest
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
from _testutils import bootstrap, set_dbms
|
|
bootstrap()
|
|
|
|
from lib.core.common import Backend
|
|
from lib.core.data import conf, kb
|
|
from lib.core.enums import EXPECTED
|
|
from lib.core.exception import SqlmapUnsupportedFeatureException
|
|
|
|
|
|
class _EnumBase(unittest.TestCase):
|
|
"""Snapshot/restore the global state these enumerators mutate.
|
|
|
|
Other tests in the suite depend on clean globals (a leaked kb.hintValue
|
|
breaks test_inference_engine; a leaked forced DBMS breaks others), so every
|
|
knob touched here is captured in setUp and put back in tearDown.
|
|
"""
|
|
|
|
# the enumeration module whose inject.getValue we patch (overridden per DBMS)
|
|
module = None
|
|
|
|
def setUp(self):
|
|
# conf knobs
|
|
self._direct = conf.direct
|
|
self._batch = conf.batch
|
|
self._user = conf.user
|
|
self._db = conf.get("db")
|
|
self._tbl = conf.get("tbl")
|
|
self._exclude = conf.get("exclude")
|
|
|
|
# injection layer (some override modules - e.g. SQLite/PostgreSQL - do not
|
|
# import inject because their overrides return constants without querying)
|
|
self._has_inject = hasattr(self.module, "inject")
|
|
if self._has_inject:
|
|
self._gv = self.module.inject.getValue
|
|
|
|
# kb.data cached* containers
|
|
self._cachedTables = kb.data.get("cachedTables")
|
|
self._cachedColumns = kb.data.get("cachedColumns")
|
|
self._cachedDbs = kb.data.get("cachedDbs")
|
|
self._cachedUsers = kb.data.get("cachedUsers")
|
|
self._cachedUsersRoles = kb.data.get("cachedUsersRoles")
|
|
self._cachedUsersPrivileges = kb.data.get("cachedUsersPrivileges")
|
|
self._has_information_schema = kb.data.get("has_information_schema")
|
|
|
|
# state other tests are sensitive to
|
|
self._hintValue = kb.hintValue
|
|
self._injectionData = kb.injection.data
|
|
self._forcedDbms = Backend.getForcedDbms()
|
|
self._stickyDBMS = kb.stickyDBMS
|
|
|
|
# avoid readInput EOFError flakiness and interactive prompts
|
|
conf.direct = True
|
|
conf.batch = True
|
|
|
|
def tearDown(self):
|
|
conf.direct = self._direct
|
|
conf.batch = self._batch
|
|
conf.user = self._user
|
|
conf.db = self._db
|
|
conf.tbl = self._tbl
|
|
conf.exclude = self._exclude
|
|
|
|
if self._has_inject:
|
|
self.module.inject.getValue = self._gv
|
|
|
|
kb.data.cachedTables = self._cachedTables
|
|
kb.data.cachedColumns = self._cachedColumns
|
|
kb.data.cachedDbs = self._cachedDbs
|
|
kb.data.cachedUsers = self._cachedUsers
|
|
kb.data.cachedUsersRoles = self._cachedUsersRoles
|
|
kb.data.cachedUsersPrivileges = self._cachedUsersPrivileges
|
|
kb.data.has_information_schema = self._has_information_schema
|
|
|
|
kb.hintValue = self._hintValue
|
|
kb.injection.data = self._injectionData
|
|
kb.stickyDBMS = self._stickyDBMS
|
|
if self._forcedDbms is not None:
|
|
Backend.forceDbms(self._forcedDbms)
|
|
else:
|
|
kb.forcedDbms = None
|
|
|
|
|
|
class TestOracleEnum(_EnumBase):
|
|
module = importlib.import_module("plugins.dbms.oracle.enumeration")
|
|
|
|
def _handler(self):
|
|
from plugins.dbms.oracle import OracleMap
|
|
set_dbms("Oracle")
|
|
return OracleMap()
|
|
|
|
def test_get_roles(self):
|
|
# rows are [GRANTEE, GRANTED_ROLE]; first column is the user, the rest roles
|
|
conf.user = None
|
|
kb.data.cachedUsersRoles = {}
|
|
self.module.inject.getValue = lambda q, *a, **k: [
|
|
["SYS", "DBA"], ["SYS", "CONNECT"], ["SCOTT", "RESOURCE"]
|
|
]
|
|
roles, areAdmins = self._handler().getRoles()
|
|
self.assertIn("SYS", roles)
|
|
self.assertIn("SCOTT", roles)
|
|
self.assertEqual(set(roles["SYS"]), {"DBA", "CONNECT"})
|
|
# DBA implies administrator
|
|
self.assertIn("SYS", areAdmins)
|
|
|
|
def test_get_roles_filtered_by_user(self):
|
|
# conf.user populates a WHERE clause; canned rows still drive the parse
|
|
conf.user = "SCOTT"
|
|
kb.data.cachedUsersRoles = {}
|
|
self.module.inject.getValue = lambda q, *a, **k: [["SCOTT", "RESOURCE"]]
|
|
roles, _ = self._handler().getRoles()
|
|
self.assertEqual(list(roles.keys()), ["SCOTT"])
|
|
self.assertEqual(roles["SCOTT"], ["RESOURCE"])
|
|
|
|
def test_get_roles_multiple_roles_per_user(self):
|
|
# a user appearing across several rows accumulates all granted roles
|
|
conf.user = None
|
|
kb.data.cachedUsersRoles = {}
|
|
self.module.inject.getValue = lambda q, *a, **k: [
|
|
["APP", "CONNECT"], ["APP", "RESOURCE"], ["APP", "CREATE SESSION"]
|
|
]
|
|
roles, _ = self._handler().getRoles()
|
|
self.assertEqual(
|
|
set(roles["APP"]), {"CONNECT", "RESOURCE", "CREATE SESSION"}
|
|
)
|
|
|
|
|
|
class TestPostgreSQLEnum(_EnumBase):
|
|
module = importlib.import_module("plugins.dbms.postgresql.enumeration")
|
|
|
|
def _handler(self):
|
|
from plugins.dbms.postgresql import PostgreSQLMap
|
|
set_dbms("PostgreSQL")
|
|
return PostgreSQLMap()
|
|
|
|
def test_get_hostname_unsupported(self):
|
|
# PostgreSQL overrides getHostname purely to warn; it returns None
|
|
self.assertIsNone(self._handler().getHostname())
|
|
|
|
|
|
class TestMySQLEnum(_EnumBase):
|
|
# MySQL's enumeration.py adds no overrides (it is a bare `pass`); cover the
|
|
# generic discovery path through the full MySQL handler instead.
|
|
module = importlib.import_module("plugins.generic.enumeration")
|
|
|
|
def _handler(self):
|
|
from plugins.dbms.mysql import MySQLMap
|
|
set_dbms("MySQL")
|
|
return MySQLMap()
|
|
|
|
def test_get_dbs(self):
|
|
conf.db = None
|
|
kb.data.cachedDbs = []
|
|
kb.data.has_information_schema = True
|
|
self.module.inject.getValue = lambda q, *a, **k: (
|
|
3 if k.get("expected") == EXPECTED.INT
|
|
else [["information_schema"], ["testdb"], ["mysql"]]
|
|
)
|
|
dbs = self._handler().getDbs()
|
|
self.assertIn("testdb", dbs)
|
|
self.assertEqual(set(kb.data.cachedDbs), set(dbs))
|
|
|
|
|
|
class TestSQLiteEnum(_EnumBase):
|
|
module = importlib.import_module("plugins.dbms.sqlite.enumeration")
|
|
|
|
def _handler(self):
|
|
from plugins.dbms.sqlite import SQLiteMap
|
|
set_dbms("SQLite")
|
|
return SQLiteMap()
|
|
|
|
def test_unsupported_simple_overrides(self):
|
|
# SQLite overrides these to a warning + an empty/neutral return value
|
|
h = self._handler()
|
|
self.assertIsNone(h.getCurrentUser())
|
|
self.assertIsNone(h.getCurrentDb())
|
|
self.assertIsNone(h.getHostname())
|
|
self.assertEqual(h.getUsers(), [])
|
|
self.assertEqual(h.getDbs(), [])
|
|
self.assertEqual(h.searchDb(), [])
|
|
self.assertEqual(h.getStatements(), [])
|
|
self.assertEqual(h.getPasswordHashes(), {})
|
|
self.assertEqual(h.getPrivileges(), {})
|
|
|
|
def test_is_dba_always_true(self):
|
|
# on SQLite the current user is treated as having all privileges
|
|
self.assertTrue(self._handler().isDba())
|
|
|
|
def test_search_column_raises(self):
|
|
with self.assertRaises(SqlmapUnsupportedFeatureException):
|
|
self._handler().searchColumn()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|