This commit is contained in:
Miroslav Stampar 2015-07-18 17:01:34 +02:00
parent a7c4400cc9
commit 21e8182ac6
6 changed files with 138 additions and 130 deletions

View File

@ -1249,10 +1249,10 @@ def checkNullConnection():
infoMsg = "testing NULL connection to the target URL" infoMsg = "testing NULL connection to the target URL"
logger.info(infoMsg) logger.info(infoMsg)
pushValue(kb.pageCompress)
kb.pageCompress = False
try: try:
pushValue(kb.pageCompress)
kb.pageCompress = False
page, headers, _ = Request.getPage(method=HTTPMETHOD.HEAD) page, headers, _ = Request.getPage(method=HTTPMETHOD.HEAD)
if not page and HTTP_HEADER.CONTENT_LENGTH in (headers or {}): if not page and HTTP_HEADER.CONTENT_LENGTH in (headers or {}):
@ -1282,7 +1282,8 @@ def checkNullConnection():
errMsg = getUnicode(errMsg) errMsg = getUnicode(errMsg)
raise SqlmapConnectionException(errMsg) raise SqlmapConnectionException(errMsg)
kb.pageCompress = popValue() finally:
kb.pageCompress = popValue()
return kb.nullConnection is not None return kb.nullConnection is not None

View File

@ -501,47 +501,49 @@ def start():
kb.testedParams.add(paramKey) kb.testedParams.add(paramKey)
if testSqlInj: if testSqlInj:
if place == PLACE.COOKIE: try:
pushValue(kb.mergeCookies) if place == PLACE.COOKIE:
kb.mergeCookies = False pushValue(kb.mergeCookies)
kb.mergeCookies = False
check = heuristicCheckSqlInjection(place, parameter) check = heuristicCheckSqlInjection(place, parameter)
if check != HEURISTIC_TEST.POSITIVE: if check != HEURISTIC_TEST.POSITIVE:
if conf.smart or (kb.ignoreCasted and check == HEURISTIC_TEST.CASTED): if conf.smart or (kb.ignoreCasted and check == HEURISTIC_TEST.CASTED):
infoMsg = "skipping %s parameter '%s'" % (paramType, parameter) infoMsg = "skipping %s parameter '%s'" % (paramType, parameter)
logger.info(infoMsg) logger.info(infoMsg)
continue continue
infoMsg = "testing for SQL injection on %s " % paramType infoMsg = "testing for SQL injection on %s " % paramType
infoMsg += "parameter '%s'" % parameter infoMsg += "parameter '%s'" % parameter
logger.info(infoMsg) logger.info(infoMsg)
injection = checkSqlInjection(place, parameter, value) injection = checkSqlInjection(place, parameter, value)
proceed = not kb.endDetection proceed = not kb.endDetection
if injection is not None and injection.place is not None: if injection is not None and injection.place is not None:
kb.injections.append(injection) kb.injections.append(injection)
# In case when user wants to end detection phase (Ctrl+C) # In case when user wants to end detection phase (Ctrl+C)
if not proceed: if not proceed:
break break
msg = "%s parameter '%s' " % (injection.place, injection.parameter) msg = "%s parameter '%s' " % (injection.place, injection.parameter)
msg += "is vulnerable. Do you want to keep testing the others (if any)? [y/N] " msg += "is vulnerable. Do you want to keep testing the others (if any)? [y/N] "
test = readInput(msg, default="N") test = readInput(msg, default="N")
if test[0] not in ("y", "Y"): if test[0] not in ("y", "Y"):
proceed = False proceed = False
paramKey = (conf.hostname, conf.path, None, None) paramKey = (conf.hostname, conf.path, None, None)
kb.testedParams.add(paramKey) kb.testedParams.add(paramKey)
else: else:
warnMsg = "%s parameter '%s' is not " % (paramType, parameter) warnMsg = "%s parameter '%s' is not " % (paramType, parameter)
warnMsg += "injectable" warnMsg += "injectable"
logger.warn(warnMsg) logger.warn(warnMsg)
if place == PLACE.COOKIE: finally:
kb.mergeCookies = popValue() if place == PLACE.COOKIE:
kb.mergeCookies = popValue()
if len(kb.injections) == 0 or (len(kb.injections) == 1 and kb.injections[0].place is None): if len(kb.injections) == 0 or (len(kb.injections) == 1 and kb.injections[0].place is None):
if kb.vainRun and not conf.multipleTargets: if kb.vainRun and not conf.multipleTargets:

View File

@ -1030,23 +1030,24 @@ class Connect(object):
if kb.nullConnection and not content and not response and not timeBasedCompare: if kb.nullConnection and not content and not response and not timeBasedCompare:
noteResponseTime = False noteResponseTime = False
pushValue(kb.pageCompress) try:
kb.pageCompress = False pushValue(kb.pageCompress)
kb.pageCompress = False
if kb.nullConnection == NULLCONNECTION.HEAD: if kb.nullConnection == NULLCONNECTION.HEAD:
method = HTTPMETHOD.HEAD method = HTTPMETHOD.HEAD
elif kb.nullConnection == NULLCONNECTION.RANGE: elif kb.nullConnection == NULLCONNECTION.RANGE:
auxHeaders[HTTP_HEADER.RANGE] = "bytes=-1" auxHeaders[HTTP_HEADER.RANGE] = "bytes=-1"
_, headers, code = Connect.getPage(url=uri, get=get, post=post, method=method, cookie=cookie, ua=ua, referer=referer, host=host, silent=silent, auxHeaders=auxHeaders, raise404=raise404, skipRead=(kb.nullConnection == NULLCONNECTION.SKIP_READ)) _, headers, code = Connect.getPage(url=uri, get=get, post=post, method=method, cookie=cookie, ua=ua, referer=referer, host=host, silent=silent, auxHeaders=auxHeaders, raise404=raise404, skipRead=(kb.nullConnection == NULLCONNECTION.SKIP_READ))
if headers: if headers:
if kb.nullConnection in (NULLCONNECTION.HEAD, NULLCONNECTION.SKIP_READ) and HTTP_HEADER.CONTENT_LENGTH in headers: if kb.nullConnection in (NULLCONNECTION.HEAD, NULLCONNECTION.SKIP_READ) and HTTP_HEADER.CONTENT_LENGTH in headers:
pageLength = int(headers[HTTP_HEADER.CONTENT_LENGTH]) pageLength = int(headers[HTTP_HEADER.CONTENT_LENGTH])
elif kb.nullConnection == NULLCONNECTION.RANGE and HTTP_HEADER.CONTENT_RANGE in headers: elif kb.nullConnection == NULLCONNECTION.RANGE and HTTP_HEADER.CONTENT_RANGE in headers:
pageLength = int(headers[HTTP_HEADER.CONTENT_RANGE][headers[HTTP_HEADER.CONTENT_RANGE].find('/') + 1:]) pageLength = int(headers[HTTP_HEADER.CONTENT_RANGE][headers[HTTP_HEADER.CONTENT_RANGE].find('/') + 1:])
finally:
kb.pageCompress = popValue() kb.pageCompress = popValue()
if not pageLength: if not pageLength:
try: try:

View File

@ -391,11 +391,13 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser
warnMsg += ". Falling back to partial UNION technique" warnMsg += ". Falling back to partial UNION technique"
singleTimeWarnMessage(warnMsg) singleTimeWarnMessage(warnMsg)
pushValue(kb.forcePartialUnion) try:
kb.forcePartialUnion = True pushValue(kb.forcePartialUnion)
value = _goUnion(query, unpack, dump) kb.forcePartialUnion = True
found = (value is not None) or (value is None and expectingNone) value = _goUnion(query, unpack, dump)
kb.forcePartialUnion = popValue() found = (value is not None) or (value is None and expectingNone)
finally:
kb.forcePartialUnion = popValue()
else: else:
singleTimeWarnMessage(warnMsg) singleTimeWarnMessage(warnMsg)

View File

@ -81,73 +81,74 @@ def _findUnionCharCount(comment, place, parameter, value, prefix, suffix, where=
return found return found
pushValue(kb.errorIsNone) try:
items, ratios = [], [] pushValue(kb.errorIsNone)
kb.errorIsNone = False items, ratios = [], []
lowerCount, upperCount = conf.uColsStart, conf.uColsStop kb.errorIsNone = False
lowerCount, upperCount = conf.uColsStart, conf.uColsStop
if lowerCount == 1: if lowerCount == 1:
found = kb.orderByColumns or _orderByTechnique() found = kb.orderByColumns or _orderByTechnique()
if found: if found:
kb.orderByColumns = found kb.orderByColumns = found
infoMsg = "target URL appears to have %d column%s in query" % (found, 's' if found > 1 else "") infoMsg = "target URL appears to have %d column%s in query" % (found, 's' if found > 1 else "")
singleTimeLogMessage(infoMsg) singleTimeLogMessage(infoMsg)
return found return found
if abs(upperCount - lowerCount) < MIN_UNION_RESPONSES: if abs(upperCount - lowerCount) < MIN_UNION_RESPONSES:
upperCount = lowerCount + MIN_UNION_RESPONSES upperCount = lowerCount + MIN_UNION_RESPONSES
min_, max_ = MAX_RATIO, MIN_RATIO min_, max_ = MAX_RATIO, MIN_RATIO
pages = {} pages = {}
for count in xrange(lowerCount, upperCount + 1):
query = agent.forgeUnionQuery('', -1, count, comment, prefix, suffix, kb.uChar, where)
payload = agent.payload(place=place, parameter=parameter, newValue=query, where=where)
page, headers = Request.queryPage(payload, place=place, content=True, raise404=False)
if not isNullValue(kb.uChar):
pages[count] = page
ratio = comparison(page, headers, getRatioValue=True) or MIN_RATIO
ratios.append(ratio)
min_, max_ = min(min_, ratio), max(max_, ratio)
items.append((count, ratio))
for count in xrange(lowerCount, upperCount + 1):
query = agent.forgeUnionQuery('', -1, count, comment, prefix, suffix, kb.uChar, where)
payload = agent.payload(place=place, parameter=parameter, newValue=query, where=where)
page, headers = Request.queryPage(payload, place=place, content=True, raise404=False)
if not isNullValue(kb.uChar): if not isNullValue(kb.uChar):
pages[count] = page for regex in (kb.uChar, r'>\s*%s\s*<' % kb.uChar):
ratio = comparison(page, headers, getRatioValue=True) or MIN_RATIO contains = [(count, re.search(regex, page or "", re.IGNORECASE) is not None) for count, page in pages.items()]
ratios.append(ratio) if len(filter(lambda x: x[1], contains)) == 1:
min_, max_ = min(min_, ratio), max(max_, ratio) retVal = filter(lambda x: x[1], contains)[0][0]
items.append((count, ratio)) break
if not isNullValue(kb.uChar): if not retVal:
for regex in (kb.uChar, r'>\s*%s\s*<' % kb.uChar): ratios.pop(ratios.index(min_))
contains = [(count, re.search(regex, page or "", re.IGNORECASE) is not None) for count, page in pages.items()] ratios.pop(ratios.index(max_))
if len(filter(lambda x: x[1], contains)) == 1:
retVal = filter(lambda x: x[1], contains)[0][0]
break
if not retVal: minItem, maxItem = None, None
ratios.pop(ratios.index(min_))
ratios.pop(ratios.index(max_))
minItem, maxItem = None, None for item in items:
if item[1] == min_:
minItem = item
elif item[1] == max_:
maxItem = item
for item in items: if all(map(lambda x: x == min_ and x != max_, ratios)):
if item[1] == min_: retVal = maxItem[0]
minItem = item
elif item[1] == max_:
maxItem = item
if all(map(lambda x: x == min_ and x != max_, ratios)): elif all(map(lambda x: x != min_ and x == max_, ratios)):
retVal = maxItem[0] retVal = minItem[0]
elif all(map(lambda x: x != min_ and x == max_, ratios)): elif abs(max_ - min_) >= MIN_STATISTICAL_RANGE:
retVal = minItem[0] deviation = stdev(ratios)
lower, upper = average(ratios) - UNION_STDEV_COEFF * deviation, average(ratios) + UNION_STDEV_COEFF * deviation
elif abs(max_ - min_) >= MIN_STATISTICAL_RANGE: if min_ < lower:
deviation = stdev(ratios) retVal = minItem[0]
lower, upper = average(ratios) - UNION_STDEV_COEFF * deviation, average(ratios) + UNION_STDEV_COEFF * deviation
if min_ < lower: if max_ > upper:
retVal = minItem[0] if retVal is None or abs(max_ - upper) > abs(min_ - lower):
retVal = maxItem[0]
if max_ > upper: finally:
if retVal is None or abs(max_ - upper) > abs(min_ - lower): kb.errorIsNone = popValue()
retVal = maxItem[0]
kb.errorIsNone = popValue()
if retVal: if retVal:
infoMsg = "target URL appears to be UNION injectable with %d columns" % retVal infoMsg = "target URL appears to be UNION injectable with %d columns" % retVal

View File

@ -742,32 +742,33 @@ class Databases:
infoMsg = "enumerating database management system schema" infoMsg = "enumerating database management system schema"
logger.info(infoMsg) logger.info(infoMsg)
pushValue(conf.db) try:
pushValue(conf.tbl) pushValue(conf.db)
pushValue(conf.col) pushValue(conf.tbl)
pushValue(conf.col)
kb.data.cachedTables = {} kb.data.cachedTables = {}
kb.data.cachedColumns = {} kb.data.cachedColumns = {}
self.getTables() self.getTables()
infoMsg = "fetched tables: " infoMsg = "fetched tables: "
infoMsg += ", ".join(["%s" % ", ".join("%s%s%s" % (unsafeSQLIdentificatorNaming(db), ".." if \ infoMsg += ", ".join(["%s" % ", ".join("%s%s%s" % (unsafeSQLIdentificatorNaming(db), ".." if \
Backend.isDbms(DBMS.MSSQL) or Backend.isDbms(DBMS.SYBASE) \ Backend.isDbms(DBMS.MSSQL) or Backend.isDbms(DBMS.SYBASE) \
else ".", unsafeSQLIdentificatorNaming(t)) for t in tbl) for db, tbl in \ else ".", unsafeSQLIdentificatorNaming(t)) for t in tbl) for db, tbl in \
kb.data.cachedTables.items()]) kb.data.cachedTables.items()])
logger.info(infoMsg) logger.info(infoMsg)
for db, tables in kb.data.cachedTables.items(): for db, tables in kb.data.cachedTables.items():
for tbl in tables: for tbl in tables:
conf.db = db conf.db = db
conf.tbl = tbl conf.tbl = tbl
self.getColumns() self.getColumns()
finally:
conf.col = popValue() conf.col = popValue()
conf.tbl = popValue() conf.tbl = popValue()
conf.db = popValue() conf.db = popValue()
return kb.data.cachedColumns return kb.data.cachedColumns