Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sqlmapproject
GitHub Repository: sqlmapproject/sqlmap
Path: blob/master/lib/request/comparison.py
3554 views
1
#!/usr/bin/env python
2
3
"""
4
Copyright (c) 2006-2026 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
if threadData.lastPageTemplate != kb.pageTemplate:
110
threadData.lastPageTemplateCleaned = removeDynamicContent(kb.pageTemplate)
111
threadData.lastPageTemplate = kb.pageTemplate
112
113
seqMatcher.set_seq1(threadData.lastPageTemplateCleaned)
114
115
if not pageLength:
116
pageLength = len(page)
117
118
if kb.nullConnection and pageLength:
119
if not seqMatcher.a:
120
errMsg = "problem occurred while retrieving original page content "
121
errMsg += "which prevents sqlmap from continuation. Please rerun, "
122
errMsg += "and if the problem persists turn off any optimization switches"
123
raise SqlmapNoneDataException(errMsg)
124
125
ratio = 1. * pageLength / len(seqMatcher.a)
126
127
if ratio > 1.:
128
ratio = 1. / ratio
129
else:
130
# Preventing "Unicode equal comparison failed to convert both arguments to Unicode"
131
# (e.g. if one page is PDF and the other is HTML)
132
if isinstance(seqMatcher.a, six.binary_type) and isinstance(page, six.text_type):
133
page = getBytes(page, kb.pageEncoding or DEFAULT_PAGE_ENCODING, "ignore")
134
elif isinstance(seqMatcher.a, six.text_type) and isinstance(page, six.binary_type):
135
seqMatcher.set_seq1(getBytes(seqMatcher.a, kb.pageEncoding or DEFAULT_PAGE_ENCODING, "ignore"))
136
137
if any(_ is None for _ in (page, seqMatcher.a)):
138
return None
139
elif seqMatcher.a and page and seqMatcher.a == page:
140
ratio = 1.
141
elif kb.skipSeqMatcher or seqMatcher.a and page and any(len(_) > MAX_DIFFLIB_SEQUENCE_LENGTH for _ in (seqMatcher.a, page)):
142
if not page or not seqMatcher.a:
143
return float(seqMatcher.a == page)
144
else:
145
ratio = 1. * len(seqMatcher.a) / len(page)
146
if ratio > 1:
147
ratio = 1. / ratio
148
else:
149
seq1, seq2 = None, None
150
151
if conf.titles:
152
seq1 = extractRegexResult(HTML_TITLE_REGEX, seqMatcher.a)
153
seq2 = extractRegexResult(HTML_TITLE_REGEX, page)
154
else:
155
seq1 = getFilteredPageContent(seqMatcher.a, True) if conf.textOnly else seqMatcher.a
156
seq2 = getFilteredPageContent(page, True) if conf.textOnly else page
157
158
if seq1 is None or seq2 is None:
159
return None
160
161
if isinstance(seq1, six.binary_type):
162
seq1 = seq1.replace(REFLECTED_VALUE_MARKER.encode(), b"")
163
elif isinstance(seq1, six.text_type):
164
seq1 = seq1.replace(REFLECTED_VALUE_MARKER, "")
165
166
if isinstance(seq2, six.binary_type):
167
seq2 = seq2.replace(REFLECTED_VALUE_MARKER.encode(), b"")
168
elif isinstance(seq2, six.text_type):
169
seq2 = seq2.replace(REFLECTED_VALUE_MARKER, "")
170
171
if kb.heavilyDynamic:
172
seq1 = seq1.split("\n" if isinstance(seq1, six.text_type) else b"\n")
173
seq2 = seq2.split("\n" if isinstance(seq2, six.text_type) else b"\n")
174
175
key = None
176
else:
177
key = (hash(seq1), hash(seq2))
178
179
seqMatcher.set_seq1(seq1)
180
seqMatcher.set_seq2(seq2)
181
182
if key in kb.cache.comparison:
183
ratio = kb.cache.comparison[key]
184
else:
185
try:
186
ratio = seqMatcher.quick_ratio() if not kb.heavilyDynamic else seqMatcher.ratio()
187
except (TypeError, MemoryError):
188
ratio = seqMatcher.ratio()
189
190
ratio = round(ratio, 3)
191
192
if key:
193
kb.cache.comparison[key] = ratio
194
195
# If the url is stable and we did not set yet the match ratio and the
196
# current injected value changes the url page content
197
if kb.matchRatio is None:
198
if ratio >= LOWER_RATIO_BOUND and ratio <= UPPER_RATIO_BOUND:
199
kb.matchRatio = ratio
200
logger.debug("setting match ratio for current parameter to %.3f" % kb.matchRatio)
201
202
if kb.testMode:
203
threadData.lastComparisonRatio = ratio
204
205
# If it has been requested to return the ratio and not a comparison
206
# response
207
if getRatioValue:
208
return ratio
209
210
elif ratio > UPPER_RATIO_BOUND:
211
return True
212
213
elif ratio < LOWER_RATIO_BOUND:
214
return False
215
216
elif kb.matchRatio is None:
217
return None
218
219
else:
220
return (ratio - kb.matchRatio) > DIFF_TOLERANCE
221
222