redirect logic rewritten from scratch

This commit is contained in:
Miroslav Stampar 2012-03-15 11:10:58 +00:00
parent 84479eebe9
commit a8c9a47092
3 changed files with 71 additions and 68 deletions

View File

@ -463,3 +463,9 @@ HASH_RECOGNITION_QUIT_THRESHOLD = 10000
# Size of a buffer used for log file output # Size of a buffer used for log file output
BUFFERED_LOG_SIZE = 10000 BUFFERED_LOG_SIZE = 10000
# Maximum number of redirections to any single URL - this is needed because of the state that cookies introduce
MAX_SINGLE_URL_REDIRECTIONS = 4
# Maximum total number of redirections (regardless of URL) - before assuming we're in a loop
MAX_TOTAL_REDIRECTIONS = 10

View File

@ -149,7 +149,6 @@ class Connect:
ignoreTimeout = kwargs.get('ignoreTimeout', kb.ignoreTimeout) ignoreTimeout = kwargs.get('ignoreTimeout', kb.ignoreTimeout)
refreshing = kwargs.get('refreshing', False) refreshing = kwargs.get('refreshing', False)
retrying = kwargs.get('retrying', False) retrying = kwargs.get('retrying', False)
redirecting = kwargs.get('redirecting', None)
crawling = kwargs.get('crawling', False) crawling = kwargs.get('crawling', False)
if not urlparse.urlsplit(url).netloc: if not urlparse.urlsplit(url).netloc:
@ -299,41 +298,20 @@ class Connect:
if not kb.proxyAuthHeader and req.has_header(HTTPHEADER.PROXY_AUTHORIZATION): if not kb.proxyAuthHeader and req.has_header(HTTPHEADER.PROXY_AUTHORIZATION):
kb.proxyAuthHeader = req.get_header(HTTPHEADER.PROXY_AUTHORIZATION) kb.proxyAuthHeader = req.get_header(HTTPHEADER.PROXY_AUTHORIZATION)
if hasattr(conn, "setcookie"):
kb.redirectSetCookie = conn.setcookie
if hasattr(conn, "redurl") and hasattr(conn, "redcode") and target\
and not redirecting and not conf.realTest:
if kb.redirectChoice is None:
msg = "sqlmap got a %d redirect to " % conn.redcode
msg += "'%s'. What do you want to do? " % conn.redurl
msg += "\n[1] Follow the redirection (default)"
msg += "\n[2] Stay on the original page"
msg += "\n[3] Ignore"
choice = readInput(msg, default="1")
kb.redirectChoice = choice
if kb.redirectChoice == REDIRECTION.IGNORE:
redirecting = conn.redcode
page = threadData.lastRedirectMsg[1]
skipLogTraffic = True
else:
kb.queryCounter += 1
kwargs['url'] = conf.url if kb.redirectChoice == REDIRECTION.ORIGINAL else conn.redurl
kwargs['redirecting'] = conn.redcode
return Connect.__getPageProxy(**kwargs)
# Return response object # Return response object
if response: if response:
return conn, None, None return conn, None, None
# Get HTTP response # Get HTTP response
if page is None: if hasattr(conn, 'redurl'):
page = threadData.lastRedirectMsg[1] if kb.redirectChoice == REDIRECTION.IGNORE\
else kb.originalPage if kb.redirectChoice == REDIRECTION.ORIGINAL\
else conn.read()
skipLogTraffic = True
else:
page = conn.read() page = conn.read()
code = redirecting or conn.code code = conn.code
responseHeaders = conn.info() responseHeaders = conn.info()
responseHeaders[URI_HTTP_HEADER] = conn.geturl() responseHeaders[URI_HTTP_HEADER] = conn.geturl()
page = decodePage(page, responseHeaders.get(HTTPHEADER.CONTENT_ENCODING), responseHeaders.get(HTTPHEADER.CONTENT_TYPE)) page = decodePage(page, responseHeaders.get(HTTPHEADER.CONTENT_ENCODING), responseHeaders.get(HTTPHEADER.CONTENT_TYPE))
@ -404,9 +382,7 @@ class Connect:
code = e.code code = e.code
threadData.lastHTTPError = (threadData.lastRequestUID, code) threadData.lastHTTPError = (threadData.lastRequestUID, code)
if code not in kb.httpErrorCodes: kb.httpErrorCodes[code] = kb.httpErrorCodes.get(code, 0) + 1
kb.httpErrorCodes[code] = 0
kb.httpErrorCodes[code] += 1
status = getUnicode(e.msg) status = getUnicode(e.msg)
responseMsg += "[#%d] (%d %s):\n" % (threadData.lastRequestUID, code, status) responseMsg += "[#%d] (%d %s):\n" % (threadData.lastRequestUID, code, status)

View File

@ -11,35 +11,44 @@ import urllib2
import urlparse import urlparse
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.common import getHostHeader from lib.core.common import getHostHeader
from lib.core.common import getUnicode from lib.core.common import getUnicode
from lib.core.common import logHTTPTraffic from lib.core.common import logHTTPTraffic
from lib.core.common import readInput
from lib.core.enums import HTTPHEADER from lib.core.enums import HTTPHEADER
from lib.core.enums import REDIRECTION
from lib.core.exception import sqlmapConnectionException from lib.core.exception import sqlmapConnectionException
from lib.core.settings import MAX_SINGLE_URL_REDIRECTIONS
from lib.core.settings import MAX_TOTAL_REDIRECTIONS
from lib.core.threads import getCurrentThreadData from lib.core.threads import getCurrentThreadData
from lib.request.basic import decodePage from lib.request.basic import decodePage
class SmartRedirectHandler(urllib2.HTTPRedirectHandler): class SmartRedirectHandler(urllib2.HTTPRedirectHandler):
# maximum number of redirections to any single URL
# this is needed because of the state that cookies introduce
max_repeats = 4
# maximum total number of redirections (regardless of URL) before
# assuming we're in a loop
max_redirections = 10
def _get_header_redirect(self, headers): def _get_header_redirect(self, headers):
retVal = None retVal = None
if "location" in headers: if headers:
retVal = headers.getheaders("location")[0].split("?")[0] if "location" in headers:
elif "uri" in headers: retVal = headers.getheaders("location")[0].split("?")[0]
retVal = headers.getheaders("uri")[0].split("?")[0] elif "uri" in headers:
retVal = headers.getheaders("uri")[0].split("?")[0]
return retVal return retVal
def common_http_redirect(self, result, headers, code, content, msg): def _ask_redirect_choice(self, redcode, redurl):
if kb.redirectChoice is None:
msg = "sqlmap got a %d redirect to " % redcode
msg += "'%s'. What do you want to do? " % redurl
msg += "\n[1] Follow the redirection (default)"
msg += "\n[2] Stay on the original page"
msg += "\n[3] Ignore"
choice = readInput(msg, default="1")
kb.redirectChoice = choice
def _process_http_redirect(self, result, headers, code, content, msg):
content = decodePage(content, headers.get(HTTPHEADER.CONTENT_ENCODING), headers.get(HTTPHEADER.CONTENT_TYPE)) content = decodePage(content, headers.get(HTTPHEADER.CONTENT_ENCODING), headers.get(HTTPHEADER.CONTENT_TYPE))
threadData = getCurrentThreadData() threadData = getCurrentThreadData()
@ -59,25 +68,26 @@ class SmartRedirectHandler(urllib2.HTTPRedirectHandler):
logger.log(7, responseMsg) logger.log(7, responseMsg)
if result: if self._get_header_redirect(headers):
if self._get_header_redirect(headers): result.redurl = self._get_header_redirect(headers)
result.redurl = self._get_header_redirect(headers)
if hasattr(result, 'redurl'): if not urlparse.urlsplit(result.redurl).netloc:
if not urlparse.urlsplit(result.redurl).netloc: result.redurl = urlparse.urljoin(conf.url, result.redurl)
result.redurl = urlparse.urljoin(conf.url, result.redurl)
if "set-cookie" in headers: if "set-cookie" in headers:
result.setcookie = headers["set-cookie"].split("; path")[0] kb.redirectSetCookie = headers["set-cookie"].split("; path")[0]
result.redcode = code result.redcode = code
return result return result
def http_error_301(self, req, fp, code, msg, headers): def http_error_301(self, req, fp, code, msg, headers):
self.infinite_loop_check(req) content = None, None
redurl = self._get_header_redirect(headers)
self._infinite_loop_check(req)
self._ask_redirect_choice(code, redurl)
content = None
try: try:
content = fp.read() content = fp.read()
except Exception, msg: except Exception, msg:
@ -85,16 +95,23 @@ class SmartRedirectHandler(urllib2.HTTPRedirectHandler):
dbgMsg += "redirect response content (%s)" % msg dbgMsg += "redirect response content (%s)" % msg
logger.debug(dbgMsg) logger.debug(dbgMsg)
if self._get_header_redirect(headers): if redurl:
req.headers[HTTPHEADER.HOST] = getHostHeader(self._get_header_redirect(headers)) req.headers[HTTPHEADER.HOST] = getHostHeader(redurl)
result = urllib2.HTTPRedirectHandler.http_error_301(self, req, fp, code, msg, headers) if kb.redirectChoice == REDIRECTION.FOLLOW:
return self.common_http_redirect(result, headers, code, content, msg) result = urllib2.HTTPRedirectHandler.http_error_301(self, req, fp, code, msg, headers)
else:
result = fp
return self._process_http_redirect(result, headers, code, content, msg)
def http_error_302(self, req, fp, code, msg, headers): def http_error_302(self, req, fp, code, msg, headers):
self.infinite_loop_check(req) content = None, None
redurl = self._get_header_redirect(headers)
self._infinite_loop_check(req)
self._ask_redirect_choice(code, redurl)
content = None
try: try:
content = fp.read() content = fp.read()
except Exception, msg: except Exception, msg:
@ -102,14 +119,18 @@ class SmartRedirectHandler(urllib2.HTTPRedirectHandler):
dbgMsg += "redirect response content (%s)" % msg dbgMsg += "redirect response content (%s)" % msg
logger.debug(dbgMsg) logger.debug(dbgMsg)
if self._get_header_redirect(headers): if redurl:
req.headers[HTTPHEADER.HOST] = getHostHeader(self._get_header_redirect(headers)) req.headers[HTTPHEADER.HOST] = getHostHeader(redurl)
result = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers) if kb.redirectChoice == REDIRECTION.FOLLOW:
return self.common_http_redirect(result, headers, code, content, msg) result = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
else:
result = fp
def infinite_loop_check(self, req): return self._process_http_redirect(result, headers, code, content, msg)
if hasattr(req, 'redirect_dict') and (req.redirect_dict.get(req.get_full_url(), 0) >= self.max_repeats or len(req.redirect_dict) >= self.max_redirections):
def _infinite_loop_check(self, req):
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):
errMsg = "infinite redirect loop detected (%s). " % ", ".join(item for item in req.redirect_dict.keys()) errMsg = "infinite redirect loop detected (%s). " % ", ".join(item for item in req.redirect_dict.keys())
errMsg += "please check all provided parameters and/or provide missing ones." errMsg += "please check all provided parameters and/or provide missing ones."
raise sqlmapConnectionException, errMsg raise sqlmapConnectionException, errMsg