Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9c42a88

Browse files
committed
Major bug fix to make it work properly with MSSQL custom limited (SELECT
TOP ...) queries with both inferential blind and Full UNION query injection
1 parent 2cc3bb2 commit 9c42a88

5 files changed

Lines changed: 60 additions & 20 deletions

File tree

lib/core/agent.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ def getFields(self, query):
285285
if query.startswith("SELECT ") and "(SELECT " in query:
286286
fieldsSelectFrom = None
287287

288-
return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsToCastList, fieldsToCastStr
288+
return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsToCastList, fieldsToCastStr
289289

290290

291291
def concatQuery(self, query):
@@ -317,7 +317,7 @@ def concatQuery(self, query):
317317
concatQuery = ""
318318
query = query.replace(", ", ",")
319319

320-
fieldsSelectFrom, fieldsSelect, fieldsNoSelect, _, fieldsToCastStr = self.getFields(query)
320+
fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, _, fieldsToCastStr = self.getFields(query)
321321
castedFields = self.nullCastConcatFields(fieldsToCastStr)
322322
concatQuery = query.replace(fieldsToCastStr, castedFields, 1)
323323

@@ -348,7 +348,11 @@ def concatQuery(self, query):
348348
concatQuery += " FROM DUAL"
349349

350350
elif kb.dbms == "Microsoft SQL Server":
351-
if fieldsSelectFrom:
351+
if fieldsSelectTop:
352+
topNum = re.search("\ASELECT\s+TOP\s+([\d]+)\s+", concatQuery, re.I).group(1)
353+
concatQuery = concatQuery.replace("SELECT TOP %s " % topNum, "TOP %s '%s'+" % (topNum, temp.start), 1)
354+
concatQuery = concatQuery.replace(" FROM ", "+'%s' FROM " % temp.stop, 1)
355+
elif fieldsSelectFrom:
352356
concatQuery = concatQuery.replace("SELECT ", "'%s'+" % temp.start, 1)
353357
concatQuery = concatQuery.replace(" FROM ", "+'%s' FROM " % temp.stop, 1)
354358
elif fieldsSelect:
@@ -393,6 +397,11 @@ def forgeInbandQuery(self, query, exprPosition=None):
393397

394398
inbandQuery = self.prefixQuery(" UNION ALL SELECT ")
395399

400+
if query.startswith("TOP"):
401+
topNum = re.search("\ATOP\s+([\d]+)\s+", query, re.I).group(1)
402+
query = query[len("TOP %s " % topNum):]
403+
inbandQuery += "TOP %s " % topNum
404+
396405
if not exprPosition:
397406
exprPosition = kb.unionPosition
398407

@@ -472,10 +481,17 @@ def limitQuery(self, num, query, field):
472481
if " ORDER BY " in limitedQuery:
473482
limitedQuery = limitedQuery[:limitedQuery.index(" ORDER BY ")]
474483

475-
limitedQuery = limitedQuery.replace("SELECT ", (limitStr % 1), 1)
476-
limitedQuery = "%s WHERE %s " % (limitedQuery, field)
477-
limitedQuery += "NOT IN (%s" % (limitStr % num)
478-
limitedQuery += "%s %s)" % (field, fromFrom)
484+
if not limitedQuery.startswith("SELECT TOP "):
485+
limitedQuery = limitedQuery.replace("SELECT ", (limitStr % 1), 1)
486+
limitedQuery = "%s WHERE %s " % (limitedQuery, field)
487+
limitedQuery += "NOT IN (%s" % (limitStr % num)
488+
limitedQuery += "%s %s)" % (field, fromFrom)
489+
else:
490+
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()
491+
quantityTopNums = topNums[0]
492+
limitedQuery = limitedQuery.replace("SELECT TOP %s" % quantityTopNums, "SELECT TOP 1", 1)
493+
startTopNums = topNums[1]
494+
limitedQuery = limitedQuery.replace(" (SELECT TOP %s" % startTopNums, " (SELECT TOP %d" % num)
479495

480496
return limitedQuery
481497

lib/core/settings.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131

3232
# sqlmap version and site
33-
VERSION = "0.6.4-rc3"
33+
VERSION = "0.6.4-rc4"
3434
VERSION_STRING = "sqlmap/%s" % VERSION
3535
SITE = "http://sqlmap.sourceforge.net"
3636

@@ -73,6 +73,7 @@
7373
SQL_STATEMENTS = {
7474
"SQL SELECT statement": (
7575
"select ",
76+
"select top ",
7677
" from ",
7778
" from dual",
7879
" where ",

lib/request/inject.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,15 @@ def __goInferenceFields(expression, expressionFields, expressionFieldsList, payl
8181
continue
8282

8383
if isinstance(num, int):
84-
origExpr = expression
84+
origExpr = expression
8585
expression = agent.limitQuery(num, expression, field)
8686

8787
if "ROWNUM" in expressionFieldsList:
8888
expressionReplaced = expression.replace(expressionFields, field, 1)
8989
else:
9090
expressionReplaced = expression
9191

92-
output = resume(expressionReplaced, payload)
92+
output = resume(expressionReplaced, payload)
9393

9494
if not output or ( expected == "int" and not output.isdigit() ):
9595
if output:
@@ -131,7 +131,7 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
131131
return output
132132

133133
if kb.dbmsDetected:
134-
_, _, _, expressionFieldsList, expressionFields = agent.getFields(expression)
134+
_, _, _, _, expressionFieldsList, expressionFields = agent.getFields(expression)
135135

136136
if len(expressionFieldsList) > 1:
137137
infoMsg = "the SQL query provided has more than a field. "
@@ -159,7 +159,17 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
159159
stopLimit = limitRegExp.group(int(limitGroupStop))
160160
limitCond = int(stopLimit) > 1
161161

162-
elif kb.dbms in ( "Oracle", "Microsoft SQL Server" ):
162+
elif kb.dbms == "Microsoft SQL Server":
163+
limitGroupStart = queries[kb.dbms].limitgroupstart
164+
limitGroupStop = queries[kb.dbms].limitgroupstop
165+
166+
if limitGroupStart.isdigit():
167+
startLimit = int(limitRegExp.group(int(limitGroupStart)))
168+
169+
stopLimit = limitRegExp.group(int(limitGroupStop))
170+
limitCond = int(stopLimit) > 1
171+
172+
elif kb.dbms == "Oracle":
163173
limitCond = False
164174
else:
165175
limitCond = True
@@ -178,6 +188,9 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
178188
untilLimitChar = expression.index(queries[kb.dbms].limitstring)
179189
expression = expression[:untilLimitChar]
180190

191+
elif kb.dbms == "Microsoft SQL Server":
192+
stopLimit += startLimit
193+
181194
if not stopLimit or stopLimit <= 1:
182195
if kb.dbms == "Oracle" and expression.endswith("FROM DUAL"):
183196
test = "n"

lib/techniques/blind/inference.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ def bisection(payload, expression, length=None):
5454
finalValue = ""
5555

5656
if kb.dbmsDetected:
57-
_, _, _, _, fieldToCastStr = agent.getFields(expression)
58-
nulledCastedField = agent.nullAndCastField(fieldToCastStr)
59-
expressionReplaced = expression.replace(fieldToCastStr, nulledCastedField, 1)
60-
expressionUnescaped = unescaper.unescape(expressionReplaced)
57+
_, _, _, _, _, fieldToCastStr = agent.getFields(expression)
58+
nulledCastedField = agent.nullAndCastField(fieldToCastStr)
59+
expressionReplaced = expression.replace(fieldToCastStr, nulledCastedField, 1)
60+
expressionUnescaped = unescaper.unescape(expressionReplaced)
6161
else:
62-
expressionUnescaped = unescaper.unescape(expression)
62+
expressionUnescaped = unescaper.unescape(expression)
6363

6464
infoMsg = "query: %s" % expressionUnescaped
6565
logger.info(infoMsg)

lib/techniques/inband/union/use.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False):
159159
conf.paramNegative = True
160160

161161
if conf.paramNegative == True and direct == False:
162-
_, _, _, expressionFieldsList, expressionFields = agent.getFields(origExpr)
162+
_, _, _, _, expressionFieldsList, expressionFields = agent.getFields(origExpr)
163163

164164
if len(expressionFieldsList) > 1:
165165
infoMsg = "the SQL query provided has more than a field. "
@@ -187,7 +187,17 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False):
187187
stopLimit = limitRegExp.group(int(limitGroupStop))
188188
limitCond = int(stopLimit) > 1
189189

190-
elif kb.dbms in ( "Oracle", "Microsoft SQL Server" ):
190+
elif kb.dbms == "Microsoft SQL Server":
191+
limitGroupStart = queries[kb.dbms].limitgroupstart
192+
limitGroupStop = queries[kb.dbms].limitgroupstop
193+
194+
if limitGroupStart.isdigit():
195+
startLimit = int(limitRegExp.group(int(limitGroupStart)))
196+
197+
stopLimit = limitRegExp.group(int(limitGroupStop))
198+
limitCond = int(stopLimit) > 1
199+
200+
elif kb.dbms == "Oracle":
191201
limitCond = False
192202
else:
193203
limitCond = True
@@ -287,7 +297,7 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False):
287297

288298
else:
289299
# Forge the inband SQL injection request
290-
query = agent.forgeInbandQuery(expression)
300+
query = agent.forgeInbandQuery(expression)
291301
payload = agent.payload(newValue=query)
292302

293303
infoMsg = "query: %s" % query

0 commit comments

Comments
 (0)