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:
Bernardo Damele 2011-01-12 12:01:32 +00:00
parent d7a7993e0d
commit af9725214a
6 changed files with 25 additions and 34 deletions

View File

@ -378,8 +378,12 @@ def checkSqlInjection(place, parameter, value):
# In case of UNION query SQL injection
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)
dbmsToUnescape = dbms if dbms is not None else injection.dbms
reqPayload, vector = unionTest(comment, place, parameter, value, prefix, suffix, dbmsToUnescape)
@ -389,6 +393,10 @@ def checkSqlInjection(place, parameter, value):
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
# object with the test's details
if injectable is True:

View File

@ -1164,7 +1164,6 @@ def __setKnowledgeBaseAttributes(flushAll=True):
kb.threadContinue = True
kb.threadException = False
kb.threadData = {}
kb.unionNegative = False
if flushAll:
kb.keywords = set(getFileItems(paths.SQL_KEYWORDS))

View File

@ -191,10 +191,6 @@ def setOs():
if condition:
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():
condition = (
not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and

View File

@ -425,9 +425,6 @@ def getValue(expression, blind=True, inband=True, error=True, time=True, fromUse
count += 1
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:
kb.technique = PAYLOAD.TECHNIQUE.ERROR
@ -461,8 +458,6 @@ def getValue(expression, blind=True, inband=True, error=True, time=True, fromUse
else:
value = __goInferenceProxy(query, fromUser, expected, batch, resumeValue, unpack, charsetType, firstChar, lastChar)
kb.unionNegative = oldUnionNegative
if value and isinstance(value, basestring):
value = value.strip()
else:

View File

@ -21,14 +21,13 @@ from lib.core.data import logger
from lib.core.data import queries
from lib.core.enums import DBMS
from lib.core.enums import PAYLOAD
from lib.core.session import setUnion
from lib.core.unescaper import unescaper
from lib.parse.html import htmlParser
from lib.request.connect import Connect as Request
def __unionPosition(comment, place, parameter, value, prefix, suffix, dbms, count, where=1):
validPayload = None
unionVector = None
vector = None
# For each column of the table (# of NULL) perform a request using
# 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:
validPayload = payload
unionVector = (exprPosition, count, comment, prefix, suffix)
vector = (exprPosition, count, comment, prefix, suffix, where)
if where == 1:
# 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)
if resultPage and (randQuery not in resultPage or randQuery2 not in resultPage):
setUnion(negative=True)
vector = (exprPosition, count, comment, prefix, suffix, 2)
break
return validPayload, unionVector
return validPayload, vector
def __unionConfirm(comment, place, parameter, value, prefix, suffix, dbms, count):
validPayload = None
unionVector = None
vector = None
# Confirm the inband SQL injection and get the exact column
# 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
# SQL injection position
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
# (single entry) inband SQL injection position with negative
# parameter validPayload
if not validPayload:
return None, None
else:
setUnion(negative=True)
return validPayload, unionVector
return validPayload, vector
def __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix, dbms):
"""
@ -101,7 +92,7 @@ def __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix
"""
validPayload = None
unionVector = None
vector = None
query = agent.prefixQuery("UNION ALL SELECT %s" % conf.uChar)
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
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:
break
clearConsoleLine(True)
return validPayload, unionVector
return validPayload, vector
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
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:
validPayload = agent.removePayloadDelimiters(validPayload, False)
return validPayload, unionVector
return validPayload, vector

View File

@ -56,7 +56,7 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, nullCh
expression = agent.concatQuery(expression, unpack)
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)
# 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), '%')
dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), status), True)
dataToStdout("\n")
except KeyboardInterrupt:
print
warnMsg = "Ctrl+C detected in dumping phase"