Minor patches

This commit is contained in:
Miroslav Štampar 2026-07-29 08:20:08 +02:00
parent 39d22d26c3
commit 3a680be4eb
11 changed files with 42 additions and 28 deletions

View file

@ -1967,19 +1967,21 @@ def getLimitRange(count, plusOne=False):
if kb.dumpTable:
if conf.limitStart and conf.limitStop and conf.limitStart > conf.limitStop:
limitStop = conf.limitStart
limitStop = min(conf.limitStart, count) # a '--start' beyond the table must not request out-of-range offsets (phantom rows)
limitStart = conf.limitStop
reverse = True
else:
if isinstance(conf.limitStop, int) and conf.limitStop > 0 and conf.limitStop < limitStop:
limitStop = conf.limitStop
if isinstance(conf.limitStart, int) and conf.limitStart > 0 and conf.limitStart <= limitStop:
# NOTE: no '<= limitStop' gate - a '--start' past the row count must yield an EMPTY range
# (correctly skipping past every row), not silently fall back to dumping the whole table
if isinstance(conf.limitStart, int) and conf.limitStart > 0:
limitStart = conf.limitStart
retVal = xrange(limitStart, limitStop + 1) if plusOne else xrange(limitStart - 1, limitStop)
if reverse:
if reverse and len(retVal): # len() guard: a clamped out-of-range '--start' can leave the range empty
retVal = xrange(retVal[-1], retVal[0] - 1, -1)
return retVal

View file

@ -577,7 +577,7 @@ def getUnicode(value, encoding=None, noneToNull=False):
try:
return six.text_type(value, encoding or (kb.get("pageEncoding") if kb.get("originalPage") else None) or UNICODE_ENCODING)
except UnicodeDecodeError:
except (UnicodeDecodeError, LookupError): # LookupError: an unknown/invalid encoding name must fall back, not crash
return six.text_type(value, UNICODE_ENCODING, errors="reversible")
elif isListLike(value):
value = list(getUnicode(_, encoding, noneToNull) for _ in value)

View file

@ -520,7 +520,8 @@ def _setOpenApiTargets():
checkFile(conf.openApiFile)
infoMsg = "parsing OpenAPI/Swagger specification from '%s'" % conf.openApiFile
logger.info(infoMsg)
content = openFile(conf.openApiFile).read()
with openFile(conf.openApiFile) as f:
content = f.read()
tags = [_.strip() for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.openApiTags) if _.strip()] if conf.openApiTags else None
if tags:
@ -835,7 +836,8 @@ def _listTamperingFunctions():
logger.info(infoMsg)
for script in sorted(glob.glob(os.path.join(paths.SQLMAP_TAMPER_PATH, "*.py"))):
content = openFile(script, 'r').read()
with openFile(script, 'r') as f:
content = f.read()
match = re.search(r'(?s)__priority__.+"""(.+)"""', content)
if match:
comment = match.group(1).strip()

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.242"
VERSION = "1.10.7.243"
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)
@ -533,8 +533,11 @@ ERROR_PARSING_REGEXES = (
r'"(?:errmsg|errorMessage|reason|msg)"\s*:\s*"(?P<result>[^"]+)"' # generic JSON error-message field (NoSQL document/REST back-ends)
)
# Regular expression used for parsing charset info from meta html headers
META_CHARSET_REGEX = r"""(?si)<head\b[^>]*>.*<meta[^>]+charset\s*=\s*["']?(?P<result>[^"'> ]+).*</head>"""
# Regular expression used for parsing charset info from meta html headers (Note: the tempered token
# '(?:(?!</head>).)*?' keeps the meta strictly INSIDE <head> - as the old trailing '.*</head>' did -
# while the bounded meta-attr scan '{0,300}?' keeps it LINEAR; the old greedy form went quadratic and
# hung for many minutes on an attacker-controlled body full of '<meta' tokens lacking '>'/'</head>')
META_CHARSET_REGEX = r"""(?si)<head\b[^>]*>(?:(?!</head>).)*?<meta[^>]{0,300}?charset\s*=\s*["']?(?P<result>[^"'> ]+)"""
# Regular expression used for parsing refresh info from meta html headers
META_REFRESH_REGEX = r'(?i)<meta http-equiv="?refresh"?[^>]+content="?[^">]+;\s*(url=)?["\']?(?P<result>[^\'">]+)'

View file

@ -67,7 +67,7 @@ def comparison(page, headers, code=None, getRatioValue=False, pageLength=None):
return _
def _adjust(condition, getRatioValue):
if not any((conf.string, conf.notString, conf.regexp, conf.code)):
if not any((conf.string, conf.notString, conf.regexp, conf.code, conf.lengths)):
# Negative logic approach is used in raw page comparison scheme as that what is "different" than original
# PAYLOAD.WHERE.NEGATIVE response is considered as True; in switch based approach negative logic is not
# applied as that what is by user considered as True is that what is returned by the comparison mechanism

View file

@ -406,7 +406,8 @@ class Connect(object):
errMsg = "problem occurred while loading cookies from file '%s'" % conf.liveCookies
raise SqlmapValueException(errMsg)
cookie = openFile(conf.liveCookies).read().strip()
with openFile(conf.liveCookies) as f:
cookie = f.read().strip()
cookie = re.sub(r"(?i)\ACookie:\s*", "", cookie)
if multipart:
@ -545,7 +546,8 @@ class Connect(object):
headers = forgeHeaders(auxHeaders, headers)
if kb.headersFile:
content = openFile(kb.headersFile, 'r').read()
with openFile(kb.headersFile, 'r') as f:
content = f.read()
for line in content.split("\n"):
line = getText(line.strip())
if ':' in line:

View file

@ -875,7 +875,8 @@ def download(taskid, target, filename):
if os.path.isfile(path):
logger.debug("(%s) Retrieved content of file %s" % (taskid, target))
content = openFile(path, "rb").read()
with openFile(path, "rb") as f:
content = f.read()
return jsonize({"success": True, "file": encodeBase64(content, binary=False)})
else:
logger.warning("[%s] File does not exist %s" % (taskid, target))

View file

@ -1117,7 +1117,7 @@ def _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, proc_id, proc
word = word + suffix
try:
current = __functions__[hash_regex](password=word, uppercase=False)
current = __functions__[hash_regex](password=getBytes(word, unsafe=False), uppercase=False)
if current in hashes:
for item in attack_info[:]:
@ -1195,7 +1195,7 @@ def _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found
word = word + suffix
try:
current = __functions__[hash_regex](password=word, uppercase=False, **kwargs)
current = __functions__[hash_regex](password=getBytes(word, unsafe=False), uppercase=False, **kwargs)
if hash_ == current:
if hash_regex == HASH.ORACLE_OLD: # only for cosmetic purposes
@ -1285,7 +1285,7 @@ def _bruteProcessVariantSalted(attack_info, hash_regex, suffix, retVal, proc_id,
((user, hash_), kwargs) = item
try:
current = __functions__[hash_regex](password=word, uppercase=False, **kwargs)
current = __functions__[hash_regex](password=getBytes(word, unsafe=False), uppercase=False, **kwargs)
if hash_ == current:
retVal.put((user, hash_, word))

View file

@ -333,7 +333,9 @@ class Entries(object):
kb.data.dumpedTable[column] = {"length": len(column), "values": BigArray()}
for entry in entries:
if entry is None or len(entry) == 0:
# skip a missing/empty ROW container, but NOT an empty-string CELL value
# (single-column dumps yield bare strings; len("")==0 must not drop the row)
if entry is None or (isListLike(entry) and len(entry) == 0):
continue
if isinstance(entry, six.string_types):

View file

@ -135,8 +135,9 @@ class Search(object):
query = agent.limitQuery(index, query, dbCond)
value = unArrayizeValue(inject.getValue(query, union=False, error=False))
value = safeSQLIdentificatorNaming(value)
foundDbs.append(value)
if not isNoneValue(value): # guard (mirrors searchTable) so a failed retrieval can't push a None/garbage name
value = safeSQLIdentificatorNaming(value)
foundDbs.append(value)
conf.dumper.lister("found databases", foundDbs)

View file

@ -627,17 +627,18 @@ class Users(object):
elif Backend.isDbms(DBMS.DB2):
privs = privilege.split(',')
privilege = privs[0]
privs = privs[1]
privs = list(privs.strip())
i = 1
if len(privs) > 1: # guard a comma-less privilege value (mirrors the inband path)
privs = privs[1]
privs = list(privs.strip())
i = 1
for priv in privs:
if priv.upper() in ('Y', 'G'):
for position, db2Priv in DB2_PRIVS.items():
if position == i:
privilege += ", " + db2Priv
for priv in privs:
if priv.upper() in ('Y', 'G'):
for position, db2Priv in DB2_PRIVS.items():
if position == i:
privilege += ", " + db2Priv
i += 1
i += 1
privileges.add(privilege)