rework some of the logic of the detection phase based on identified DBMS along the way

This commit is contained in:
Bernardo Damele 2015-02-21 02:23:42 +00:00
parent 4f939b5719
commit 52dd92748a

View File

@ -64,6 +64,7 @@ from lib.core.settings import DUMMY_XSS_CHECK_APPENDIX
from lib.core.settings import FORMAT_EXCEPTION_STRINGS from lib.core.settings import FORMAT_EXCEPTION_STRINGS
from lib.core.settings import HEURISTIC_CHECK_ALPHABET from lib.core.settings import HEURISTIC_CHECK_ALPHABET
from lib.core.settings import SUHOSIN_MAX_VALUE_LENGTH from lib.core.settings import SUHOSIN_MAX_VALUE_LENGTH
from lib.core.settings import SUPPORTED_DBMS
from lib.core.settings import UNKNOWN_DBMS from lib.core.settings import UNKNOWN_DBMS
from lib.core.settings import URI_HTTP_HEADER from lib.core.settings import URI_HTTP_HEADER
from lib.core.settings import LOWER_RATIO_BOUND from lib.core.settings import LOWER_RATIO_BOUND
@ -89,7 +90,6 @@ def checkSqlInjection(place, parameter, value):
kb.testMode = True kb.testMode = True
paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place
tests = getSortedInjectionTests() tests = getSortedInjectionTests()
while tests: while tests:
@ -100,26 +100,38 @@ def checkSqlInjection(place, parameter, value):
break break
if conf.dbms is None: if conf.dbms is None:
# If the DBMS has not yet been fingerprinted (via simple heuristic check
# or via DBMS-specific payload) and boolean-based blind has been identified
# then attempt to identify with a simple DBMS specific boolean-based
# test what the DBMS may be
if not injection.dbms and PAYLOAD.TECHNIQUE.BOOLEAN in injection.data: if not injection.dbms and PAYLOAD.TECHNIQUE.BOOLEAN in injection.data:
if not Backend.getIdentifiedDbms() and not kb.heuristicDbms: if not Backend.getIdentifiedDbms() and kb.heuristicDbms is False:
kb.heuristicDbms = heuristicCheckDbms(injection) or UNKNOWN_DBMS kb.heuristicDbms = heuristicCheckDbms(injection)
if not conf.testFilter and (Backend.getErrorParsedDBMSes() or kb.heuristicDbms) not in ([], None, UNKNOWN_DBMS): # If the DBMS has already been fingerprinted (via DBMS-specific
if kb.reduceTests is None and Backend.getErrorParsedDBMSes(): # error message, simple heuristic check or via DBMS-specific
msg = "heuristic (parsing) test showed that the " # payload), ask the user to limit the tests to the fingerprinted
msg += "back-end DBMS could be '%s'. " % (Format.getErrorParsedDBMSes() if Backend.getErrorParsedDBMSes() else kb.heuristicDbms) # DBMS
msg += "Do you want to skip test payloads specific for other DBMSes? [Y/n]" if kb.reduceTests is None and not conf.testFilter and (intersect(Backend.getErrorParsedDBMSes(), \
kb.reduceTests = [] if readInput(msg, default='Y').upper() != 'Y' else (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) SUPPORTED_DBMS, True) or kb.heuristicDbms or injection.dbms):
msg = "it looks like the back-end DBMS is '%s'. " % (Format.getErrorParsedDBMSes() or kb.heuristicDbms or injection.dbms)
msg += "Do you want to skip test payloads specific for other DBMSes? [Y/n]"
kb.reduceTests = (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) if readInput(msg, default='Y').upper() == 'Y' else []
if kb.extendTests is None and (conf.level < 5 or conf.risk < 3): # If the DBMS has already been fingerprinted (via DBMS-specific
_ = (Format.getErrorParsedDBMSes() if Backend.getErrorParsedDBMSes() else kb.heuristicDbms) # error message, via simple heuristic check or via DBMS-specific
msg = "do you want to include all tests for '%s' " % _ # payload), ask the user to extend the tests to all DBMS-specific,
msg += "extending provided level (%d) and risk (%s) values? [Y/n]" % (conf.level, conf.risk) # regardless of --level and --risk values provided
kb.extendTests = [] if readInput(msg, default='Y').upper() != 'Y' else (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) if kb.extendTests is None and not conf.testFilter and (conf.level < 5 or conf.risk < 3) \
elif kb.extendTests is None and (conf.level < 5 or conf.risk < 3): and (intersect(Backend.getErrorParsedDBMSes(), SUPPORTED_DBMS, True) or \
msg = "do you want to include all tests for '%s' " % conf.dbms kb.heuristicDbms or injection.dbms):
msg += "extending provided level (%d) and risk (%s)? [Y/n]" % (conf.level, conf.risk) msg = "do you want to include all tests for '%s' " % (Format.getErrorParsedDBMSes() or kb.heuristicDbms or injection.dbms)
kb.extendTests = [] if readInput(msg, default='Y').upper() != 'Y' else ([conf.dbms]) msg += "extending provided "
msg += "level (%d)" % conf.level if conf.level < 5 else ""
msg += " and " if conf.level < 5 and conf.risk < 3 else ""
msg += "risk (%d)" % conf.risk if conf.risk < 3 else ""
msg += " values? [Y/n]" if conf.level < 5 and conf.risk < 3 else " value? [Y/n]"
kb.extendTests = (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) if readInput(msg, default='Y').upper() == 'Y' else []
title = test.title title = test.title
kb.testType = stype = test.stype kb.testType = stype = test.stype
@ -179,9 +191,6 @@ def checkSqlInjection(place, parameter, value):
logger.debug(debugMsg) logger.debug(debugMsg)
continue continue
# Skip DBMS-specific test if it does not match either the
# previously identified or the user's provided DBMS (either
# from program switch or from parsed error message(s))
if "details" in test and "dbms" in test.details: if "details" in test and "dbms" in test.details:
dbms = test.details.dbms dbms = test.details.dbms
else: else:
@ -198,20 +207,26 @@ def checkSqlInjection(place, parameter, value):
continue continue
if dbms is not None: if dbms is not None:
if injection.dbms is not None and not intersect(injection.dbms, dbms): # Skip DBMS-specific test if it does not match the
# previously identified DBMS (via DBMS-specific payload)
if injection.dbms is not None and not intersect(dbms, injection.dbms, True):
debugMsg = "skipping test '%s' because " % title debugMsg = "skipping test '%s' because " % title
debugMsg += "the back-end DBMS identified is " debugMsg += "the back-end DBMS identified is "
debugMsg += "%s" % injection.dbms debugMsg += "%s" % injection.dbms
logger.debug(debugMsg) logger.debug(debugMsg)
continue continue
if conf.dbms is not None and not intersect(conf.dbms.lower(), [_.lower() for _ in arrayizeValue(dbms)]): # Skip DBMS-specific test if it does not match the user's
# provided DBMS
if conf.dbms is not None and not intersect(dbms, conf.dbms, True):
debugMsg = "skipping test '%s' because " % title debugMsg = "skipping test '%s' because " % title
debugMsg += "the provided DBMS is %s" % conf.dbms debugMsg += "the provided DBMS is %s" % conf.dbms
logger.debug(debugMsg) logger.debug(debugMsg)
continue continue
if kb.reduceTests and not intersect(dbms, kb.reduceTests): # Skip DBMS-specific test if it does not match the
# previously identified DBMS (via DBMS-specific error message)
if kb.reduceTests and not intersect(dbms, kb.reduceTests, True):
debugMsg = "skipping test '%s' because " % title debugMsg = "skipping test '%s' because " % title
debugMsg += "the parsed error message(s) showed " debugMsg += "the parsed error message(s) showed "
debugMsg += "that the back-end DBMS could be " debugMsg += "that the back-end DBMS could be "
@ -484,7 +499,7 @@ def checkSqlInjection(place, parameter, value):
configUnion(test.request.char, test.request.columns) configUnion(test.request.char, test.request.columns)
if not Backend.getIdentifiedDbms(): if not Backend.getIdentifiedDbms():
if kb.heuristicDbms in (None, UNKNOWN_DBMS): if kb.heuristicDbms is None:
warnMsg = "using unescaped version of the test " warnMsg = "using unescaped version of the test "
warnMsg += "because of zero knowledge of the " warnMsg += "because of zero knowledge of the "
warnMsg += "back-end DBMS. You can try to " warnMsg += "back-end DBMS. You can try to "
@ -646,7 +661,13 @@ def checkSqlInjection(place, parameter, value):
return injection return injection
def heuristicCheckDbms(injection): def heuristicCheckDbms(injection):
retVal = None """
This functions is called when boolean-based blind is identified with a
generic payload and the DBMS has not yet been fingerprinted to attempt
to identify with a simple DBMS specific boolean-based test what the DBMS
may be
"""
retVal = False
pushValue(kb.injection) pushValue(kb.injection)
kb.injection = injection kb.injection = injection
@ -667,7 +688,7 @@ def heuristicCheckDbms(injection):
kb.injection = popValue() kb.injection = popValue()
if retVal: if retVal:
infoMsg = "heuristic (extended) test shows that the back-end DBMS " # not as important as "parsing" counter-part (because of false-positives) infoMsg = "heuristic (extended) test shows that the back-end DBMS " # Not as important as "parsing" counter-part (because of false-positives)
infoMsg += "could be '%s' " % retVal infoMsg += "could be '%s' " % retVal
logger.info(infoMsg) logger.info(infoMsg)
@ -798,9 +819,7 @@ def heuristicCheckSqlInjection(place, parameter):
return None return None
origValue = conf.paramDict[place][parameter] origValue = conf.paramDict[place][parameter]
paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place
prefix = "" prefix = ""
suffix = "" suffix = ""
@ -812,6 +831,7 @@ def heuristicCheckSqlInjection(place, parameter):
suffix = conf.suffix suffix = conf.suffix
randStr = "" randStr = ""
while '\'' not in randStr: while '\'' not in randStr:
randStr = randomStr(length=10, alphabet=HEURISTIC_CHECK_ALPHABET) randStr = randomStr(length=10, alphabet=HEURISTIC_CHECK_ALPHABET)
@ -879,8 +899,7 @@ def heuristicCheckSqlInjection(place, parameter):
if value in (page or ""): if value in (page or ""):
infoMsg = "heuristic (XSS) test shows that %s parameter " % paramType infoMsg = "heuristic (XSS) test shows that %s parameter " % paramType
infoMsg += "'%s' might " % parameter infoMsg += "'%s' might be vulnerable to XSS attacks" % parameter
infoMsg += "be vulnerable to XSS attacks"
logger.info(infoMsg) logger.info(infoMsg)
kb.heuristicMode = False kb.heuristicMode = False