Minor patches

This commit is contained in:
Miroslav Štampar 2026-07-11 15:18:42 +02:00
parent 97c8b45ccc
commit ab59a4b72a
8 changed files with 62 additions and 13 deletions

View file

@ -20,7 +20,7 @@ from lib.core.enums import OS
from thirdparty import six
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.10.7.80"
VERSION = "1.10.7.81"
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)

View file

@ -5,6 +5,7 @@ Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
See the file 'LICENSE' for copying permission
"""
import binascii
import re
import time
@ -29,6 +30,20 @@ from lib.core.settings import UNICODE_ENCODING
from lib.utils.safe2bin import safecharencode
from lib.utils.timeout import timeout
def _hexifyBinary(value):
"""
Renders a raw binary cell returned by a driver in -d mode as uppercase hex, instead of letting it reach
the text channel as a str() like '<memory at 0x...>' (PostgreSQL bytea -> psycopg2 memoryview) or a
control-character blob that gets blanked/corrupted (e.g. MSSQL varbinary -> bytes). Text columns come back
as native strings, so only genuine binary values are converted (matches HEX()-based rendering elsewhere).
"""
if isinstance(value, memoryview):
value = value.tobytes()
if isinstance(value, (bytes, bytearray)):
return getUnicode(binascii.hexlify(value)).upper()
return value
def direct(query, content=True):
select = True
query = agent.payloadDirect(query)
@ -63,6 +78,8 @@ def direct(query, content=True):
timeout(func=conf.dbmsConnector.execute, args=(query,), duration=conf.timeout, default=None)
elif not (output and ("%soutput" % conf.tablePrefix) not in query and ("%sfile" % conf.tablePrefix) not in query):
output, state = timeout(func=conf.dbmsConnector.select, args=(query,), duration=conf.timeout, default=None)
if output and isListLike(output):
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:

View file

@ -117,7 +117,7 @@ class SQLAlchemy(GenericConnector):
logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex))
return None
def execute(self, query):
def execute(self, query, commit=True):
retVal = False
# Reference: https://stackoverflow.com/a/69491015
@ -126,10 +126,14 @@ class SQLAlchemy(GenericConnector):
try:
self.cursor = self.connector.execute(query)
if hasattr(self.connector, "commit"): # Note: SQLAlchemy 2.0+ dropped implicit autocommit (otherwise DML changes - e.g. via --sql-query - would be silently lost)
# Note: SQLAlchemy 2.0+ dropped implicit autocommit (otherwise DML changes - e.g. via --sql-query -
# would be silently lost). SELECT goes through select() with commit=False so the result set is
# fetched BEFORE committing: on some drivers (e.g. pymssql) commit() discards the open cursor, which
# otherwise made every MSSQL '-d' query silently return empty (banner/is-dba/dump all blank).
if commit and hasattr(self.connector, "commit"):
self.connector.commit()
retVal = True
except (_sqlalchemy.exc.OperationalError, _sqlalchemy.exc.ProgrammingError) as ex:
except (_sqlalchemy.exc.OperationalError, _sqlalchemy.exc.ProgrammingError, _sqlalchemy.exc.DataError, _sqlalchemy.exc.IntegrityError) as ex:
logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex))
# Roll back the failed statement's transaction so it does not poison every following query with
# 'InFailedSqlTransaction' (SQLAlchemy 2.0+ keeps the transaction open after an error). Without this
@ -148,7 +152,14 @@ class SQLAlchemy(GenericConnector):
def select(self, query):
retVal = None
if self.execute(query):
# Fetch BEFORE committing (commit=False): committing can discard the open result cursor on some drivers
# (e.g. pymssql), which silently emptied every MSSQL '-d' result. No DML is persisted by a SELECT anyway.
if self.execute(query, commit=False):
retVal = self.fetchall()
if hasattr(self.connector, "commit"):
try:
self.connector.commit()
except Exception:
pass
return retVal

View file

@ -54,11 +54,20 @@ class Connector(GenericConnector):
logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex).replace("\n", " "))
return None
def execute(self, query):
def execute(self, query, commit=True):
retVal = False
try:
self.cursor.execute(getText(query))
# Commit non-SELECT (DML/DDL) here: direct() routes those to execute() alone, so without this a
# '--sql-query'/'--sql-shell' write was silently rolled back on connection close. select() passes
# commit=False and commits only AFTER fetchall(), because on pymssql commit() discards the open
# result cursor (which otherwise emptied every SELECT result).
if commit:
try:
self.connector.commit()
except pymssql.OperationalError:
pass
retVal = True
except (pymssql.OperationalError, pymssql.ProgrammingError) as ex:
logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex).replace("\n", " "))
@ -70,7 +79,7 @@ class Connector(GenericConnector):
def select(self, query):
retVal = None
if self.execute(query):
if self.execute(query, commit=False):
retVal = self.fetchall()
try:

View file

@ -12,7 +12,6 @@ except:
import logging
import struct
import sys
from lib.core.common import getSafeExString
from lib.core.data import conf
@ -34,7 +33,7 @@ class Connector(GenericConnector):
self.initConnection()
try:
self.connector = pymysql.connect(host=self.hostname, user=self.user, passwd=self.password.encode(sys.stdin.encoding), db=self.db, port=self.port, connect_timeout=conf.timeout, use_unicode=True)
self.connector = pymysql.connect(host=self.hostname, user=self.user, passwd=self.password, db=self.db, port=self.port, connect_timeout=conf.timeout, use_unicode=True)
except (pymysql.OperationalError, pymysql.InternalError, pymysql.ProgrammingError, struct.error) as ex:
raise SqlmapConnectionException(getSafeExString(ex))

View file

@ -55,7 +55,10 @@ class Connector(GenericConnector):
try:
self.cursor.execute(query)
retVal = True
except (psycopg2.OperationalError, psycopg2.ProgrammingError) as ex:
# Note: also catch DataError/IntegrityError (e.g. division-by-zero, bad cast, unique violation from a
# user '--sql-query') so the commit() below still runs and clears the aborted transaction; otherwise
# PostgreSQL poisons every later query with 'InFailedSqlTransaction' and silently returns None
except (psycopg2.OperationalError, psycopg2.ProgrammingError, psycopg2.DataError, psycopg2.IntegrityError) as ex:
logger.warning(("(remote) '%s'" % getSafeExString(ex)).strip())
except psycopg2.InternalError as ex:
raise SqlmapConnectionException(getSafeExString(ex))

View file

@ -17,6 +17,7 @@ from lib.core.common import isNoneValue
from lib.core.common import isStackingAvailable
from lib.core.common import randomStr
from lib.core.compat import LooseVersion
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import paths
@ -100,7 +101,7 @@ class Takeover(GenericTakeover):
def copyExecCmd(self, cmd):
output = None
if isStackingAvailable():
if isStackingAvailable() or conf.direct:
# Reference: https://medium.com/greenwolf-security/authenticated-arbitrary-command-execution-on-postgresql-9-3-latest-cd18945914d5
self._forgedCmd = "DROP TABLE IF EXISTS %s;" % self.cmdTblName
self._forgedCmd += "CREATE TABLE %s(%s text);" % (self.cmdTblName, self.tblField)

View file

@ -54,11 +54,20 @@ class Connector(GenericConnector):
logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex).replace("\n", " "))
return None
def execute(self, query):
def execute(self, query, commit=True):
retVal = False
try:
self.cursor.execute(getText(query))
# Commit non-SELECT (DML/DDL) here: direct() routes those to execute() alone, so without this a
# '--sql-query'/'--sql-shell' write was silently rolled back on connection close. select() passes
# commit=False and commits only AFTER fetchall(), because on pymssql commit() discards the open
# result cursor (which otherwise emptied every SELECT result).
if commit:
try:
self.connector.commit()
except pymssql.OperationalError:
pass
retVal = True
except (pymssql.OperationalError, pymssql.ProgrammingError) as ex:
logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex).replace("\n", " "))
@ -70,7 +79,7 @@ class Connector(GenericConnector):
def select(self, query):
retVal = None
if self.execute(query):
if self.execute(query, commit=False):
retVal = self.fetchall()
try: