mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2025-06-07 06:33:06 +03:00
Properly deal with partial (single entry) UNION injections.
Got rid of kb.union*, now it's all stored/used from kb.injection. Minor bug fix with where=2 detection phase.
This commit is contained in:
parent
d7a7993e0d
commit
af9725214a
|
@ -378,8 +378,12 @@ def checkSqlInjection(place, parameter, value):
|
||||||
|
|
||||||
# In case of UNION query SQL injection
|
# In case of UNION query SQL injection
|
||||||
elif method == PAYLOAD.METHOD.UNION:
|
elif method == PAYLOAD.METHOD.UNION:
|
||||||
|
# Test for UNION injection and set the sample
|
||||||
|
# payload as well as the vector.
|
||||||
|
# NOTE: vector is set to a tuple with 6 elements,
|
||||||
|
# used afterwards by Agent.forgeInbandQuery()
|
||||||
|
# method to forge the UNION query payload
|
||||||
configUnion(test.request.char, test.request.columns)
|
configUnion(test.request.char, test.request.columns)
|
||||||
|
|
||||||
dbmsToUnescape = dbms if dbms is not None else injection.dbms
|
dbmsToUnescape = dbms if dbms is not None else injection.dbms
|
||||||
reqPayload, vector = unionTest(comment, place, parameter, value, prefix, suffix, dbmsToUnescape)
|
reqPayload, vector = unionTest(comment, place, parameter, value, prefix, suffix, dbmsToUnescape)
|
||||||
|
|
||||||
|
@ -389,6 +393,10 @@ def checkSqlInjection(place, parameter, value):
|
||||||
|
|
||||||
injectable = True
|
injectable = True
|
||||||
|
|
||||||
|
# Overwrite 'where' because it can differ
|
||||||
|
# in unionTest()'s vector (1 or 2)
|
||||||
|
where = vector[5]
|
||||||
|
|
||||||
# If the injection test was successful feed the injection
|
# If the injection test was successful feed the injection
|
||||||
# object with the test's details
|
# object with the test's details
|
||||||
if injectable is True:
|
if injectable is True:
|
||||||
|
|
|
@ -1164,7 +1164,6 @@ def __setKnowledgeBaseAttributes(flushAll=True):
|
||||||
kb.threadContinue = True
|
kb.threadContinue = True
|
||||||
kb.threadException = False
|
kb.threadException = False
|
||||||
kb.threadData = {}
|
kb.threadData = {}
|
||||||
kb.unionNegative = False
|
|
||||||
|
|
||||||
if flushAll:
|
if flushAll:
|
||||||
kb.keywords = set(getFileItems(paths.SQL_KEYWORDS))
|
kb.keywords = set(getFileItems(paths.SQL_KEYWORDS))
|
||||||
|
|
|
@ -191,10 +191,6 @@ def setOs():
|
||||||
if condition:
|
if condition:
|
||||||
dataToSessionFile("[%s][%s][%s][OS][%s]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), safeFormatString(kb.os)))
|
dataToSessionFile("[%s][%s][%s][OS][%s]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), safeFormatString(kb.os)))
|
||||||
|
|
||||||
def setUnion(negative=False):
|
|
||||||
if negative:
|
|
||||||
kb.unionNegative = True
|
|
||||||
|
|
||||||
def setRemoteTempPath():
|
def setRemoteTempPath():
|
||||||
condition = (
|
condition = (
|
||||||
not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and
|
not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and
|
||||||
|
|
|
@ -425,9 +425,6 @@ def getValue(expression, blind=True, inband=True, error=True, time=True, fromUse
|
||||||
count += 1
|
count += 1
|
||||||
found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE
|
found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE
|
||||||
|
|
||||||
oldUnionNegative = kb.unionNegative
|
|
||||||
kb.unionNegative = False
|
|
||||||
|
|
||||||
if error and isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) and not found:
|
if error and isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) and not found:
|
||||||
kb.technique = PAYLOAD.TECHNIQUE.ERROR
|
kb.technique = PAYLOAD.TECHNIQUE.ERROR
|
||||||
|
|
||||||
|
@ -461,8 +458,6 @@ def getValue(expression, blind=True, inband=True, error=True, time=True, fromUse
|
||||||
else:
|
else:
|
||||||
value = __goInferenceProxy(query, fromUser, expected, batch, resumeValue, unpack, charsetType, firstChar, lastChar)
|
value = __goInferenceProxy(query, fromUser, expected, batch, resumeValue, unpack, charsetType, firstChar, lastChar)
|
||||||
|
|
||||||
kb.unionNegative = oldUnionNegative
|
|
||||||
|
|
||||||
if value and isinstance(value, basestring):
|
if value and isinstance(value, basestring):
|
||||||
value = value.strip()
|
value = value.strip()
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -21,14 +21,13 @@ from lib.core.data import logger
|
||||||
from lib.core.data import queries
|
from lib.core.data import queries
|
||||||
from lib.core.enums import DBMS
|
from lib.core.enums import DBMS
|
||||||
from lib.core.enums import PAYLOAD
|
from lib.core.enums import PAYLOAD
|
||||||
from lib.core.session import setUnion
|
|
||||||
from lib.core.unescaper import unescaper
|
from lib.core.unescaper import unescaper
|
||||||
from lib.parse.html import htmlParser
|
from lib.parse.html import htmlParser
|
||||||
from lib.request.connect import Connect as Request
|
from lib.request.connect import Connect as Request
|
||||||
|
|
||||||
def __unionPosition(comment, place, parameter, value, prefix, suffix, dbms, count, where=1):
|
def __unionPosition(comment, place, parameter, value, prefix, suffix, dbms, count, where=1):
|
||||||
validPayload = None
|
validPayload = None
|
||||||
unionVector = None
|
vector = None
|
||||||
|
|
||||||
# For each column of the table (# of NULL) perform a request using
|
# For each column of the table (# of NULL) perform a request using
|
||||||
# the UNION ALL SELECT statement to test it the target url is
|
# the UNION ALL SELECT statement to test it the target url is
|
||||||
|
@ -48,7 +47,7 @@ def __unionPosition(comment, place, parameter, value, prefix, suffix, dbms, coun
|
||||||
|
|
||||||
if resultPage and randQuery in resultPage and " UNION ALL SELECT " not in resultPage:
|
if resultPage and randQuery in resultPage and " UNION ALL SELECT " not in resultPage:
|
||||||
validPayload = payload
|
validPayload = payload
|
||||||
unionVector = (exprPosition, count, comment, prefix, suffix)
|
vector = (exprPosition, count, comment, prefix, suffix, where)
|
||||||
|
|
||||||
if where == 1:
|
if where == 1:
|
||||||
# Prepare expression with delimiters
|
# Prepare expression with delimiters
|
||||||
|
@ -64,34 +63,26 @@ def __unionPosition(comment, place, parameter, value, prefix, suffix, dbms, coun
|
||||||
resultPage, _ = Request.queryPage(payload, place=place, content=True)
|
resultPage, _ = Request.queryPage(payload, place=place, content=True)
|
||||||
|
|
||||||
if resultPage and (randQuery not in resultPage or randQuery2 not in resultPage):
|
if resultPage and (randQuery not in resultPage or randQuery2 not in resultPage):
|
||||||
setUnion(negative=True)
|
vector = (exprPosition, count, comment, prefix, suffix, 2)
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
return validPayload, unionVector
|
return validPayload, vector
|
||||||
|
|
||||||
def __unionConfirm(comment, place, parameter, value, prefix, suffix, dbms, count):
|
def __unionConfirm(comment, place, parameter, value, prefix, suffix, dbms, count):
|
||||||
validPayload = None
|
validPayload = None
|
||||||
unionVector = None
|
vector = None
|
||||||
|
|
||||||
# Confirm the inband SQL injection and get the exact column
|
# Confirm the inband SQL injection and get the exact column
|
||||||
# position which can be used to extract data
|
# position which can be used to extract data
|
||||||
validPayload, unionVector = __unionPosition(comment, place, parameter, value, prefix, suffix, dbms, count)
|
validPayload, vector = __unionPosition(comment, place, parameter, value, prefix, suffix, dbms, count)
|
||||||
|
|
||||||
# Assure that the above function found the exploitable full inband
|
# Assure that the above function found the exploitable full inband
|
||||||
# SQL injection position
|
# SQL injection position
|
||||||
if not validPayload:
|
if not validPayload:
|
||||||
validPayload, unionVector = __unionPosition(comment, place, parameter, value, prefix, suffix, dbms, count, where=2)
|
validPayload, vector = __unionPosition(comment, place, parameter, value, prefix, suffix, dbms, count, where=2)
|
||||||
|
|
||||||
# Assure that the above function found the exploitable partial
|
return validPayload, vector
|
||||||
# (single entry) inband SQL injection position with negative
|
|
||||||
# parameter validPayload
|
|
||||||
if not validPayload:
|
|
||||||
return None, None
|
|
||||||
else:
|
|
||||||
setUnion(negative=True)
|
|
||||||
|
|
||||||
return validPayload, unionVector
|
|
||||||
|
|
||||||
def __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix, dbms):
|
def __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix, dbms):
|
||||||
"""
|
"""
|
||||||
|
@ -101,7 +92,7 @@ def __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix
|
||||||
"""
|
"""
|
||||||
|
|
||||||
validPayload = None
|
validPayload = None
|
||||||
unionVector = None
|
vector = None
|
||||||
query = agent.prefixQuery("UNION ALL SELECT %s" % conf.uChar)
|
query = agent.prefixQuery("UNION ALL SELECT %s" % conf.uChar)
|
||||||
|
|
||||||
for count in range(conf.uColsStart, conf.uColsStop+1):
|
for count in range(conf.uColsStart, conf.uColsStop+1):
|
||||||
|
@ -118,14 +109,14 @@ def __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix
|
||||||
debugMsg = "testing number of columns: %s" % status
|
debugMsg = "testing number of columns: %s" % status
|
||||||
logger.debug(debugMsg)
|
logger.debug(debugMsg)
|
||||||
|
|
||||||
validPayload, unionVector = __unionConfirm(comment, place, parameter, value, prefix, suffix, dbms, count)
|
validPayload, vector = __unionConfirm(comment, place, parameter, value, prefix, suffix, dbms, count)
|
||||||
|
|
||||||
if validPayload:
|
if validPayload:
|
||||||
break
|
break
|
||||||
|
|
||||||
clearConsoleLine(True)
|
clearConsoleLine(True)
|
||||||
|
|
||||||
return validPayload, unionVector
|
return validPayload, vector
|
||||||
|
|
||||||
def unionTest(comment, place, parameter, value, prefix, suffix, dbms):
|
def unionTest(comment, place, parameter, value, prefix, suffix, dbms):
|
||||||
"""
|
"""
|
||||||
|
@ -138,9 +129,9 @@ def unionTest(comment, place, parameter, value, prefix, suffix, dbms):
|
||||||
|
|
||||||
oldTechnique = kb.technique
|
oldTechnique = kb.technique
|
||||||
kb.technique = PAYLOAD.TECHNIQUE.UNION
|
kb.technique = PAYLOAD.TECHNIQUE.UNION
|
||||||
validPayload, unionVector = __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix, dbms)
|
validPayload, vector = __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix, dbms)
|
||||||
|
|
||||||
if validPayload:
|
if validPayload:
|
||||||
validPayload = agent.removePayloadDelimiters(validPayload, False)
|
validPayload = agent.removePayloadDelimiters(validPayload, False)
|
||||||
|
|
||||||
return validPayload, unionVector
|
return validPayload, vector
|
||||||
|
|
|
@ -56,7 +56,7 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, nullCh
|
||||||
expression = agent.concatQuery(expression, unpack)
|
expression = agent.concatQuery(expression, unpack)
|
||||||
expression = unescaper.unescape(expression)
|
expression = unescaper.unescape(expression)
|
||||||
|
|
||||||
if kb.unionNegative and not direct:
|
if kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == 2 and not direct:
|
||||||
_, _, _, _, _, expressionFieldsList, expressionFields = agent.getFields(origExpr)
|
_, _, _, _, _, expressionFieldsList, expressionFields = agent.getFields(origExpr)
|
||||||
|
|
||||||
# We have to check if the SQL query might return multiple entries
|
# We have to check if the SQL query might return multiple entries
|
||||||
|
@ -194,6 +194,8 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, nullCh
|
||||||
status = '%d/%d entries (%d%s)' % (count, length, round(100.0*count/length), '%')
|
status = '%d/%d entries (%d%s)' % (count, length, round(100.0*count/length), '%')
|
||||||
dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), status), True)
|
dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), status), True)
|
||||||
|
|
||||||
|
dataToStdout("\n")
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print
|
print
|
||||||
warnMsg = "Ctrl+C detected in dumping phase"
|
warnMsg = "Ctrl+C detected in dumping phase"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user