2013-02-14 15:32:17 +04:00
|
|
|
#!/usr/bin/env python
|
2008-12-05 18:34:13 +03:00
|
|
|
|
|
|
|
"""
|
2015-01-06 17:02:16 +03:00
|
|
|
Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/)
|
2010-10-15 03:18:29 +04:00
|
|
|
See the file 'doc/COPYING' for copying permission
|
2008-12-05 18:34:13 +03:00
|
|
|
"""
|
|
|
|
|
|
|
|
import re
|
|
|
|
|
2011-06-11 03:18:43 +04:00
|
|
|
from lib.core.common import extractRegexResult
|
2011-01-14 17:37:03 +03:00
|
|
|
from lib.core.common import getFilteredPageContent
|
2011-08-12 19:33:37 +04:00
|
|
|
from lib.core.common import listToStrValue
|
2010-12-04 13:13:18 +03:00
|
|
|
from lib.core.common import removeDynamicContent
|
2013-01-29 23:53:11 +04:00
|
|
|
from lib.core.common import wasLastResponseDBMSError
|
|
|
|
from lib.core.common import wasLastResponseHTTPError
|
2008-12-05 18:34:13 +03:00
|
|
|
from lib.core.data import conf
|
2010-09-13 17:31:01 +04:00
|
|
|
from lib.core.data import kb
|
2009-02-09 13:28:03 +03:00
|
|
|
from lib.core.data import logger
|
2012-12-06 17:14:19 +04:00
|
|
|
from lib.core.exception import SqlmapNoneDataException
|
2011-05-26 00:54:30 +04:00
|
|
|
from lib.core.settings import DEFAULT_PAGE_ENCODING
|
2010-11-10 01:49:31 +03:00
|
|
|
from lib.core.settings import DIFF_TOLERANCE
|
2011-06-11 03:18:43 +04:00
|
|
|
from lib.core.settings import HTML_TITLE_REGEX
|
2011-02-04 02:25:56 +03:00
|
|
|
from lib.core.settings import MIN_RATIO
|
|
|
|
from lib.core.settings import MAX_RATIO
|
2012-11-13 18:22:59 +04:00
|
|
|
from lib.core.settings import REFLECTED_VALUE_MARKER
|
2010-12-24 14:06:57 +03:00
|
|
|
from lib.core.settings import LOWER_RATIO_BOUND
|
|
|
|
from lib.core.settings import UPPER_RATIO_BOUND
|
2011-01-16 13:52:42 +03:00
|
|
|
from lib.core.threads import getCurrentThreadData
|
2008-12-05 18:34:13 +03:00
|
|
|
|
2011-08-12 20:48:11 +04:00
|
|
|
def comparison(page, headers, code=None, getRatioValue=False, pageLength=None):
|
2012-03-30 13:42:58 +04:00
|
|
|
_ = _adjust(_comparison(page, headers, code, getRatioValue, pageLength), getRatioValue)
|
|
|
|
return _
|
2012-03-29 18:33:27 +04:00
|
|
|
|
|
|
|
def _adjust(condition, getRatioValue):
|
2012-08-22 11:58:39 +04:00
|
|
|
if not any((conf.string, conf.notString, conf.regexp, conf.code)):
|
2012-03-29 18:35:47 +04:00
|
|
|
# Negative logic approach is used in raw page comparison scheme as that what is "different" than original
|
|
|
|
# PAYLOAD.WHERE.NEGATIVE response is considered as True; in switch based approach negative logic is not
|
|
|
|
# applied as that what is by user considered as True is that what is returned by the comparison mechanism
|
|
|
|
# itself
|
2012-07-16 18:06:39 +04:00
|
|
|
retVal = not condition if kb.negativeLogic and condition is not None and not getRatioValue else condition
|
2012-03-29 18:33:27 +04:00
|
|
|
else:
|
|
|
|
retVal = condition if not getRatioValue else (MAX_RATIO if condition else MIN_RATIO)
|
|
|
|
|
|
|
|
return retVal
|
2012-03-29 17:39:12 +04:00
|
|
|
|
|
|
|
def _comparison(page, headers, code, getRatioValue, pageLength):
|
2012-04-11 01:48:34 +04:00
|
|
|
threadData = getCurrentThreadData()
|
|
|
|
|
2013-01-18 19:49:35 +04:00
|
|
|
if kb.testMode:
|
2013-01-18 20:00:11 +04:00
|
|
|
threadData.lastComparisonHeaders = listToStrValue(headers.headers) if headers else ""
|
2013-01-18 19:49:35 +04:00
|
|
|
threadData.lastComparisonPage = page
|
2012-04-11 01:48:34 +04:00
|
|
|
|
2010-11-04 00:51:36 +03:00
|
|
|
if page is None and pageLength is None:
|
|
|
|
return None
|
|
|
|
|
2012-04-11 01:48:34 +04:00
|
|
|
seqMatcher = threadData.seqMatcher
|
2011-01-16 13:52:42 +03:00
|
|
|
seqMatcher.set_seq1(kb.pageTemplate)
|
2008-12-05 18:34:13 +03:00
|
|
|
|
2013-01-18 19:49:35 +04:00
|
|
|
if any((conf.string, conf.notString, conf.regexp)):
|
2013-01-18 20:00:11 +04:00
|
|
|
rawResponse = "%s%s" % (listToStrValue(headers.headers) if headers else "", page)
|
2011-06-11 03:05:47 +04:00
|
|
|
|
2013-01-18 19:49:35 +04:00
|
|
|
# String to match in page when the query is True and/or valid
|
|
|
|
if conf.string:
|
|
|
|
return conf.string in rawResponse
|
2011-08-12 19:33:37 +04:00
|
|
|
|
2013-01-18 19:49:35 +04:00
|
|
|
# String to match in page when the query is False and/or invalid
|
|
|
|
if conf.notString:
|
|
|
|
return conf.notString not in rawResponse
|
|
|
|
|
|
|
|
# Regular expression to match in page when the query is True and/or valid
|
|
|
|
if conf.regexp:
|
|
|
|
return re.search(conf.regexp, rawResponse, re.I | re.M) is not None
|
2010-11-04 00:51:36 +03:00
|
|
|
|
2012-03-15 19:52:12 +04:00
|
|
|
# HTTP code to match when the query is valid
|
2012-12-18 12:36:26 +04:00
|
|
|
if conf.code:
|
2012-03-29 17:39:12 +04:00
|
|
|
return conf.code == code
|
2011-08-12 20:48:11 +04:00
|
|
|
|
2011-06-11 03:05:47 +04:00
|
|
|
if page:
|
2010-11-13 01:44:15 +03:00
|
|
|
# In case of an DBMS error page return None
|
2015-08-18 04:09:01 +03:00
|
|
|
if kb.errorIsNone and (wasLastResponseDBMSError() or wasLastResponseHTTPError()) and not kb.negativeLogic:
|
2010-11-13 01:44:15 +03:00
|
|
|
return None
|
|
|
|
|
2010-11-07 03:12:00 +03:00
|
|
|
# Dynamic content lines to be excluded before comparison
|
2011-01-14 17:37:03 +03:00
|
|
|
if not kb.nullConnection:
|
2010-12-04 13:13:18 +03:00
|
|
|
page = removeDynamicContent(page)
|
2011-01-16 13:52:42 +03:00
|
|
|
seqMatcher.set_seq1(removeDynamicContent(kb.pageTemplate))
|
2010-11-04 00:51:36 +03:00
|
|
|
|
|
|
|
if not pageLength:
|
|
|
|
pageLength = len(page)
|
2010-11-03 15:40:11 +03:00
|
|
|
|
2011-02-03 18:55:19 +03:00
|
|
|
if kb.nullConnection and pageLength:
|
2011-02-22 16:18:47 +03:00
|
|
|
if not seqMatcher.a:
|
2013-07-31 11:24:34 +04:00
|
|
|
errMsg = "problem occurred while retrieving original page content "
|
2011-04-22 02:31:02 +04:00
|
|
|
errMsg += "which prevents sqlmap from continuation. Please rerun, "
|
|
|
|
errMsg += "and if the problem persists turn off any optimization switches"
|
2013-01-04 02:20:55 +04:00
|
|
|
raise SqlmapNoneDataException(errMsg)
|
2011-02-22 16:18:47 +03:00
|
|
|
|
2011-01-16 13:52:42 +03:00
|
|
|
ratio = 1. * pageLength / len(seqMatcher.a)
|
2010-11-07 19:23:03 +03:00
|
|
|
|
2010-09-16 13:32:09 +04:00
|
|
|
if ratio > 1.:
|
|
|
|
ratio = 1. / ratio
|
|
|
|
else:
|
2011-05-26 00:54:30 +04:00
|
|
|
# Preventing "Unicode equal comparison failed to convert both arguments to Unicode"
|
|
|
|
# (e.g. if one page is PDF and the other is HTML)
|
|
|
|
if isinstance(seqMatcher.a, str) and isinstance(page, unicode):
|
2011-06-07 09:57:21 +04:00
|
|
|
page = page.encode(kb.pageEncoding or DEFAULT_PAGE_ENCODING, 'ignore')
|
2011-05-26 00:54:30 +04:00
|
|
|
elif isinstance(seqMatcher.a, unicode) and isinstance(page, str):
|
2011-06-07 09:57:21 +04:00
|
|
|
seqMatcher.a = seqMatcher.a.encode(kb.pageEncoding or DEFAULT_PAGE_ENCODING, 'ignore')
|
2011-05-26 00:54:30 +04:00
|
|
|
|
2011-06-17 20:58:50 +04:00
|
|
|
seq1, seq2 = None, None
|
|
|
|
|
2011-06-11 03:18:43 +04:00
|
|
|
if conf.titles:
|
2011-06-17 20:58:50 +04:00
|
|
|
seq1 = extractRegexResult(HTML_TITLE_REGEX, seqMatcher.a)
|
|
|
|
seq2 = extractRegexResult(HTML_TITLE_REGEX, page)
|
|
|
|
else:
|
|
|
|
seq1 = getFilteredPageContent(seqMatcher.a, True) if conf.textOnly else seqMatcher.a
|
|
|
|
seq2 = getFilteredPageContent(page, True) if conf.textOnly else page
|
|
|
|
|
2011-06-17 21:10:52 +04:00
|
|
|
if seq1 is None or seq2 is None:
|
2011-06-17 21:12:47 +04:00
|
|
|
return None
|
2012-11-13 18:22:59 +04:00
|
|
|
|
|
|
|
seq1 = seq1.replace(REFLECTED_VALUE_MARKER, "")
|
|
|
|
seq2 = seq2.replace(REFLECTED_VALUE_MARKER, "")
|
|
|
|
|
|
|
|
count = 0
|
|
|
|
while count < min(len(seq1), len(seq2)):
|
|
|
|
if seq1[count] == seq2[count]:
|
|
|
|
count += 1
|
|
|
|
else:
|
|
|
|
break
|
2015-09-30 11:13:19 +03:00
|
|
|
|
2012-11-13 18:22:59 +04:00
|
|
|
if count:
|
2015-09-30 11:13:19 +03:00
|
|
|
try:
|
|
|
|
_seq1 = seq1[count:]
|
|
|
|
_seq2 = seq2[count:]
|
|
|
|
except MemoryError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
seq1 = _seq1
|
|
|
|
seq2 = _seq2
|
2012-11-13 18:22:59 +04:00
|
|
|
|
2014-11-21 11:39:49 +03:00
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
seqMatcher.set_seq1(seq1)
|
2014-12-27 00:24:28 +03:00
|
|
|
except MemoryError:
|
|
|
|
seq1 = seq1[:len(seq1) / 1024]
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
|
|
|
|
while True:
|
|
|
|
try:
|
2014-11-21 11:39:49 +03:00
|
|
|
seqMatcher.set_seq2(seq2)
|
|
|
|
except MemoryError:
|
2014-12-27 00:24:28 +03:00
|
|
|
seq2 = seq2[:len(seq2) / 1024]
|
2014-11-21 11:39:49 +03:00
|
|
|
else:
|
|
|
|
break
|
2012-11-13 18:22:59 +04:00
|
|
|
|
|
|
|
ratio = round(seqMatcher.quick_ratio(), 3)
|
2010-03-10 17:14:27 +03:00
|
|
|
|
2009-02-09 13:28:03 +03:00
|
|
|
# If the url is stable and we did not set yet the match ratio and the
|
|
|
|
# current injected value changes the url page content
|
2010-12-18 12:51:34 +03:00
|
|
|
if kb.matchRatio is None:
|
2015-10-22 15:58:06 +03:00
|
|
|
if (count or ratio >= LOWER_RATIO_BOUND) and ratio <= UPPER_RATIO_BOUND:
|
2010-12-18 12:51:34 +03:00
|
|
|
kb.matchRatio = ratio
|
|
|
|
logger.debug("setting match ratio for current parameter to %.3f" % kb.matchRatio)
|
2010-01-02 05:02:12 +03:00
|
|
|
|
2009-02-09 13:28:03 +03:00
|
|
|
# If it has been requested to return the ratio and not a comparison
|
|
|
|
# response
|
2011-02-04 02:25:56 +03:00
|
|
|
if getRatioValue:
|
2009-02-09 13:28:03 +03:00
|
|
|
return ratio
|
|
|
|
|
2010-12-27 21:27:42 +03:00
|
|
|
elif ratio > UPPER_RATIO_BOUND:
|
2010-11-05 16:14:12 +03:00
|
|
|
return True
|
|
|
|
|
2015-08-18 04:09:01 +03:00
|
|
|
elif ratio < LOWER_RATIO_BOUND:
|
|
|
|
return False
|
|
|
|
|
2010-12-18 12:51:34 +03:00
|
|
|
elif kb.matchRatio is None:
|
2010-11-10 02:38:29 +03:00
|
|
|
return None
|
|
|
|
|
2008-12-20 04:54:08 +03:00
|
|
|
else:
|
2011-04-18 01:58:34 +04:00
|
|
|
return (ratio - kb.matchRatio) > DIFF_TOLERANCE
|