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

Skip to content

Commit 8d06975

Browse files
committed
Major enhancement to make the comparison algorithm work properly also
on url not stables automatically by using the difflib SequenceMatcher object: this changed a lot into the structure of the code, has to be extensively beta-tested! Please, do report bugs on sqlmap-users mailing list if you scout them. Cheers, Bernardo
1 parent 7e8ac16 commit 8d06975

8 files changed

Lines changed: 54 additions & 127 deletions

File tree

lib/controller/checks.py

Lines changed: 29 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -71,19 +71,19 @@ def checkSqlInjection(place, parameter, value, parenthesis):
7171
payload = agent.payload(place, parameter, value, "%s%s%s AND %s%d=%d %s" % (value, prefix, ")" * parenthesis, "(" * parenthesis, randInt, randInt, postfix))
7272
trueResult = Request.queryPage(payload, place)
7373

74-
if trueResult == kb.defaultResult:
74+
if trueResult == True:
7575
payload = agent.payload(place, parameter, value, "%s%s%s AND %s%d=%d %s" % (value, prefix, ")" * parenthesis, "(" * parenthesis, randInt, randInt + 1, postfix))
7676
falseResult = Request.queryPage(payload, place)
7777

78-
if falseResult != kb.defaultResult:
78+
if falseResult != True:
7979
infoMsg = "confirming custom injection "
8080
infoMsg += "on %s parameter '%s'" % (place, parameter)
8181
logger.info(infoMsg)
8282

8383
payload = agent.payload(place, parameter, value, "%s%s%s AND %s%s %s" % (value, prefix, ")" * parenthesis, "(" * parenthesis, randStr, postfix))
8484
falseResult = Request.queryPage(payload, place)
8585

86-
if falseResult != kb.defaultResult:
86+
if falseResult != True:
8787
infoMsg = "%s parameter '%s' is " % (place, parameter)
8888
infoMsg += "custom injectable "
8989
logger.info(infoMsg)
@@ -97,19 +97,19 @@ def checkSqlInjection(place, parameter, value, parenthesis):
9797
payload = agent.payload(place, parameter, value, "%s%s AND %s%d=%d" % (value, ")" * parenthesis, "(" * parenthesis, randInt, randInt))
9898
trueResult = Request.queryPage(payload, place)
9999

100-
if trueResult == kb.defaultResult:
100+
if trueResult == True:
101101
payload = agent.payload(place, parameter, value, "%s%s AND %s%d=%d" % (value, ")" * parenthesis, "(" * parenthesis, randInt, randInt + 1))
102102
falseResult = Request.queryPage(payload, place)
103103

104-
if falseResult != kb.defaultResult:
104+
if falseResult != True:
105105
infoMsg = "confirming unescaped numeric injection "
106106
infoMsg += "on %s parameter '%s'" % (place, parameter)
107107
logger.info(infoMsg)
108108

109109
payload = agent.payload(place, parameter, value, "%s%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
110110
falseResult = Request.queryPage(payload, place)
111111

112-
if falseResult != kb.defaultResult:
112+
if falseResult != True:
113113
infoMsg = "%s parameter '%s' is " % (place, parameter)
114114
infoMsg += "unescaped numeric injectable "
115115
infoMsg += "with %d parenthesis" % parenthesis
@@ -128,19 +128,19 @@ def checkSqlInjection(place, parameter, value, parenthesis):
128128
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s'='%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
129129
trueResult = Request.queryPage(payload, place)
130130

131-
if trueResult == kb.defaultResult:
131+
if trueResult == True:
132132
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s'='%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + randomStr(1)))
133133
falseResult = Request.queryPage(payload, place)
134134

135-
if falseResult != kb.defaultResult:
135+
if falseResult != True:
136136
infoMsg = "confirming single quoted string injection "
137137
infoMsg += "on %s parameter '%s'" % (place, parameter)
138138
logger.info(infoMsg)
139139

140140
payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
141141
falseResult = Request.queryPage(payload, place)
142142

143-
if falseResult != kb.defaultResult:
143+
if falseResult != True:
144144
infoMsg = "%s parameter '%s' is " % (place, parameter)
145145
infoMsg += "single quoted string injectable "
146146
infoMsg += "with %d parenthesis" % parenthesis
@@ -159,19 +159,19 @@ def checkSqlInjection(place, parameter, value, parenthesis):
159159
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s' LIKE '%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
160160
trueResult = Request.queryPage(payload, place)
161161

162-
if trueResult == kb.defaultResult:
162+
if trueResult == True:
163163
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s' LIKE '%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + randomStr(1)))
164164
falseResult = Request.queryPage(payload, place)
165165

166-
if falseResult != kb.defaultResult:
166+
if falseResult != True:
167167
infoMsg = "confirming LIKE single quoted string injection "
168168
infoMsg += "on %s parameter '%s'" % (place, parameter)
169169
logger.info(infoMsg)
170170

171171
payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
172172
falseResult = Request.queryPage(payload, place)
173173

174-
if falseResult != kb.defaultResult:
174+
if falseResult != True:
175175
infoMsg = "%s parameter '%s' is " % (place, parameter)
176176
infoMsg += "LIKE single quoted string injectable "
177177
infoMsg += "with %d parenthesis" % parenthesis
@@ -190,19 +190,19 @@ def checkSqlInjection(place, parameter, value, parenthesis):
190190
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\"=\"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
191191
trueResult = Request.queryPage(payload, place)
192192

193-
if trueResult == kb.defaultResult:
193+
if trueResult == True:
194194
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\"=\"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + randomStr(1)))
195195
falseResult = Request.queryPage(payload, place)
196196

197-
if falseResult != kb.defaultResult:
197+
if falseResult != True:
198198
infoMsg = "confirming double quoted string injection "
199199
infoMsg += "on %s parameter '%s'" % (place, parameter)
200200
logger.info(infoMsg)
201201

202202
payload = agent.payload(place, parameter, value, "%s\"%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
203203
falseResult = Request.queryPage(payload, place)
204204

205-
if falseResult != kb.defaultResult:
205+
if falseResult != True:
206206
infoMsg = "%s parameter '%s' is " % (place, parameter)
207207
infoMsg += "double quoted string injectable "
208208
infoMsg += "with %d parenthesis" % parenthesis
@@ -221,19 +221,19 @@ def checkSqlInjection(place, parameter, value, parenthesis):
221221
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\" LIKE \"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
222222
trueResult = Request.queryPage(payload, place)
223223

224-
if trueResult == kb.defaultResult:
224+
if trueResult == True:
225225
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\" LIKE \"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + randomStr(1)))
226226
falseResult = Request.queryPage(payload, place)
227227

228-
if falseResult != kb.defaultResult:
228+
if falseResult != True:
229229
infoMsg = "confirming LIKE double quoted string injection "
230230
infoMsg += "on %s parameter '%s'" % (place, parameter)
231231
logger.info(infoMsg)
232232

233233
payload = agent.payload(place, parameter, value, "%s\"%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
234234
falseResult = Request.queryPage(payload, place)
235235

236-
if falseResult != kb.defaultResult:
236+
if falseResult != True:
237237
infoMsg = "%s parameter '%s' is " % (place, parameter)
238238
infoMsg += "LIKE double quoted string injectable "
239239
infoMsg += "with %d parenthesis" % parenthesis
@@ -262,7 +262,7 @@ def checkDynParam(place, parameter, value):
262262
payload = agent.payload(place, parameter, value, str(randInt))
263263
dynResult1 = Request.queryPage(payload, place)
264264

265-
if kb.defaultResult == dynResult1:
265+
if True == dynResult1:
266266
return False
267267

268268
infoMsg = "confirming that %s parameter '%s' is dynamic" % (place, parameter)
@@ -274,8 +274,8 @@ def checkDynParam(place, parameter, value):
274274
payload = agent.payload(place, parameter, value, "\"%s" % randomStr())
275275
dynResult3 = Request.queryPage(payload, place)
276276

277-
condition = kb.defaultResult != dynResult2
278-
condition |= kb.defaultResult != dynResult3
277+
condition = True != dynResult2
278+
condition |= True != dynResult3
279279

280280
return condition
281281

@@ -306,52 +306,12 @@ def checkStability():
306306
condition &= secondPage == thirdPage
307307

308308
if condition == False:
309-
# Prepare for the comparison algorithm based on page length value
310-
pageLengths = []
311-
requestsPages = ( firstPage, secondPage, thirdPage )
312-
313-
for requestPages in requestsPages:
314-
pageLengths.append(len(str(requestPages)))
315-
316-
if pageLengths:
317-
conf.pageLengths = ( min(pageLengths) - ( ( min(pageLengths) * 2 ) / 100 ),
318-
max(pageLengths) + ( ( max(pageLengths) * 2 ) / 100 ) )
319-
320-
if conf.pageLengths[0] < conf.pageLengths[1]:
321-
warnMsg = "url is not stable, sqlmap inspected the page "
322-
warnMsg += "and identified that page length can be used "
323-
warnMsg += "in the comparison algorithm"
324-
logger.warn(warnMsg)
325-
326-
kb.defaultResult = True
327-
328-
return True
329-
330-
# Prepare for the comparison algorithm based on page content's
331-
# stable lines subset
332-
counter = 0
333-
firstLines = firstPage.split("\n")
334-
secondLines = secondPage.split("\n")
335-
thirdLines = thirdPage.split("\n")
336-
337-
for firstLine in firstLines:
338-
if counter > len(secondLines) or counter > len(thirdLines):
339-
break
340-
341-
if firstLine in secondLines and firstLine in thirdLines:
342-
conf.equalLines.append(firstLine)
343-
344-
counter += 1
345-
346-
if conf.equalLines:
347-
warnMsg = "url is not stable, sqlmap inspected the page "
348-
warnMsg += "content and identified a stable lines subset "
349-
warnMsg += "to be used in the comparison algorithm"
350-
logger.warn(warnMsg)
351-
352-
kb.defaultResult = True
353-
354-
return True
309+
warnMsg = "url is not stable, sqlmap will base the page "
310+
warnMsg += "comparison on a sequence matcher, if no dynamic nor "
311+
warnMsg += "injectable parameters are detected, refer to user's "
312+
warnMsg += "manual paragraph 'Page comparison' and provide a "
313+
warnMsg += "string or regular expression to match on"
314+
logger.warn(warnMsg)
355315

356316
if condition == True:
357317
logMsg = "url is stable"
@@ -428,7 +388,8 @@ def checkConnection():
428388
logger.info(infoMsg)
429389

430390
try:
431-
kb.defaultResult = Request.queryPage()
391+
page, _ = Request.getPage()
392+
conf.seqMatcher.set_seq1(page)
432393
except sqlmapConnectionException, exceptionMsg:
433394
if conf.multipleTargets:
434395
exceptionMsg += ", skipping to next url"

lib/controller/controller.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -175,18 +175,9 @@ def start():
175175

176176
if not kb.injPlace or not kb.injParameter or not kb.injType:
177177
if not conf.string and not conf.regexp and not conf.eRegexp:
178-
if not checkStability():
179-
errMsg = "url is not stable, try with --string or "
180-
errMsg += "--regexp options, refer to the user's manual "
181-
errMsg += "paragraph 'Page comparison' for details"
182-
183-
if conf.multipleTargets:
184-
errMsg += ", skipping to next url"
185-
logger.warn(errMsg)
186-
187-
continue
188-
else:
189-
raise sqlmapConnectionException, errMsg
178+
# NOTE: this is not needed anymore, leaving only to display
179+
# a warning message to the user in case the page is not stable
180+
checkStability()
190181

191182
for place in conf.parameters.keys():
192183
if not conf.paramDict.has_key(place):

lib/core/option.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626

2727
import cookielib
28+
import difflib
2829
import logging
2930
import os
3031
import re
@@ -570,10 +571,8 @@ def __setConfAttributes():
570571
logger.debug(debugMsg)
571572

572573
conf.cj = None
573-
conf.pageLengths = []
574574
conf.dbmsHandler = None
575575
conf.dumpPath = None
576-
conf.equalLines = []
577576
conf.httpHeaders = []
578577
conf.hostname = None
579578
conf.loggedToOut = None
@@ -586,6 +585,8 @@ def __setConfAttributes():
586585
conf.port = None
587586
conf.retries = 0
588587
conf.scheme = None
588+
#conf.seqMatcher = difflib.SequenceMatcher(lambda x: x in " \t")
589+
conf.seqMatcher = difflib.SequenceMatcher(None)
589590
conf.sessionFP = None
590591
conf.start = True
591592
conf.threadException = False
@@ -601,7 +602,6 @@ def __setKnowledgeBaseAttributes():
601602
logger.debug(debugMsg)
602603

603604
kb.absFilePaths = set()
604-
kb.defaultResult = None
605605
kb.docRoot = None
606606
kb.dbms = None
607607
kb.dbmsDetected = False

lib/request/comparison.py

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
from lib.core.data import conf
3131

3232

33-
def comparison(page, headers=None, content=False):
33+
def comparison(page, headers=None, getSeqMatcher=False):
3434
regExpResults = None
3535

3636
# String to be excluded before calculating page hash
@@ -67,38 +67,15 @@ def comparison(page, headers=None, content=False):
6767
else:
6868
return False
6969

70-
# By default it returns the page content MD5 hash
71-
if not conf.equalLines and not conf.pageLengths:
72-
return md5.new(page).hexdigest()
70+
# By default it returns sequence matcher between the first untouched
71+
# HTTP response page content and this content
72+
conf.seqMatcher.set_seq2(page)
7373

74-
# Comparison algorithm based on page length value
75-
elif conf.pageLengths:
76-
minValue = conf.pageLengths[0]
77-
maxValue = conf.pageLengths[1]
74+
if getSeqMatcher:
75+
return round(conf.seqMatcher.ratio(), 5)
7876

79-
if len(page) >= minValue and len(page) <= maxValue:
80-
return True
81-
82-
# Comparison algorithm based on page content's stable lines subset
83-
elif conf.equalLines:
84-
counter = 0
85-
trueLines = 0
86-
pageLines = page.split("\n")
87-
88-
for commonLine in conf.equalLines:
89-
if counter >= len(pageLines):
90-
break
91-
92-
if commonLine in pageLines:
93-
trueLines += 1
94-
95-
counter += 1
77+
elif round(conf.seqMatcher.ratio(), 5) > 0.9:
78+
return True
9679

97-
# TODO: just debug prints
98-
#print "trueLines:", trueLines, "len(conf.equalLines):", len(conf.equalLines)
99-
#print "result:", ( trueLines * 100 ) / len(conf.equalLines)
100-
101-
if ( trueLines * 100 ) / len(conf.equalLines) >= 98:
102-
return True
103-
else:
104-
return False
80+
else:
81+
return False

lib/request/connect.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ def getPage(**kwargs):
227227

228228

229229
@staticmethod
230-
def queryPage(value=None, place=None, content=False):
230+
def queryPage(value=None, place=None, content=False, getSeqMatcher=False):
231231
"""
232232
This method calls a function to get the target url page content
233233
and returns its page MD5 hash or a boolean value in case of
@@ -270,7 +270,7 @@ def queryPage(value=None, place=None, content=False):
270270

271271
if content:
272272
return page, headers
273-
elif page and headers:
274-
return comparison(page, headers, content)
273+
elif page:
274+
return comparison(page, headers, getSeqMatcher)
275275
else:
276276
return False

lib/techniques/blind/inference.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,11 @@ def getChar(idx):
9797

9898
while (maxValue - minValue) != 1:
9999
queriesCount[0] += 1
100-
limit = ((maxValue + minValue) / 2)
101-
100+
limit = ((maxValue + minValue) / 2)
102101
forgedPayload = payload % (expressionUnescaped, idx, limit)
102+
result = Request.queryPage(forgedPayload)
103103

104-
result = Request.queryPage(forgedPayload)
105-
106-
if result == kb.defaultResult:
104+
if result == True:
107105
minValue = limit
108106
else:
109107
maxValue = limit

lib/utils/parenthesis.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def checkForParenthesis():
7676
payload = agent.payload(newValue=query)
7777
result = Request.queryPage(payload)
7878

79-
if result == kb.defaultResult:
79+
if result == True:
8080
count = parenthesis
8181

8282
logMsg = "the injectable parameter requires %d parenthesis" % count

0 commit comments

Comments
 (0)