Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sqlmapproject
GitHub Repository: sqlmapproject/sqlmap
Path: blob/master/lib/request/redirecthandler.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
import io
9
import re
10
import time
11
import types
12
13
from lib.core.common import getHostHeader
14
from lib.core.common import getSafeExString
15
from lib.core.common import logHTTPTraffic
16
from lib.core.common import readInput
17
from lib.core.convert import getBytes
18
from lib.core.convert import getUnicode
19
from lib.core.data import conf
20
from lib.core.data import kb
21
from lib.core.data import logger
22
from lib.core.enums import CUSTOM_LOGGING
23
from lib.core.enums import HTTP_HEADER
24
from lib.core.enums import HTTPMETHOD
25
from lib.core.enums import REDIRECTION
26
from lib.core.exception import SqlmapConnectionException
27
from lib.core.settings import DEFAULT_COOKIE_DELIMITER
28
from lib.core.settings import MAX_CONNECTION_READ_SIZE
29
from lib.core.settings import MAX_CONNECTION_TOTAL_SIZE
30
from lib.core.settings import MAX_SINGLE_URL_REDIRECTIONS
31
from lib.core.settings import MAX_TOTAL_REDIRECTIONS
32
from lib.core.threads import getCurrentThreadData
33
from lib.request.basic import decodePage
34
from lib.request.basic import parseResponse
35
from thirdparty import six
36
from thirdparty.six.moves import urllib as _urllib
37
38
class SmartRedirectHandler(_urllib.request.HTTPRedirectHandler):
39
def _get_header_redirect(self, headers):
40
retVal = None
41
42
if headers:
43
if HTTP_HEADER.LOCATION in headers:
44
retVal = headers[HTTP_HEADER.LOCATION]
45
elif HTTP_HEADER.URI in headers:
46
retVal = headers[HTTP_HEADER.URI]
47
48
return retVal
49
50
def _ask_redirect_choice(self, redcode, redurl, method):
51
with kb.locks.redirect:
52
if kb.choices.redirect is None:
53
msg = "got a %d redirect to " % redcode
54
msg += "'%s'. Do you want to follow? [Y/n] " % redurl
55
56
kb.choices.redirect = REDIRECTION.YES if readInput(msg, default='Y', boolean=True) else REDIRECTION.NO
57
58
if kb.choices.redirect == REDIRECTION.YES and method == HTTPMETHOD.POST and kb.resendPostOnRedirect is None:
59
msg = "redirect is a result of a "
60
msg += "POST request. Do you want to "
61
msg += "resend original POST data to a new "
62
msg += "location? [%s] " % ("Y/n" if not kb.originalPage else "y/N")
63
64
kb.resendPostOnRedirect = readInput(msg, default=('Y' if not kb.originalPage else 'N'), boolean=True)
65
66
if kb.resendPostOnRedirect:
67
self.redirect_request = self._redirect_request
68
69
def _redirect_request(self, req, fp, code, msg, headers, newurl):
70
return _urllib.request.Request(newurl.replace(' ', '%20'), data=req.data, headers=req.headers, origin_req_host=req.get_origin_req_host() if hasattr(req, "get_origin_req_host") else req.origin_req_host)
71
72
def http_error_302(self, req, fp, code, msg, headers):
73
start = time.time()
74
content = None
75
forceRedirect = False
76
redurl = self._get_header_redirect(headers) if not conf.ignoreRedirects else None
77
78
try:
79
content = fp.read(MAX_CONNECTION_TOTAL_SIZE)
80
except: # e.g. IncompleteRead
81
content = b""
82
finally:
83
if content:
84
try: # try to write it back to the read buffer so we could reuse it in further steps
85
fp.fp._rbuf.truncate(0)
86
fp.fp._rbuf.write(content)
87
except:
88
pass
89
90
content = decodePage(content, headers.get(HTTP_HEADER.CONTENT_ENCODING), headers.get(HTTP_HEADER.CONTENT_TYPE))
91
92
threadData = getCurrentThreadData()
93
threadData.lastRedirectMsg = (threadData.lastRequestUID, content)
94
95
redirectMsg = "HTTP redirect "
96
redirectMsg += "[#%d] (%d %s):\r\n" % (threadData.lastRequestUID, code, getUnicode(msg))
97
98
if headers:
99
logHeaders = "\r\n".join("%s: %s" % (getUnicode(key.capitalize() if hasattr(key, "capitalize") else key), getUnicode(value)) for (key, value) in headers.items())
100
else:
101
logHeaders = ""
102
103
redirectMsg += logHeaders
104
if content:
105
redirectMsg += "\r\n\r\n%s" % getUnicode(content[:MAX_CONNECTION_READ_SIZE])
106
107
logHTTPTraffic(threadData.lastRequestMsg, redirectMsg, start, time.time())
108
logger.log(CUSTOM_LOGGING.TRAFFIC_IN, redirectMsg)
109
110
if redurl:
111
try:
112
if not _urllib.parse.urlsplit(redurl).netloc:
113
redurl = _urllib.parse.urljoin(req.get_full_url(), redurl)
114
115
self._infinite_loop_check(req)
116
if conf.scope:
117
if not re.search(conf.scope, redurl, re.I):
118
redurl = None
119
else:
120
forceRedirect = True
121
else:
122
self._ask_redirect_choice(code, redurl, req.get_method())
123
except ValueError:
124
redurl = None
125
result = fp
126
127
if redurl and (kb.choices.redirect == REDIRECTION.YES or forceRedirect):
128
parseResponse(content, headers)
129
130
req.headers[HTTP_HEADER.HOST] = getHostHeader(redurl)
131
if headers and HTTP_HEADER.SET_COOKIE in headers:
132
cookies = dict()
133
delimiter = conf.cookieDel or DEFAULT_COOKIE_DELIMITER
134
last = None
135
136
for part in getUnicode(req.headers.get(HTTP_HEADER.COOKIE, "")).split(delimiter) + ([headers[HTTP_HEADER.SET_COOKIE]] if HTTP_HEADER.SET_COOKIE in headers else []):
137
if '=' in part:
138
part = part.strip()
139
key, value = part.split('=', 1)
140
cookies[key] = value
141
last = key
142
elif last:
143
cookies[last] += "%s%s" % (delimiter, part)
144
145
req.headers[HTTP_HEADER.COOKIE] = delimiter.join("%s=%s" % (key, cookies[key]) for key in cookies)
146
147
try:
148
result = _urllib.request.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
149
except _urllib.error.HTTPError as ex:
150
result = ex
151
152
# Dirty hack for https://github.com/sqlmapproject/sqlmap/issues/4046
153
try:
154
hasattr(result, "read")
155
except KeyError:
156
class _(object):
157
pass
158
result = _()
159
160
# Dirty hack for http://bugs.python.org/issue15701
161
try:
162
result.info()
163
except AttributeError:
164
def _(self):
165
return getattr(self, "hdrs", {})
166
167
result.info = types.MethodType(_, result)
168
169
if not hasattr(result, "read"):
170
def _(self, length=None):
171
try:
172
retVal = getSafeExString(ex) # Note: pyflakes mistakenly marks 'ex' as undefined (NOTE: tested in both Python2 and Python3)
173
except:
174
retVal = ""
175
return getBytes(retVal)
176
177
result.read = types.MethodType(_, result)
178
179
if not getattr(result, "url", None):
180
result.url = redurl
181
182
if not getattr(result, "code", None):
183
result.code = 999
184
except:
185
redurl = None
186
result = fp
187
fp.read = io.BytesIO(b"").read
188
else:
189
result = fp
190
191
threadData.lastRedirectURL = (threadData.lastRequestUID, redurl)
192
193
result.redcode = code
194
result.redurl = getUnicode(redurl) if six.PY3 else redurl
195
return result
196
197
http_error_301 = http_error_303 = http_error_307 = http_error_302
198
199
def _infinite_loop_check(self, req):
200
if hasattr(req, 'redirect_dict') and (req.redirect_dict.get(req.get_full_url(), 0) >= MAX_SINGLE_URL_REDIRECTIONS or len(req.redirect_dict) >= MAX_TOTAL_REDIRECTIONS):
201
errMsg = "infinite redirect loop detected (%s). " % ", ".join(item for item in req.redirect_dict.keys())
202
errMsg += "Please check all provided parameters and/or provide missing ones"
203
raise SqlmapConnectionException(errMsg)
204
205