Compare commits

...

13 Commits
1.7 ... 1.7.2

Author SHA1 Message Date
Miroslav Stampar
4585243175 Implements tamper script if2case (#5301) 2023-02-01 13:53:19 +01:00
Miroslav Stampar
fbfed061b8 Fixes #5300 2023-01-28 21:50:26 +01:00
Miroslav Stampar
fdbc323aa6 One more update for #5295 2023-01-24 12:08:02 +01:00
Miroslav Stampar
6336389322 Another update for #5295 2023-01-24 12:00:23 +01:00
Miroslav Stampar
a7b59243e2 One more update regarding #4870 2023-01-23 18:04:47 +01:00
Miroslav Stampar
c8eea24ac4 Implements #5295 2023-01-23 16:40:41 +01:00
Miroslav Stampar
1be7a5aea8 Fixes #4870 2023-01-23 16:21:46 +01:00
Miroslav Stampar
d0d4cf4f6d Minor update regarding #5297 2023-01-23 16:05:46 +01:00
Miroslav Stampar
1f83076e70 Fixes #5287 2023-01-15 18:07:44 +01:00
Miroslav Stampar
b0a1efaa44 Minor update for #5279 2023-01-09 17:12:26 +01:00
Miroslav Stampar
de527f1814 Minor update for #5285 2023-01-09 15:35:21 +01:00
Miroslav Stampar
96adc7c098 Fixes #5285 2023-01-09 15:34:08 +01:00
Miroslav Stampar
7940b572ef Trivy minor version bump 2023-01-02 23:39:27 +01:00
11 changed files with 133 additions and 11 deletions

View File

@@ -489,7 +489,7 @@ class Agent(object):
if field and Backend.getIdentifiedDbms(): if field and Backend.getIdentifiedDbms():
rootQuery = queries[Backend.getIdentifiedDbms()] rootQuery = queries[Backend.getIdentifiedDbms()]
if field.startswith("(CASE") or field.startswith("(IIF") or conf.noCast: if field.startswith("(CASE") or field.startswith("(IIF") or conf.noCast and not (field.startswith("COUNT(") and getTechnique() in (PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.UNION) and Backend.getIdentifiedDbms() == DBMS.MSSQL):
nulledCastedField = field nulledCastedField = field
else: else:
if not (Backend.isDbms(DBMS.SQLITE) and not isDBMSVersionAtLeast('3')): if not (Backend.isDbms(DBMS.SQLITE) and not isDBMSVersionAtLeast('3')):
@@ -596,6 +596,9 @@ class Agent(object):
if not _: if not _:
fieldsSelectFrom = None fieldsSelectFrom = None
if re.search(r"\bWHERE\b.+(MIN|MAX)", query, re.I):
fieldsMinMaxstr = None
fieldsToCastStr = fieldsNoSelect fieldsToCastStr = fieldsNoSelect
if fieldsSubstr: if fieldsSubstr:

View File

@@ -1696,11 +1696,20 @@ def _cleanupOptions():
try: try:
conf.ignoreCode = [int(_) for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.ignoreCode)] conf.ignoreCode = [int(_) for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.ignoreCode)]
except ValueError: except ValueError:
errMsg = "options '--ignore-code' should contain a list of integer values or a wildcard value '%s'" % IGNORE_CODE_WILDCARD errMsg = "option '--ignore-code' should contain a list of integer values or a wildcard value '%s'" % IGNORE_CODE_WILDCARD
raise SqlmapSyntaxException(errMsg) raise SqlmapSyntaxException(errMsg)
else: else:
conf.ignoreCode = [] conf.ignoreCode = []
if conf.abortCode:
try:
conf.abortCode = [int(_) for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.abortCode)]
except ValueError:
errMsg = "option '--abort-code' should contain a list of integer values"
raise SqlmapSyntaxException(errMsg)
else:
conf.abortCode = []
if conf.paramFilter: if conf.paramFilter:
conf.paramFilter = [_.strip() for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.paramFilter.upper())] conf.paramFilter = [_.strip() for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.paramFilter.upper())]
else: else:
@@ -2655,6 +2664,9 @@ def _basicOptionValidation():
raise SqlmapSyntaxException(errMsg) raise SqlmapSyntaxException(errMsg)
if conf.paramExclude: if conf.paramExclude:
if re.search(r"\A\w+,", conf.paramExclude):
conf.paramExclude = r"\A(%s)\Z" % ('|'.join(re.escape(_).strip() for _ in conf.paramExclude.split(',')))
try: try:
re.compile(conf.paramExclude) re.compile(conf.paramExclude)
except Exception as ex: except Exception as ex:

View File

@@ -39,6 +39,7 @@ optDict = {
"authType": "string", "authType": "string",
"authCred": "string", "authCred": "string",
"authFile": "string", "authFile": "string",
"abortCode": "string",
"ignoreCode": "string", "ignoreCode": "string",
"ignoreProxy": "boolean", "ignoreProxy": "boolean",
"ignoreRedirects": "boolean", "ignoreRedirects": "boolean",
@@ -204,6 +205,7 @@ optDict = {
"General": { "General": {
"trafficFile": "string", "trafficFile": "string",
"abortOnEmpty": "boolean",
"answers": "string", "answers": "string",
"batch": "boolean", "batch": "boolean",
"base64Parameter": "string", "base64Parameter": "string",

View File

@@ -20,7 +20,7 @@ from thirdparty import six
from thirdparty.six import unichr as _unichr from thirdparty.six import unichr as _unichr
# sqlmap version (<major>.<minor>.<month>.<monthly commit>) # sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.7" VERSION = "1.7.2.0"
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} 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) VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)

View File

@@ -201,8 +201,11 @@ def cmdLineParser(argv=None):
request.add_argument("--auth-file", dest="authFile", request.add_argument("--auth-file", dest="authFile",
help="HTTP authentication PEM cert/private key file") help="HTTP authentication PEM cert/private key file")
request.add_argument("--abort-code", dest="abortCode",
help="Abort on (problematic) HTTP error code(s) (e.g. 401)")
request.add_argument("--ignore-code", dest="ignoreCode", request.add_argument("--ignore-code", dest="ignoreCode",
help="Ignore (problematic) HTTP error code (e.g. 401)") help="Ignore (problematic) HTTP error code(s) (e.g. 401)")
request.add_argument("--ignore-proxy", dest="ignoreProxy", action="store_true", request.add_argument("--ignore-proxy", dest="ignoreProxy", action="store_true",
help="Ignore system default proxy settings") help="Ignore system default proxy settings")
@@ -628,6 +631,9 @@ def cmdLineParser(argv=None):
general.add_argument("-t", dest="trafficFile", general.add_argument("-t", dest="trafficFile",
help="Log all HTTP traffic into a textual file") help="Log all HTTP traffic into a textual file")
general.add_argument("--abort-on-empty", dest="abortOnEmpty", action="store_true",
help="Abort data retrieval on empty results")
general.add_argument("--answers", dest="answers", general.add_argument("--answers", dest="answers",
help="Set predefined answers (e.g. \"quit=N,follow=N\")") help="Set predefined answers (e.g. \"quit=N,follow=N\")")

View File

@@ -767,6 +767,11 @@ class Connect(object):
if not multipart: if not multipart:
logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg)
if code in conf.abortCode:
errMsg = "aborting due to detected HTTP code '%d'" % code
singleTimeLogMessage(errMsg, logging.CRITICAL)
raise SystemExit
if ex.code not in (conf.ignoreCode or []): if ex.code not in (conf.ignoreCode or []):
if ex.code == _http_client.UNAUTHORIZED: if ex.code == _http_client.UNAUTHORIZED:
errMsg = "not authorized, try to provide right HTTP " errMsg = "not authorized, try to provide right HTTP "
@@ -921,6 +926,12 @@ class Connect(object):
errMsg += "function '%s' ('%s')" % (function.__name__, getSafeExString(ex)) errMsg += "function '%s' ('%s')" % (function.__name__, getSafeExString(ex))
raise SqlmapGenericException(errMsg) raise SqlmapGenericException(errMsg)
for _ in (getattr(conn, "redcode", None), code):
if _ is not None and _ in conf.abortCode:
errMsg = "aborting due to detected HTTP code '%d'" % _
singleTimeLogMessage(errMsg, logging.CRITICAL)
raise SystemExit
threadData.lastPage = page threadData.lastPage = page
threadData.lastCode = code threadData.lastCode = code

View File

@@ -501,6 +501,11 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser
kb.safeCharEncode = False kb.safeCharEncode = False
if not any((kb.testMode, conf.dummy, conf.offline, conf.noCast, conf.hexConvert)) and value is None and Backend.getDbms() and conf.dbmsHandler and kb.fingerprinted: if not any((kb.testMode, conf.dummy, conf.offline, conf.noCast, conf.hexConvert)) and value is None and Backend.getDbms() and conf.dbmsHandler and kb.fingerprinted:
if conf.abortOnEmpty:
errMsg = "aborting due to empty data retrieval"
logger.critical(errMsg)
raise SystemExit
else:
warnMsg = "in case of continuous data retrieval problems you are advised to try " warnMsg = "in case of continuous data retrieval problems you are advised to try "
warnMsg += "a switch '--no-cast' " warnMsg += "a switch '--no-cast' "
warnMsg += "or switch '--hex'" if hasattr(queries[Backend.getIdentifiedDbms()], "hex") else "" warnMsg += "or switch '--hex'" if hasattr(queries[Backend.getIdentifiedDbms()], "hex") else ""

View File

@@ -21,7 +21,7 @@ try:
if hasattr(module, "dialects"): if hasattr(module, "dialects"):
_sqlalchemy = module _sqlalchemy = module
warnings.simplefilter(action="ignore", category=_sqlalchemy.exc.SAWarning) warnings.simplefilter(action="ignore", category=_sqlalchemy.exc.SAWarning)
except ImportError: except:
pass pass
finally: finally:
sys.path = _path sys.path = _path
@@ -39,6 +39,7 @@ from lib.core.exception import SqlmapFilePathException
from lib.core.exception import SqlmapMissingDependence from lib.core.exception import SqlmapMissingDependence
from plugins.generic.connector import Connector as GenericConnector from plugins.generic.connector import Connector as GenericConnector
from thirdparty import six from thirdparty import six
from thirdparty.six.moves import urllib as _urllib
def getSafeExString(ex, encoding=None): # Cross-referenced function def getSafeExString(ex, encoding=None): # Cross-referenced function
raise NotImplementedError raise NotImplementedError
@@ -50,6 +51,14 @@ class SQLAlchemy(GenericConnector):
self.dialect = dialect self.dialect = dialect
self.address = conf.direct self.address = conf.direct
if conf.dbmsUser:
self.address = self.address.replace("'%s':" % conf.dbmsUser, "%s:" % _urllib.parse.quote(conf.dbmsUser))
self.address = self.address.replace("%s:" % conf.dbmsUser, "%s:" % _urllib.parse.quote(conf.dbmsUser))
if conf.dbmsPass:
self.address = self.address.replace(":'%s'@" % conf.dbmsPass, ":%s@" % _urllib.parse.quote(conf.dbmsPass))
self.address = self.address.replace(":%s@" % conf.dbmsPass, ":%s@" % _urllib.parse.quote(conf.dbmsPass))
if self.dialect: if self.dialect:
self.address = re.sub(r"\A.+://", "%s://" % self.dialect, self.address) self.address = re.sub(r"\A.+://", "%s://" % self.dialect, self.address)

View File

@@ -101,8 +101,12 @@ authCred =
# Syntax: key_file # Syntax: key_file
authFile = authFile =
# Abort on (problematic) HTTP error code (e.g. 401).
# Valid: string
abortCode =
# Ignore (problematic) HTTP error code (e.g. 401). # Ignore (problematic) HTTP error code (e.g. 401).
# Valid: integer # Valid: string
ignoreCode = ignoreCode =
# Ignore system default proxy settings. # Ignore system default proxy settings.
@@ -702,6 +706,9 @@ sessionFile =
# Log all HTTP traffic into a textual file. # Log all HTTP traffic into a textual file.
trafficFile = trafficFile =
# Abort data retrieval on empty results.
abortOnEmpty = False
# Set predefined answers (e.g. "quit=N,follow=N"). # Set predefined answers (e.g. "quit=N,follow=N").
answers = answers =

View File

@@ -64,7 +64,6 @@ try:
from lib.core.common import setPaths from lib.core.common import setPaths
from lib.core.common import weAreFrozen from lib.core.common import weAreFrozen
from lib.core.convert import getUnicode from lib.core.convert import getUnicode
from lib.core.common import MKSTEMP_PREFIX
from lib.core.common import setColor from lib.core.common import setColor
from lib.core.common import unhandledExceptionMessage from lib.core.common import unhandledExceptionMessage
from lib.core.compat import LooseVersion from lib.core.compat import LooseVersion
@@ -73,6 +72,7 @@ try:
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.datatype import OrderedSet from lib.core.datatype import OrderedSet
from lib.core.enums import MKSTEMP_PREFIX
from lib.core.exception import SqlmapBaseException from lib.core.exception import SqlmapBaseException
from lib.core.exception import SqlmapShellQuitException from lib.core.exception import SqlmapShellQuitException
from lib.core.exception import SqlmapSilentQuitException from lib.core.exception import SqlmapSilentQuitException

67
tamper/if2case.py Normal file
View File

@@ -0,0 +1,67 @@
#!/usr/bin/env python
"""
Copyright (c) 2006-2023 sqlmap developers (https://sqlmap.org/)
See the file 'doc/COPYING' for copying permission
"""
from lib.core.compat import xrange
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.HIGHEST
def dependencies():
pass
def tamper(payload, **kwargs):
"""
Replaces instances like 'IF(A, B, C)' with 'CASE WHEN (A) THEN (B) ELSE (C) END' counterpart
Requirement:
* MySQL
* SQLite (possibly)
* SAP MaxDB (possibly)
Tested against:
* MySQL 5.0 and 5.5
Notes:
* Useful to bypass very weak and bespoke web application firewalls
that filter the IF() functions
>>> tamper('IF(1, 2, 3)')
'CASE WHEN (1) THEN (2) ELSE (3) END'
>>> tamper('SELECT IF((1=1), (SELECT "foo"), NULL)')
'SELECT CASE WHEN (1=1) THEN (SELECT "foo") ELSE (NULL) END'
"""
if payload and payload.find("IF") > -1:
while payload.find("IF(") > -1:
index = payload.find("IF(")
depth = 1
commas, end = [], None
for i in xrange(index + len("IF("), len(payload)):
if depth == 1 and payload[i] == ',':
commas.append(i)
elif depth == 1 and payload[i] == ')':
end = i
break
elif payload[i] == '(':
depth += 1
elif payload[i] == ')':
depth -= 1
if len(commas) == 2 and end:
a = payload[index + len("IF("):commas[0]].strip("()")
b = payload[commas[0] + 1:commas[1]].lstrip().strip("()")
c = payload[commas[1] + 1:end].lstrip().strip("()")
newVal = "CASE WHEN (%s) THEN (%s) ELSE (%s) END" % (a, b, c)
payload = payload[:index] + newVal + payload[end + 1:]
else:
break
return payload