Major bug fix to make it work properly with MSSQL custom limited (SELECT

TOP ...) queries with both inferential blind and Full UNION query
injection
This commit is contained in:
Bernardo Damele 2009-01-02 23:26:45 +00:00
parent 2cc3bb2f6a
commit 9c42a883be
5 changed files with 60 additions and 20 deletions

View File

@ -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

View File

@ -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 ",

View File

@ -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"

View File

@ -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)

View File

@ -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