diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 34eb41b5f..57dcdc09a 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) @@ -6,6 +6,7 @@ See the file 'LICENSE' for copying permission """ import copy +import httplib import logging import os import random @@ -21,7 +22,6 @@ from lib.core.agent import agent from lib.core.common import Backend from lib.core.common import extractRegexResult from lib.core.common import extractTextTagContent -from lib.core.common import filterNone from lib.core.common import findDynamicContent from lib.core.common import Format from lib.core.common import getFilteredPageContent @@ -48,7 +48,7 @@ from lib.core.common import unArrayizeValue from lib.core.common import urlencode from lib.core.common import wasLastResponseDBMSError from lib.core.common import wasLastResponseHTTPError -from lib.core.compat import xrange +from lib.core.convert import unicodeencode from lib.core.defaults import defaults from lib.core.data import conf from lib.core.data import kb @@ -106,8 +106,7 @@ from lib.request.inject import checkBooleanExpression from lib.request.templates import getPageTemplate from lib.techniques.union.test import unionTest from lib.techniques.union.use import configUnion -from thirdparty import six -from thirdparty.six.moves import http_client as _http_client +from lib.core.settings import INFERENCE_EQUALS_CHAR def checkSqlInjection(place, parameter, value): # Store here the details about boundaries and payload used to @@ -581,7 +580,7 @@ def checkSqlInjection(place, parameter, value): else: errorSet = set() - candidates = filterNone(_.strip() if _.strip() in trueRawResponse and _.strip() not in falseRawResponse else None for _ in (trueSet - falseSet - errorSet)) + candidates = filter(None, (_.strip() if _.strip() in trueRawResponse and _.strip() not in falseRawResponse else None for _ in (trueSet - falseSet - errorSet))) if candidates: candidates = sorted(candidates, key=lambda _: len(_)) @@ -595,7 +594,7 @@ def checkSqlInjection(place, parameter, value): logger.info(infoMsg) if not any((conf.string, conf.notString)): - candidates = filterNone(_.strip() if _.strip() in falseRawResponse and _.strip() not in trueRawResponse else None for _ in (falseSet - trueSet)) + candidates = filter(None, (_.strip() if _.strip() in falseRawResponse and _.strip() not in trueRawResponse else None for _ in (falseSet - trueSet))) if candidates: candidates = sorted(candidates, key=lambda _: len(_)) @@ -694,7 +693,7 @@ def checkSqlInjection(place, parameter, value): # Test for UNION query SQL injection reqPayload, vector = unionTest(comment, place, parameter, value, prefix, suffix) - if isinstance(reqPayload, six.string_types): + if isinstance(reqPayload, basestring): infoMsg = "%s parameter '%s' is '%s' injectable" % (paramType, parameter, title) logger.info(infoMsg) @@ -915,23 +914,23 @@ def checkFalsePositives(injection): if randInt3 > randInt2 > randInt1: break - if not checkBooleanExpression("%d=%d" % (randInt1, randInt1)): + if not checkBooleanExpression("%d%s%d" % (randInt1,INFERENCE_EQUALS_CHAR, randInt1)): retVal = False break # Just in case if DBMS hasn't properly recovered from previous delayed request if PAYLOAD.TECHNIQUE.BOOLEAN not in injection.data: - checkBooleanExpression("%d=%d" % (randInt1, randInt2)) + checkBooleanExpression("%d%s%d" % (randInt1,INFERENCE_EQUALS_CHAR, randInt2)) - if checkBooleanExpression("%d=%d" % (randInt1, randInt3)): # this must not be evaluated to True + if checkBooleanExpression("%d%s%d" % (randInt1,INFERENCE_EQUALS_CHAR, randInt3)): # this must not be evaluated to True retVal = False break - elif checkBooleanExpression("%d=%d" % (randInt3, randInt2)): # this must not be evaluated to True + elif checkBooleanExpression("%d%s%d" % (randInt3,INFERENCE_EQUALS_CHAR, randInt2)): # this must not be evaluated to True retVal = False break - elif not checkBooleanExpression("%d=%d" % (randInt2, randInt2)): # this must be evaluated to True + elif not checkBooleanExpression("%d%s%d" % (randInt2,INFERENCE_EQUALS_CHAR, randInt2)): # this must be evaluated to True retVal = False break @@ -1339,9 +1338,6 @@ def checkWaf(): if any((conf.string, conf.notString, conf.regexp, conf.dummy, conf.offline, conf.skipWaf)): return None - if kb.originalCode == _http_client.NOT_FOUND: - return None - _ = hashDBRetrieve(HASHDB_KEYS.CHECK_WAF_RESULT, True) if _ is not None: if _: @@ -1426,24 +1422,17 @@ def identifyWaf(): page, headers, code = None, None, None try: pushValue(kb.redirectChoice) - pushValue(kb.resendPostOnRedirect) - kb.redirectChoice = REDIRECTION.YES - kb.resendPostOnRedirect = True - if kwargs.get("get"): kwargs["get"] = urlencode(kwargs["get"]) kwargs["raise404"] = False kwargs["silent"] = True kwargs["finalCode"] = True - page, headers, code = Request.getPage(*args, **kwargs) except Exception: pass finally: - kb.resendPostOnRedirect = popValue() kb.redirectChoice = popValue() - return page or "", headers or {}, code retVal = [] @@ -1509,59 +1498,46 @@ def checkNullConnection(): if conf.data: return False - _ = hashDBRetrieve(HASHDB_KEYS.CHECK_NULL_CONNECTION_RESULT, True) - if _ is not None: - kb.nullConnection = _ + infoMsg = "testing NULL connection to the target URL" + logger.info(infoMsg) - if _: - dbgMsg = "resuming NULL connection method '%s'" % _ - logger.debug(dbgMsg) + pushValue(kb.pageCompress) + kb.pageCompress = False - else: - infoMsg = "testing NULL connection to the target URL" - logger.info(infoMsg) + try: + page, headers, _ = Request.getPage(method=HTTPMETHOD.HEAD, raise404=False) - pushValue(kb.pageCompress) - kb.pageCompress = False + if not page and HTTP_HEADER.CONTENT_LENGTH in (headers or {}): + kb.nullConnection = NULLCONNECTION.HEAD - try: - page, headers, _ = Request.getPage(method=HTTPMETHOD.HEAD, raise404=False) + infoMsg = "NULL connection is supported with HEAD method ('Content-Length')" + logger.info(infoMsg) + else: + page, headers, _ = Request.getPage(auxHeaders={HTTP_HEADER.RANGE: "bytes=-1"}) - if not page and HTTP_HEADER.CONTENT_LENGTH in (headers or {}): - kb.nullConnection = NULLCONNECTION.HEAD + if page and len(page) == 1 and HTTP_HEADER.CONTENT_RANGE in (headers or {}): + kb.nullConnection = NULLCONNECTION.RANGE - infoMsg = "NULL connection is supported with HEAD method ('Content-Length')" + infoMsg = "NULL connection is supported with GET method ('Range')" logger.info(infoMsg) else: - page, headers, _ = Request.getPage(auxHeaders={HTTP_HEADER.RANGE: "bytes=-1"}) + _, headers, _ = Request.getPage(skipRead=True) - if page and len(page) == 1 and HTTP_HEADER.CONTENT_RANGE in (headers or {}): - kb.nullConnection = NULLCONNECTION.RANGE + if HTTP_HEADER.CONTENT_LENGTH in (headers or {}): + kb.nullConnection = NULLCONNECTION.SKIP_READ - infoMsg = "NULL connection is supported with GET method ('Range')" + infoMsg = "NULL connection is supported with 'skip-read' method" logger.info(infoMsg) - else: - _, headers, _ = Request.getPage(skipRead=True) - if HTTP_HEADER.CONTENT_LENGTH in (headers or {}): - kb.nullConnection = NULLCONNECTION.SKIP_READ + except SqlmapConnectionException: + pass - infoMsg = "NULL connection is supported with 'skip-read' method" - logger.info(infoMsg) + finally: + kb.pageCompress = popValue() - except SqlmapConnectionException: - pass - - finally: - kb.pageCompress = popValue() - kb.nullConnection = False if kb.nullConnection is None else kb.nullConnection - hashDBWrite(HASHDB_KEYS.CHECK_NULL_CONNECTION_RESULT, kb.nullConnection, True) - - return kb.nullConnection in getPublicTypeMembers(NULLCONNECTION, True) + return kb.nullConnection is not None def checkConnection(suppressOutput=False): - threadData = getCurrentThreadData() - if not re.search(r"\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\Z", conf.hostname): if not any((conf.proxy, conf.tor, conf.dummy, conf.offline)): try: @@ -1586,7 +1562,8 @@ def checkConnection(suppressOutput=False): try: kb.originalPageTime = time.time() - Request.queryPage(content=True, noteResponseTime=False) + page, headers, _ = Request.queryPage(content=True, noteResponseTime=False) + kb.originalPage = kb.pageTemplate = page kb.errorIsNone = False @@ -1609,8 +1586,10 @@ def checkConnection(suppressOutput=False): else: kb.errorIsNone = True + threadData = getCurrentThreadData() + if kb.redirectChoice == REDIRECTION.YES and threadData.lastRedirectURL and threadData.lastRedirectURL[0] == threadData.lastRequestUID: - if (threadData.lastRedirectURL[1] or "").startswith("https://") and conf.hostname in getUnicode(threadData.lastRedirectURL[1]): + if (threadData.lastRedirectURL[1] or "").startswith("https://") and unicodeencode(conf.hostname) in threadData.lastRedirectURL[1]: conf.url = re.sub(r"https?://", "https://", conf.url) match = re.search(r":(\d+)", threadData.lastRedirectURL[1]) port = match.group(1) if match else 443 @@ -1625,7 +1604,7 @@ def checkConnection(suppressOutput=False): warnMsg += "any addressing issues" singleTimeWarnMessage(warnMsg) - if any(code in kb.httpErrorCodes for code in (_http_client.NOT_FOUND, )): + if any(code in kb.httpErrorCodes for code in (httplib.NOT_FOUND, )): errMsg = getSafeExString(ex) logger.critical(errMsg) @@ -1639,9 +1618,6 @@ def checkConnection(suppressOutput=False): kb.ignoreNotFound = True else: raise - finally: - kb.originalPage = kb.pageTemplate = threadData.lastPage - kb.originalCode = threadData.lastCode return True