From d5ff557a1364080e1dac53f65d52537fdd38ba34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0tampar?= Date: Sat, 11 Jul 2026 16:13:12 +0200 Subject: [PATCH] Some more patches for -d --- lib/core/dicts.py | 2 +- lib/core/settings.py | 2 +- lib/request/direct.py | 11 +++++-- plugins/dbms/access/connector.py | 4 ++- plugins/dbms/clickhouse/connector.py | 46 +++++++++++++++++++++++++++- plugins/dbms/db2/connector.py | 2 +- plugins/dbms/derby/connector.py | 2 +- plugins/dbms/informix/connector.py | 2 +- plugins/dbms/snowflake/connector.py | 8 +++++ 9 files changed, 70 insertions(+), 9 deletions(-) diff --git a/lib/core/dicts.py b/lib/core/dicts.py index f57e01a94..b2d4c7f94 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -232,7 +232,7 @@ DBMS_DICT = { DBMS.SQLITE: (SQLITE_ALIASES, "python-sqlite", "https://docs.python.org/3/library/sqlite3.html", "sqlite"), DBMS.ACCESS: (ACCESS_ALIASES, "python-pyodbc", "https://github.com/mkleehammer/pyodbc", "access"), DBMS.FIREBIRD: (FIREBIRD_ALIASES, "python3-firebirdsql", "https://github.com/nakagami/pyfirebirdsql/", "firebird"), - DBMS.MAXDB: (MAXDB_ALIASES, None, None, "maxdb"), + DBMS.MAXDB: (MAXDB_ALIASES, None, None, None), # 'maxdb'/'sapdb' SQLAlchemy dialect was removed long ago; a dead dialect only produces a misleading warning DBMS.SYBASE: (SYBASE_ALIASES, "python-pymssql", "https://github.com/pymssql/pymssql", "sybase"), DBMS.DB2: (DB2_ALIASES, "python ibm-db", "https://github.com/ibmdb/python-ibmdb", "ibm_db_sa"), DBMS.HSQLDB: (HSQLDB_ALIASES, "python jaydebeapi & python-jpype", "https://pypi.python.org/pypi/JayDeBeApi/ & https://github.com/jpype-project/jpype", None), diff --git a/lib/core/settings.py b/lib/core/settings.py index bc6db894e..fba21d81b 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.83" +VERSION = "1.10.7.84" 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/request/direct.py b/lib/request/direct.py index 6316e6dcf..52f410e90 100644 --- a/lib/request/direct.py +++ b/lib/request/direct.py @@ -60,7 +60,10 @@ def direct(query, content=True): break if select: - if re.search(r"(?i)\ASELECT ", query) is None: + # only auto-wrap a bare scalar expression (e.g. 'CURRENT_USER', '48*60') in SELECT; a user-supplied + # complete row-returning statement (--sql-query 'PRAGMA ...' / 'WITH ...' / 'EXPLAIN ...') must be left + # intact, otherwise 'SELECT PRAGMA ...' is a syntax error and silently returns nothing + if re.search(r"(?i)\A\s*(?:SELECT|WITH|PRAGMA|EXPLAIN|DESCRIBE|DESC|SHOW|TABLE|VALUES)\b", query) is None: query = "SELECT %s" % query if conf.binaryFields: @@ -82,7 +85,11 @@ def direct(query, content=True): output = [tuple(_hexifyBinary(_) for _ in row) if isListLike(row) else _hexifyBinary(row) for row in output] if state == TIMEOUT_STATE.NORMAL: hashDBWrite(query, output, True) - elif state == TIMEOUT_STATE.TIMEOUT: + elif state in (TIMEOUT_STATE.TIMEOUT, TIMEOUT_STATE.EXCEPTION): + # a timed-out OR fatally-errored query (e.g. the connector raised SqlmapConnectionException, or the + # connection dropped mid-scan) left the connection unusable; reconnect so the rest of the scan + # recovers instead of every following query being silently swallowed to None (wrong/empty data). + # A genuinely dead DB makes connect() raise here and the scan aborts cleanly, as with TIMEOUT. conf.dbmsConnector.close() conf.dbmsConnector.connect() elif output: diff --git a/plugins/dbms/access/connector.py b/plugins/dbms/access/connector.py index 91b8f2466..c1313bbf3 100644 --- a/plugins/dbms/access/connector.py +++ b/plugins/dbms/access/connector.py @@ -38,7 +38,9 @@ class Connector(GenericConnector): self.checkFileDb() try: - self.connector = pyodbc.connect('Driver={Microsoft Access Driver (*.mdb)};Dbq=%s;Uid=Admin;Pwd=;' % self.db) + # ACE driver ('*.mdb, *.accdb') handles both legacy Jet .mdb and modern .accdb (the old '*.mdb'-only + # Jet driver is 32-bit-only and absent on modern installs); honor supplied credentials, not Admin/empty + self.connector = pyodbc.connect('Driver={Microsoft Access Driver (*.mdb, *.accdb)};Dbq=%s;Uid=%s;Pwd=%s;' % (self.db, self.user or "Admin", self.password or "")) except (pyodbc.Error, pyodbc.OperationalError) as ex: raise SqlmapConnectionException(getSafeExString(ex)) diff --git a/plugins/dbms/clickhouse/connector.py b/plugins/dbms/clickhouse/connector.py index 83a868de7..12d4987ee 100755 --- a/plugins/dbms/clickhouse/connector.py +++ b/plugins/dbms/clickhouse/connector.py @@ -5,7 +5,51 @@ Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +try: + import clickhouse_connect + import clickhouse_connect.dbapi +except: + pass + +import logging + +from lib.core.common import getSafeExString +from lib.core.data import conf +from lib.core.data import logger +from lib.core.exception import SqlmapConnectionException from plugins.generic.connector import Connector as GenericConnector class Connector(GenericConnector): - pass + """ + Homepage: https://github.com/ClickHouse/clickhouse-connect + User guide: https://clickhouse.com/docs/integrations/python + License: Apache 2.0 + """ + + def connect(self): + self.initConnection() + + try: + self.connector = clickhouse_connect.dbapi.connect(host=self.hostname, port=self.port, username=self.user, password=self.password, database=self.db) + except clickhouse_connect.dbapi.Error as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except clickhouse_connect.dbapi.Error as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + return None + + def execute(self, query): + try: + self.cursor.execute(query) + except clickhouse_connect.dbapi.Error as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + + def select(self, query): + self.execute(query) + return self.fetchall() diff --git a/plugins/dbms/db2/connector.py b/plugins/dbms/db2/connector.py index 0a8e96b7a..7e30a3369 100644 --- a/plugins/dbms/db2/connector.py +++ b/plugins/dbms/db2/connector.py @@ -32,7 +32,7 @@ class Connector(GenericConnector): try: database = "DRIVER={IBM DB2 ODBC DRIVER};DATABASE=%s;HOSTNAME=%s;PORT=%s;PROTOCOL=TCPIP;" % (self.db, self.hostname, self.port) self.connector = ibm_db_dbi.connect(database, self.user, self.password) - except ibm_db_dbi.OperationalError as ex: + except ibm_db_dbi.Error as ex: # base class: ibm_db_dbi maps wrong-credential (SQLSTATE 28) to ProgrammingError, not OperationalError raise SqlmapConnectionException(getSafeExString(ex)) self.initCursor() diff --git a/plugins/dbms/derby/connector.py b/plugins/dbms/derby/connector.py index 7be45f741..1069977b2 100644 --- a/plugins/dbms/derby/connector.py +++ b/plugins/dbms/derby/connector.py @@ -30,7 +30,7 @@ class Connector(GenericConnector): self.initConnection() try: - self.connector = drda.connect(host=self.hostname, database=self.db, port=self.port) + self.connector = drda.connect(host=self.hostname, database=self.db, port=self.port, user=self.user or None, password=self.password or None) except drda.OperationalError as ex: raise SqlmapConnectionException(getSafeExString(ex)) diff --git a/plugins/dbms/informix/connector.py b/plugins/dbms/informix/connector.py index e6f05889c..b7ae9e805 100644 --- a/plugins/dbms/informix/connector.py +++ b/plugins/dbms/informix/connector.py @@ -32,7 +32,7 @@ class Connector(GenericConnector): try: database = "DATABASE=%s;HOSTNAME=%s;PORT=%s;PROTOCOL=TCPIP;" % (self.db, self.hostname, self.port) self.connector = ibm_db_dbi.connect(database, self.user, self.password) - except ibm_db_dbi.OperationalError as ex: + except ibm_db_dbi.Error as ex: # base class: ibm_db_dbi maps wrong-credential (SQLSTATE 28) to ProgrammingError, not OperationalError raise SqlmapConnectionException(getSafeExString(ex)) self.initCursor() diff --git a/plugins/dbms/snowflake/connector.py b/plugins/dbms/snowflake/connector.py index c24f3ab17..cad4fd3a9 100644 --- a/plugins/dbms/snowflake/connector.py +++ b/plugins/dbms/snowflake/connector.py @@ -32,6 +32,14 @@ class Connector(GenericConnector): def connect(self): self.initConnection() + # Snowflake's mandatory 'account' identifier is carried in the DSN host field + # (e.g. -d "snowflake://user:pass@ACCOUNT/db"); warehouse/schema are optional. These were previously + # read from self.account/self.warehouse/self.schema which were never set anywhere -> AttributeError on + # every attempt. + self.account = self.hostname + self.warehouse = getattr(self, "warehouse", None) + self.schema = getattr(self, "schema", None) + try: self.connector = snowflake.connector.connect( user=self.user,