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():
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
else:
if not (Backend.isDbms(DBMS.SQLITE) and not isDBMSVersionAtLeast('3')):
@@ -596,6 +596,9 @@ class Agent(object):
if not _:
fieldsSelectFrom = None
if re.search(r"\bWHERE\b.+(MIN|MAX)", query, re.I):
fieldsMinMaxstr = None
fieldsToCastStr = fieldsNoSelect
if fieldsSubstr:

View File

@@ -1696,11 +1696,20 @@ def _cleanupOptions():
try:
conf.ignoreCode = [int(_) for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.ignoreCode)]
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)
else:
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:
conf.paramFilter = [_.strip() for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.paramFilter.upper())]
else:
@@ -2655,6 +2664,9 @@ def _basicOptionValidation():
raise SqlmapSyntaxException(errMsg)
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:
re.compile(conf.paramExclude)
except Exception as ex:

View File

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

View File

@@ -20,7 +20,7 @@ from thirdparty import six
from thirdparty.six import unichr as _unichr
# 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_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

@@ -201,8 +201,11 @@ def cmdLineParser(argv=None):
request.add_argument("--auth-file", dest="authFile",
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",
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",
help="Ignore system default proxy settings")
@@ -628,6 +631,9 @@ def cmdLineParser(argv=None):
general.add_argument("-t", dest="trafficFile",
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",
help="Set predefined answers (e.g. \"quit=N,follow=N\")")

View File

@@ -767,6 +767,11 @@ class Connect(object):
if not multipart:
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 == _http_client.UNAUTHORIZED:
errMsg = "not authorized, try to provide right HTTP "
@@ -921,6 +926,12 @@ class Connect(object):
errMsg += "function '%s' ('%s')" % (function.__name__, getSafeExString(ex))
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.lastCode = code

View File

@@ -501,10 +501,15 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser
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:
warnMsg = "in case of continuous data retrieval problems you are advised to try "
warnMsg += "a switch '--no-cast' "
warnMsg += "or switch '--hex'" if hasattr(queries[Backend.getIdentifiedDbms()], "hex") else ""
singleTimeWarnMessage(warnMsg)
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 += "a switch '--no-cast' "
warnMsg += "or switch '--hex'" if hasattr(queries[Backend.getIdentifiedDbms()], "hex") else ""
singleTimeWarnMessage(warnMsg)
# Dirty patch (MSSQL --binary-fields with 0x31003200...)
if Backend.isDbms(DBMS.MSSQL) and conf.binaryFields:

View File

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

View File

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

View File

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