From 0fc4ebdc1b3f37a3cdf60bdbcd1b36c004b03a75 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Sun, 16 Jan 2011 01:17:09 +0000 Subject: [PATCH] Major bug fix. Minor code refactoring. --- lib/controller/checks.py | 11 ++++++----- lib/core/agent.py | 23 +++++++---------------- lib/techniques/inband/union/test.py | 20 ++++++++++---------- lib/techniques/inband/union/use.py | 2 +- 4 files changed, 24 insertions(+), 32 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 3513776ad..76d0bafc2 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -72,11 +72,13 @@ def unescapeDbms(payload, injection, dbms): # provided a DBMS (conf.dbms), unescape the strings between single # quotes in the payload if injection.dbms is not None: - payload = unescape(payload, injection.dbms) + payload = unescape(payload, dbms=injection.dbms) elif dbms is not None: - payload = unescape(payload, dbms) + payload = unescape(payload, dbms=dbms) elif conf.dbms is not None: - payload = unescape(payload, conf.dbms) + payload = unescape(payload, dbms=conf.dbms) + elif getIdentifiedDBMS() is not None: + payload = unescape(payload, dbms=getIdentifiedDBMS()) return payload @@ -387,8 +389,7 @@ def checkSqlInjection(place, parameter, value): logger.warn(warnMsg) configUnion(test.request.char, test.request.columns) - dbmsToUnescape = kb.misc.fpDbms if kb.misc.fpDbms 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) if isinstance(reqPayload, basestring): infoMsg = "%s parameter '%s' is '%s' injectable" % (place, parameter, title) diff --git a/lib/core/agent.py b/lib/core/agent.py index 32cd6f08b..c08e1dd70 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -422,26 +422,20 @@ class Agent: fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, _, fieldsToCastStr = self.getFields(query) if getIdentifiedDBMS() == DBMS.MYSQL: - if fieldsSelectCase: - concatenatedQuery = concatenatedQuery.replace("SELECT ", "CONCAT('%s'," % kb.misc.start, 1) - concatenatedQuery += ",'%s')" % kb.misc.stop - elif fieldsSelectFrom: + if fieldsSelectFrom: concatenatedQuery = concatenatedQuery.replace("SELECT ", "CONCAT('%s'," % kb.misc.start, 1) concatenatedQuery = concatenatedQuery.replace(" FROM ", ",'%s') FROM " % kb.misc.stop, 1) - elif fieldsSelect: + elif (fieldsSelect, fieldsSelectCase): concatenatedQuery = concatenatedQuery.replace("SELECT ", "CONCAT('%s'," % kb.misc.start, 1) concatenatedQuery += ",'%s')" % kb.misc.stop elif fieldsNoSelect: concatenatedQuery = "CONCAT('%s',%s,'%s')" % (kb.misc.start, concatenatedQuery, kb.misc.stop) elif getIdentifiedDBMS() in ( DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE ): - if fieldsSelectCase: - concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.misc.start, 1) - concatenatedQuery += "||'%s'" % kb.misc.stop - elif fieldsSelectFrom: + if fieldsSelectFrom: concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.misc.start, 1) concatenatedQuery = concatenatedQuery.replace(" FROM ", "||'%s' FROM " % kb.misc.stop, 1) - elif fieldsSelect: + elif (fieldsSelect, fieldsSelectCase): concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.misc.start, 1) concatenatedQuery += "||'%s'" % kb.misc.stop elif fieldsNoSelect: @@ -455,13 +449,10 @@ class Agent: topNum = re.search("\ASELECT\s+TOP\s+([\d]+)\s+", concatenatedQuery, re.I).group(1) concatenatedQuery = concatenatedQuery.replace("SELECT TOP %s " % topNum, "TOP %s '%s'+" % (topNum, kb.misc.start), 1) concatenatedQuery = concatenatedQuery.replace(" FROM ", "+'%s' FROM " % kb.misc.stop, 1) - elif fieldsSelectCase: - concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % kb.misc.start, 1) - concatenatedQuery += "+'%s'" % kb.misc.stop elif fieldsSelectFrom: concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % kb.misc.start, 1) concatenatedQuery = concatenatedQuery.replace(" FROM ", "+'%s' FROM " % kb.misc.stop, 1) - elif fieldsSelect: + elif (fieldsSelect, fieldsSelectCase): concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % kb.misc.start, 1) concatenatedQuery += "+'%s'" % kb.misc.stop elif fieldsNoSelect: @@ -524,7 +515,7 @@ class Agent: inbandQuery += ", " if element == position: - if " FROM " in query and "EXISTS(" not in query and not query.startswith("SELECT ") and "(CASE WHEN (" not in query: + if " FROM " in query and "EXISTS(" not in query and not query.startswith("SELECT "): conditionIndex = query.index(" FROM ") inbandQuery += query[:conditionIndex] else: @@ -532,7 +523,7 @@ class Agent: else: inbandQuery += char - if " FROM " in query and "EXISTS(" not in query and not query.startswith("SELECT ") and "(CASE WHEN (" not in query: + if " FROM " in query and "EXISTS(" not in query and not query.startswith("SELECT "): conditionIndex = query.index(" FROM ") inbandQuery += query[conditionIndex:] diff --git a/lib/techniques/inband/union/test.py b/lib/techniques/inband/union/test.py index 9474edbb1..63cb00c53 100644 --- a/lib/techniques/inband/union/test.py +++ b/lib/techniques/inband/union/test.py @@ -27,7 +27,7 @@ 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): +def __unionPosition(comment, place, parameter, value, prefix, suffix, count, where=1): validPayload = None vector = None @@ -38,7 +38,7 @@ def __unionPosition(comment, place, parameter, value, prefix, suffix, dbms, coun # Prepare expression with delimiters randQuery = randomStr() randQueryProcessed = agent.concatQuery("\'%s\'" % randQuery) - randQueryUnescaped = unescaper.unescape(randQueryProcessed, dbms=dbms) + randQueryUnescaped = unescaper.unescape(randQueryProcessed) # Forge the inband SQL injection request query = agent.forgeInbandQuery(randQueryUnescaped, position, count, comment, prefix, suffix, conf.uChar) @@ -55,7 +55,7 @@ def __unionPosition(comment, place, parameter, value, prefix, suffix, dbms, coun # Prepare expression with delimiters randQuery2 = randomStr() randQueryProcessed2 = agent.concatQuery("\'%s\'" % randQuery2) - randQueryUnescaped2 = unescaper.unescape(randQueryProcessed2, dbms=dbms) + randQueryUnescaped2 = unescaper.unescape(randQueryProcessed2) # Confirm that it is a full inband SQL injection query = agent.forgeInbandQuery(randQueryUnescaped, position, count, comment, prefix, suffix, conf.uChar, multipleUnions=randQueryUnescaped2) @@ -71,22 +71,22 @@ def __unionPosition(comment, place, parameter, value, prefix, suffix, dbms, coun return validPayload, vector -def __unionConfirm(comment, place, parameter, value, prefix, suffix, dbms, count): +def __unionConfirm(comment, place, parameter, value, prefix, suffix, count): validPayload = None vector = None # Confirm the inband SQL injection and get the exact column # position which can be used to extract data - validPayload, vector = __unionPosition(comment, place, parameter, value, prefix, suffix, dbms, count) + validPayload, vector = __unionPosition(comment, place, parameter, value, prefix, suffix, count) # Assure that the above function found the exploitable full inband # SQL injection position if not validPayload: - validPayload, vector = __unionPosition(comment, place, parameter, value, prefix, suffix, dbms, count, where=2) + validPayload, vector = __unionPosition(comment, place, parameter, value, prefix, suffix, count, where=2) return validPayload, vector -def __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix, dbms): +def __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix): """ This method tests if the target url is affected by an inband SQL injection vulnerability. The test is done up to 50 columns @@ -111,7 +111,7 @@ def __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix debugMsg = "testing %s columns (%d%%)" % (status, round(100.0*count/conf.uColsStop)) logger.debug(debugMsg) - validPayload, vector = __unionConfirm(comment, place, parameter, value, prefix, suffix, dbms, count) + validPayload, vector = __unionConfirm(comment, place, parameter, value, prefix, suffix, count) if validPayload: break @@ -120,7 +120,7 @@ def __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix return validPayload, vector -def unionTest(comment, place, parameter, value, prefix, suffix, dbms): +def unionTest(comment, place, parameter, value, prefix, suffix): """ This method tests if the target url is affected by an inband SQL injection vulnerability. The test is done up to 3*50 times @@ -130,7 +130,7 @@ def unionTest(comment, place, parameter, value, prefix, suffix, dbms): return kb.technique = PAYLOAD.TECHNIQUE.UNION - validPayload, vector = __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix, dbms) + validPayload, vector = __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) if validPayload: validPayload = agent.removePayloadDelimiters(validPayload, False) diff --git a/lib/techniques/inband/union/use.py b/lib/techniques/inband/union/use.py index 497cb65ff..1cc04c79a 100644 --- a/lib/techniques/inband/union/use.py +++ b/lib/techniques/inband/union/use.py @@ -67,7 +67,7 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, unpack # entry per time # NOTE: I assume that only queries that get data from a table can # return multiple entries - if " FROM " in expression and "EXISTS(" not in expression: + if " FROM " in expression.upper() and " FROM DUAL" not in expression.upper() and "EXISTS(" not in expression.upper(): limitRegExp = re.search(queries[getIdentifiedDBMS()].limitregexp.query, expression, re.I) if limitRegExp: