Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sqlmapproject
GitHub Repository: sqlmapproject/sqlmap
Path: blob/master/lib/request/comparison.py
2989 views
1
#!/usr/bin/env python
2
3
"""
4
Copyright (c) 2006-2025 sqlmap developers (https://sqlmap.org)
5
See the file 'LICENSE' for copying permission
6
"""
7
8
from __future__ import division
9
10
import re
11
12
from lib.core.common import extractRegexResult
13
from lib.core.common import getFilteredPageContent
14
from lib.core.common import listToStrValue
15
from lib.core.common import removeDynamicContent
16
from lib.core.common import getLastRequestHTTPError
17
from lib.core.common import wasLastResponseDBMSError
18
from lib.core.common import wasLastResponseHTTPError
19
from lib.core.convert import getBytes
20
from lib.core.data import conf
21
from lib.core.data import kb
22
from lib.core.data import logger
23
from lib.core.exception import SqlmapNoneDataException
24
from lib.core.settings import DEFAULT_PAGE_ENCODING
25
from lib.core.settings import DIFF_TOLERANCE
26
from lib.core.settings import HTML_TITLE_REGEX
27
from lib.core.settings import LOWER_RATIO_BOUND
28
from lib.core.settings import MAX_DIFFLIB_SEQUENCE_LENGTH
29
from lib.core.settings import MAX_RATIO
30
from lib.core.settings import MIN_RATIO
31
from lib.core.settings import REFLECTED_VALUE_MARKER
32
from lib.core.settings import UPPER_RATIO_BOUND
33
from lib.core.settings import URI_HTTP_HEADER
34
from lib.core.threads import getCurrentThreadData
35
from thirdparty import six
36
37
def comparison(page, headers, code=None, getRatioValue=False, pageLength=None):
38
if not isinstance(page, (six.text_type, six.binary_type, type(None))):
39
logger.critical("got page of type %s; repr(page)[:200]=%s" % (type(page), repr(page)[:200]))
40
41
try:
42
page = b"".join(page)
43
except:
44
page = six.text_type(page)
45
46
_ = _adjust(_comparison(page, headers, code, getRatioValue, pageLength), getRatioValue)
47
return _
48
49
def _adjust(condition, getRatioValue):
50
if not any((conf.string, conf.notString, conf.regexp, conf.code)):
51
# Negative logic approach is used in raw page comparison scheme as that what is "different" than original
52
# PAYLOAD.WHERE.NEGATIVE response is considered as True; in switch based approach negative logic is not
53
# applied as that what is by user considered as True is that what is returned by the comparison mechanism
54
# itself
55
retVal = not condition if kb.negativeLogic and condition is not None and not getRatioValue else condition
56
else:
57
retVal = condition if not getRatioValue else (MAX_RATIO if condition else MIN_RATIO)
58
59
return retVal
60
61
def _comparison(page, headers, code, getRatioValue, pageLength):
62
threadData = getCurrentThreadData()
63
64
if kb.testMode:
65
threadData.lastComparisonHeaders = listToStrValue(_ for _ in headers.headers if not _.startswith("%s:" % URI_HTTP_HEADER)) if headers else ""
66
threadData.lastComparisonPage = page
67
threadData.lastComparisonCode = code
68
69
if page is None and pageLength is None:
70
return None
71
72
if any((conf.string, conf.notString, conf.regexp)):
73
rawResponse = "%s%s" % (listToStrValue(_ for _ in headers.headers if not _.startswith("%s:" % URI_HTTP_HEADER)) if headers else "", page)
74
75
# String to match in page when the query is True
76
if conf.string:
77
return conf.string in rawResponse
78
79
# String to match in page when the query is False
80
if conf.notString:
81
if conf.notString in rawResponse:
82
return False
83
else:
84
if kb.errorIsNone and (wasLastResponseDBMSError() or wasLastResponseHTTPError()):
85
return None
86
else:
87
return True
88
89
# Regular expression to match in page when the query is True and/or valid
90
if conf.regexp:
91
return re.search(conf.regexp, rawResponse, re.I | re.M) is not None
92
93
# HTTP code to match when the query is valid
94
if conf.code:
95
return conf.code == code
96
97
seqMatcher = threadData.seqMatcher
98
seqMatcher.set_seq1(kb.pageTemplate)
99
100
if page:
101
# In case of an DBMS error page return None
102
if kb.errorIsNone and (wasLastResponseDBMSError() or wasLastResponseHTTPError()) and not kb.negativeLogic:
103
if not (wasLastResponseHTTPError() and getLastRequestHTTPError() in (conf.ignoreCode or [])):
104
return None
105
106
# Dynamic content lines to be excluded before comparison
107
if not kb.nullConnection:
108
page = removeDynamicContent(page)
109
seqMatcher.set_seq1(removeDynamicContent(kb.pageTemplate))
110
111
if not pageLength:
112
pageLength = len(page)
113
114
if kb.nullConnection and pageLength:
115
if not seqMatcher.a:
116
errMsg = "problem occurred while retrieving original page content "
117
errMsg += "which prevents sqlmap from continuation. Please rerun, "
118
errMsg += "and if the problem persists turn off any optimization switches"
119
raise SqlmapNoneDataException(errMsg)
120
121
ratio = 1. * pageLength / len(seqMatcher.a)
122
123
if ratio > 1.:
124
ratio = 1. / ratio
125
else:
126
# Preventing "Unicode equal comparison failed to convert both arguments to Unicode"
127
# (e.g. if one page is PDF and the other is HTML)
128
if isinstance(seqMatcher.a, six.binary_type) and isinstance(page, six.text_type):
129
page = getBytes(page, kb.pageEncoding or DEFAULT_PAGE_ENCODING, "ignore")
130
elif isinstance(seqMatcher.a, six.text_type) and isinstance(page, six.binary_type):
131
seqMatcher.set_seq1(getBytes(seqMatcher.a, kb.pageEncoding or DEFAULT_PAGE_ENCODING, "ignore"))
132
133
if any(_ is None for _ in (page, seqMatcher.a)):
134
return None
135
elif seqMatcher.a and page and seqMatcher.a == page:
136
ratio = 1.
137
elif kb.skipSeqMatcher or seqMatcher.a and page and any(len(_) > MAX_DIFFLIB_SEQUENCE_LENGTH for _ in (seqMatcher.a, page)):
138
if not page or not seqMatcher.a:
139
return float(seqMatcher.a == page)
140
else:
141
ratio = 1. * len(seqMatcher.a) / len(page)
142
if ratio > 1:
143
ratio = 1. / ratio
144
else:
145
seq1, seq2 = None, None
146
147
if conf.titles:
148
seq1 = extractRegexResult(HTML_TITLE_REGEX, seqMatcher.a)
149
seq2 = extractRegexResult(HTML_TITLE_REGEX, page)
150
else:
151
seq1 = getFilteredPageContent(seqMatcher.a, True) if conf.textOnly else seqMatcher.a
152
seq2 = getFilteredPageContent(page, True) if conf.textOnly else page
153
154
if seq1 is None or seq2 is None:
155
return None
156
157
if isinstance(seq1, six.binary_type):
158
seq1 = seq1.replace(REFLECTED_VALUE_MARKER.encode(), b"")
159
elif isinstance(seq1, six.text_type):
160
seq1 = seq1.replace(REFLECTED_VALUE_MARKER, "")
161
162
if isinstance(seq2, six.binary_type):
163
seq2 = seq2.replace(REFLECTED_VALUE_MARKER.encode(), b"")
164
elif isinstance(seq2, six.text_type):
165
seq2 = seq2.replace(REFLECTED_VALUE_MARKER, "")
166
167
if kb.heavilyDynamic:
168
seq1 = seq1.split("\n" if isinstance(seq1, six.text_type) else b"\n")
169
seq2 = seq2.split("\n" if isinstance(seq2, six.text_type) else b"\n")
170
171
key = None
172
else:
173
key = (hash(seq1), hash(seq2))
174
175
seqMatcher.set_seq1(seq1)
176
seqMatcher.set_seq2(seq2)
177
178
if key in kb.cache.comparison:
179
ratio = kb.cache.comparison[key]
180
else:
181
ratio = round(seqMatcher.quick_ratio() if not kb.heavilyDynamic else seqMatcher.ratio(), 3)
182
183
if key:
184
kb.cache.comparison[key] = ratio
185
186
# If the url is stable and we did not set yet the match ratio and the
187
# current injected value changes the url page content
188
if kb.matchRatio is None:
189
if ratio >= LOWER_RATIO_BOUND and ratio <= UPPER_RATIO_BOUND:
190
kb.matchRatio = ratio
191
logger.debug("setting match ratio for current parameter to %.3f" % kb.matchRatio)
192
193
if kb.testMode:
194
threadData.lastComparisonRatio = ratio
195
196
# If it has been requested to return the ratio and not a comparison
197
# response
198
if getRatioValue:
199
return ratio
200
201
elif ratio > UPPER_RATIO_BOUND:
202
return True
203
204
elif ratio < LOWER_RATIO_BOUND:
205
return False
206
207
elif kb.matchRatio is None:
208
return None
209
210
else:
211
return (ratio - kb.matchRatio) > DIFF_TOLERANCE
212
213