From 9c42a883be4a3b992a01b653118f00ea6919b0dc Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Fri, 2 Jan 2009 23:26:45 +0000 Subject: [PATCH] Major bug fix to make it work properly with MSSQL custom limited (SELECT TOP ...) queries with both inferential blind and Full UNION query injection --- lib/core/agent.py | 30 +++++++++++++++++++++++------- lib/core/settings.py | 3 ++- lib/request/inject.py | 21 +++++++++++++++++---- lib/techniques/blind/inference.py | 10 +++++----- lib/techniques/inband/union/use.py | 16 +++++++++++++--- 5 files changed, 60 insertions(+), 20 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index a006c98e0..b45dfe242 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -285,7 +285,7 @@ class Agent: if query.startswith("SELECT ") and "(SELECT " in query: fieldsSelectFrom = None - return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsToCastList, fieldsToCastStr + return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsToCastList, fieldsToCastStr def concatQuery(self, query): @@ -317,7 +317,7 @@ class Agent: concatQuery = "" query = query.replace(", ", ",") - fieldsSelectFrom, fieldsSelect, fieldsNoSelect, _, fieldsToCastStr = self.getFields(query) + fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, _, fieldsToCastStr = self.getFields(query) castedFields = self.nullCastConcatFields(fieldsToCastStr) concatQuery = query.replace(fieldsToCastStr, castedFields, 1) @@ -348,7 +348,11 @@ class Agent: concatQuery += " FROM DUAL" elif kb.dbms == "Microsoft SQL Server": - if fieldsSelectFrom: + if fieldsSelectTop: + topNum = re.search("\ASELECT\s+TOP\s+([\d]+)\s+", concatQuery, re.I).group(1) + concatQuery = concatQuery.replace("SELECT TOP %s " % topNum, "TOP %s '%s'+" % (topNum, temp.start), 1) + concatQuery = concatQuery.replace(" FROM ", "+'%s' FROM " % temp.stop, 1) + elif fieldsSelectFrom: concatQuery = concatQuery.replace("SELECT ", "'%s'+" % temp.start, 1) concatQuery = concatQuery.replace(" FROM ", "+'%s' FROM " % temp.stop, 1) elif fieldsSelect: @@ -393,6 +397,11 @@ class Agent: inbandQuery = self.prefixQuery(" UNION ALL SELECT ") + if query.startswith("TOP"): + topNum = re.search("\ATOP\s+([\d]+)\s+", query, re.I).group(1) + query = query[len("TOP %s " % topNum):] + inbandQuery += "TOP %s " % topNum + if not exprPosition: exprPosition = kb.unionPosition @@ -472,10 +481,17 @@ class Agent: if " ORDER BY " in limitedQuery: limitedQuery = limitedQuery[:limitedQuery.index(" ORDER BY ")] - limitedQuery = limitedQuery.replace("SELECT ", (limitStr % 1), 1) - limitedQuery = "%s WHERE %s " % (limitedQuery, field) - limitedQuery += "NOT IN (%s" % (limitStr % num) - limitedQuery += "%s %s)" % (field, fromFrom) + if not limitedQuery.startswith("SELECT TOP "): + limitedQuery = limitedQuery.replace("SELECT ", (limitStr % 1), 1) + limitedQuery = "%s WHERE %s " % (limitedQuery, field) + limitedQuery += "NOT IN (%s" % (limitStr % num) + limitedQuery += "%s %s)" % (field, fromFrom) + else: + topNums = re.search("\ASELECT\s+TOP\s+([\d]+)\s+.+?\s+FROM\s+.+?\s+WHERE\s+.+?\s+NOT\s+IN\s+\(SELECT\s+TOP\s+([\d]+)\s+", limitedQuery, re.I).groups() + quantityTopNums = topNums[0] + limitedQuery = limitedQuery.replace("SELECT TOP %s" % quantityTopNums, "SELECT TOP 1", 1) + startTopNums = topNums[1] + limitedQuery = limitedQuery.replace(" (SELECT TOP %s" % startTopNums, " (SELECT TOP %d" % num) return limitedQuery diff --git a/lib/core/settings.py b/lib/core/settings.py index da8ed8e51..29ab96cb1 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -30,7 +30,7 @@ import sys # sqlmap version and site -VERSION = "0.6.4-rc3" +VERSION = "0.6.4-rc4" VERSION_STRING = "sqlmap/%s" % VERSION SITE = "http://sqlmap.sourceforge.net" @@ -73,6 +73,7 @@ MATCH_RATIO = 0.9 SQL_STATEMENTS = { "SQL SELECT statement": ( "select ", + "select top ", " from ", " from dual", " where ", diff --git a/lib/request/inject.py b/lib/request/inject.py index e8bb3488c..0cb51e4d3 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -81,7 +81,7 @@ def __goInferenceFields(expression, expressionFields, expressionFieldsList, payl continue if isinstance(num, int): - origExpr = expression + origExpr = expression expression = agent.limitQuery(num, expression, field) if "ROWNUM" in expressionFieldsList: @@ -89,7 +89,7 @@ def __goInferenceFields(expression, expressionFields, expressionFieldsList, payl else: expressionReplaced = expression - output = resume(expressionReplaced, payload) + output = resume(expressionReplaced, payload) if not output or ( expected == "int" and not output.isdigit() ): if output: @@ -131,7 +131,7 @@ def __goInferenceProxy(expression, fromUser=False, expected=None): return output if kb.dbmsDetected: - _, _, _, expressionFieldsList, expressionFields = agent.getFields(expression) + _, _, _, _, expressionFieldsList, expressionFields = agent.getFields(expression) if len(expressionFieldsList) > 1: infoMsg = "the SQL query provided has more than a field. " @@ -159,7 +159,17 @@ def __goInferenceProxy(expression, fromUser=False, expected=None): stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 - elif kb.dbms in ( "Oracle", "Microsoft SQL Server" ): + elif kb.dbms == "Microsoft SQL Server": + limitGroupStart = queries[kb.dbms].limitgroupstart + limitGroupStop = queries[kb.dbms].limitgroupstop + + if limitGroupStart.isdigit(): + startLimit = int(limitRegExp.group(int(limitGroupStart))) + + stopLimit = limitRegExp.group(int(limitGroupStop)) + limitCond = int(stopLimit) > 1 + + elif kb.dbms == "Oracle": limitCond = False else: limitCond = True @@ -178,6 +188,9 @@ def __goInferenceProxy(expression, fromUser=False, expected=None): untilLimitChar = expression.index(queries[kb.dbms].limitstring) expression = expression[:untilLimitChar] + elif kb.dbms == "Microsoft SQL Server": + stopLimit += startLimit + if not stopLimit or stopLimit <= 1: if kb.dbms == "Oracle" and expression.endswith("FROM DUAL"): test = "n" diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index fc7992f23..415f971df 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -54,12 +54,12 @@ def bisection(payload, expression, length=None): finalValue = "" if kb.dbmsDetected: - _, _, _, _, fieldToCastStr = agent.getFields(expression) - nulledCastedField = agent.nullAndCastField(fieldToCastStr) - expressionReplaced = expression.replace(fieldToCastStr, nulledCastedField, 1) - expressionUnescaped = unescaper.unescape(expressionReplaced) + _, _, _, _, _, fieldToCastStr = agent.getFields(expression) + nulledCastedField = agent.nullAndCastField(fieldToCastStr) + expressionReplaced = expression.replace(fieldToCastStr, nulledCastedField, 1) + expressionUnescaped = unescaper.unescape(expressionReplaced) else: - expressionUnescaped = unescaper.unescape(expression) + expressionUnescaped = unescaper.unescape(expression) infoMsg = "query: %s" % expressionUnescaped logger.info(infoMsg) diff --git a/lib/techniques/inband/union/use.py b/lib/techniques/inband/union/use.py index d4b8418df..114ed3cdc 100644 --- a/lib/techniques/inband/union/use.py +++ b/lib/techniques/inband/union/use.py @@ -159,7 +159,7 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False): conf.paramNegative = True if conf.paramNegative == True and direct == False: - _, _, _, expressionFieldsList, expressionFields = agent.getFields(origExpr) + _, _, _, _, expressionFieldsList, expressionFields = agent.getFields(origExpr) if len(expressionFieldsList) > 1: infoMsg = "the SQL query provided has more than a field. " @@ -187,7 +187,17 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False): stopLimit = limitRegExp.group(int(limitGroupStop)) limitCond = int(stopLimit) > 1 - elif kb.dbms in ( "Oracle", "Microsoft SQL Server" ): + elif kb.dbms == "Microsoft SQL Server": + limitGroupStart = queries[kb.dbms].limitgroupstart + limitGroupStop = queries[kb.dbms].limitgroupstop + + if limitGroupStart.isdigit(): + startLimit = int(limitRegExp.group(int(limitGroupStart))) + + stopLimit = limitRegExp.group(int(limitGroupStop)) + limitCond = int(stopLimit) > 1 + + elif kb.dbms == "Oracle": limitCond = False else: limitCond = True @@ -287,7 +297,7 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False): else: # Forge the inband SQL injection request - query = agent.forgeInbandQuery(expression) + query = agent.forgeInbandQuery(expression) payload = agent.payload(newValue=query) infoMsg = "query: %s" % query