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

Skip to content

Commit fd76f04

Browse files
committed
Added common pattern value support to bisection algorithm
1 parent 9bce226 commit fd76f04

2 files changed

Lines changed: 87 additions & 19 deletions

File tree

lib/core/common.py

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,22 +1252,36 @@ def goGoodSamaritan(part, prevValue, originalCharset):
12521252
predictionSet = set()
12531253
wildIndexes = []
12541254
singleValue = None
1255+
commonPatternValue = None
1256+
countSingleValues = 0
12551257

12561258
# If the header (e.g. Databases) we are looking for has common
12571259
# outputs defined
12581260
if part in kb.commonOutputs:
1261+
commonPartOutputs = kb.commonOutputs[part]
1262+
commonPatternValue = common_finder_only(prevValue, commonPartOutputs)
1263+
1264+
# If the longest common prefix is the same as previous value then
1265+
# do not consider it
1266+
if commonPatternValue and commonPatternValue == prevValue:
1267+
commonPatternValue = None
1268+
12591269
# For each common output
1260-
for item in kb.commonOutputs[part]:
1270+
for item in commonPartOutputs:
12611271
# Check if the common output (item) starts with prevValue
12621272
# where prevValue is the enumerated character(s) so far
12631273
if item.startswith(prevValue):
12641274
singleValue = item
1275+
countSingleValues += 1
12651276

12661277
if len(item) > len(prevValue):
12671278
char = item[len(prevValue)]
1279+
predictionSet.add(char)
12681280

1269-
if char not in predictionSet:
1270-
predictionSet.add(char)
1281+
# Reset single value if there is more than one possible common
1282+
# output
1283+
if countSingleValues > 1:
1284+
singleValue = None
12711285

12721286
commonCharset = []
12731287
otherCharset = []
@@ -1282,12 +1296,9 @@ def goGoodSamaritan(part, prevValue, originalCharset):
12821296

12831297
commonCharset.sort()
12841298

1285-
if len(commonCharset) > 1:
1286-
return None, commonCharset, otherCharset
1287-
else:
1288-
return singleValue, commonCharset, originalCharset
1299+
return singleValue, commonPatternValue, commonCharset, originalCharset
12891300
else:
1290-
return None, None, originalCharset
1301+
return None, None, None, originalCharset
12911302

12921303
def getCompiledRegex(regex, *args):
12931304
"""
@@ -1389,3 +1400,25 @@ def write(self, fp):
13891400
fp.write("%s = %s\n" % (key, getUnicode(value).replace('\n', '\n\t')))
13901401

13911402
fp.write("\n")
1403+
1404+
# http://boredzo.org/blog/archives/2007-01-06/longest-common-prefix-in-python-2
1405+
def longest_common_prefix(*sequences):
1406+
if len(sequences) == 1:
1407+
return sequences[0]
1408+
1409+
sequences = [pair[1] for pair in sorted((len(fi), fi) for fi in sequences)]
1410+
1411+
if not sequences:
1412+
return None
1413+
1414+
for i, comparison_ch in enumerate(sequences[0]):
1415+
for fi in sequences[1:]:
1416+
ch = fi[i]
1417+
1418+
if ch != comparison_ch:
1419+
return fi[:i]
1420+
1421+
return sequences[0]
1422+
1423+
def common_finder_only(initial, sequence):
1424+
return longest_common_prefix(*filter(lambda x: x.startswith(initial), sequence))

lib/techniques/blind/inference.py

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from lib.core.data import conf
3939
from lib.core.data import kb
4040
from lib.core.data import logger
41+
from lib.core.data import queries
4142
from lib.core.exception import sqlmapConnectionException
4243
from lib.core.exception import sqlmapValueException
4344
from lib.core.exception import sqlmapThreadException
@@ -144,7 +145,12 @@ def tryHint(idx):
144145

145146
return None
146147

147-
def getChar(idx, charTbl=asciiTbl, continuousOrder=True, expand=charsetType is None): # continuousOrder means that distance between each two neighbour's numerical values is exactly 1
148+
def getChar(idx, charTbl=asciiTbl, continuousOrder=True, expand=charsetType is None):
149+
"""
150+
continuousOrder means that distance between each two neighbour's
151+
numerical values is exactly 1
152+
"""
153+
148154
result = tryHint(idx)
149155

150156
if result:
@@ -153,7 +159,8 @@ def getChar(idx, charTbl=asciiTbl, continuousOrder=True, expand=charsetType is N
153159
if not continuousOrder:
154160
originalTbl = list(charTbl)
155161
else:
156-
shiftTable = [5, 4] # used for gradual expanding into unicode charspace
162+
# Used for gradual expanding into unicode charspace
163+
shiftTable = [5, 4]
157164

158165
if len(charTbl) == 1:
159166
forgedPayload = safeStringFormat(payload.replace('%3E', '%3D'), (expressionUnescaped, idx, charTbl[0]))
@@ -192,7 +199,8 @@ def getChar(idx, charTbl=asciiTbl, continuousOrder=True, expand=charsetType is N
192199

193200
if type(charTbl) != xrange:
194201
charTbl = charTbl[position:]
195-
else: # xrange - extended virtual charset used for memory/space optimization
202+
else:
203+
# xrange() - extended virtual charset used for memory/space optimization
196204
charTbl = xrange(charTbl[position], charTbl[-1] + 1)
197205
else:
198206
maxValue = posValue
@@ -206,9 +214,14 @@ def getChar(idx, charTbl=asciiTbl, continuousOrder=True, expand=charsetType is N
206214
if continuousOrder:
207215
if maxValue == 1:
208216
return None
209-
elif minValue == maxChar: # going beyond the original charset
210-
# if the original charTbl was [0,..,127] new one will be [128,..,128*16-1] or from 128 to 2047
211-
# and instead of making a HUGE list with all elements we use here xrange, which is a virtual list
217+
218+
# Going beyond the original charset
219+
elif minValue == maxChar:
220+
# If the original charTbl was [0,..,127] new one
221+
# will be [128,..,128*16-1] or from 128 to 2047
222+
# and instead of making a HUGE list with all
223+
# elements we use here xrange, which is a virtual
224+
# list
212225
if expand and shiftTable:
213226
charTbl = xrange(maxChar + 1, (maxChar + 1) << shiftTable.pop())
214227
maxChar = maxValue = charTbl[-1]
@@ -222,7 +235,10 @@ def getChar(idx, charTbl=asciiTbl, continuousOrder=True, expand=charsetType is N
222235
if minValue == maxChar or maxValue == minChar:
223236
return None
224237

225-
for retVal in (originalTbl[originalTbl.index(minValue)], originalTbl[originalTbl.index(minValue) + 1]): # if we are working with non-continuous set both minValue and character afterwards are possible candidates
238+
# If we are working with non-continuous elements, set
239+
# both minValue and character afterwards are possible
240+
# candidates
241+
for retVal in (originalTbl[originalTbl.index(minValue)], originalTbl[originalTbl.index(minValue) + 1]):
226242
forgedPayload = safeStringFormat(payload.replace('%3E', '%3D'), (expressionUnescaped, idx, retVal))
227243
queriesCount[0] += 1
228244
result = Request.queryPage(urlencode(forgedPayload))
@@ -244,6 +260,7 @@ def etaProgressUpdate(charTime, index):
244260
progress.update(index)
245261
progress.draw(eta)
246262

263+
# Go multi-threading (--threads > 1)
247264
if conf.threads > 1 and isinstance(length, int) and length > 1:
248265
value = [ None ] * length
249266
index = [ firstChar ] # As list for python nested function scoping
@@ -386,6 +403,8 @@ def downloadThread():
386403
dataToStdout(infoMsg)
387404

388405
conf.seqLock = None
406+
407+
# No multi-threading (--threads = 1)
389408
else:
390409
index = firstChar
391410

@@ -398,7 +417,7 @@ def downloadThread():
398417
# the moment
399418
if conf.useCommonPrediction and len(finalValue) > 0 and kb.partRun is not None:
400419
val = None
401-
singleValue, commonCharset, otherCharset = goGoodSamaritan(kb.partRun, finalValue, asciiTbl)
420+
singleValue, commonPatternValue, commonCharset, otherCharset = goGoodSamaritan(kb.partRun, finalValue, asciiTbl)
402421

403422
# If there is one single output in common-outputs, check
404423
# it via equal against the query output
@@ -422,10 +441,26 @@ def downloadThread():
422441

423442
break
424443

444+
# If there is a common pattern starting with finalValue,
445+
# check it via equal against the substring-query output
446+
if commonPatternValue is not None:
447+
# Substring-query containing equals commonPatternValue
448+
subquery = queries[kb.dbms].substring % (expressionUnescaped, 1, len(commonPatternValue))
449+
query = agent.prefixQuery(" %s" % safeStringFormat('AND (%s) = %s', (subquery, unescaper.unescape('\'%s\'' % commonPatternValue))))
450+
query = agent.postfixQuery(query)
451+
queriesCount[0] += 1
452+
result = Request.queryPage(urlencode(agent.payload(newValue=query)))
453+
454+
# Did we have luck?
455+
if result:
456+
val = commonPatternValue[index-1:]
457+
index += len(val)-1
458+
425459
# Otherwise if there is no singleValue (single match from
426-
# txt/common-outputs.txt) use the returned common
427-
# charset only to retrieve the query output
428-
if commonCharset:
460+
# txt/common-outputs.txt) and no commonPatternValue
461+
# (common pattern) use the returned common charset only
462+
# to retrieve the query output
463+
if not val and commonCharset:
429464
val = getChar(index, commonCharset, False)
430465

431466
# If we had no luck with singleValue and common charset,

0 commit comments

Comments
 (0)