sqlmap/lib/controller/checks.py

1621 lines
75 KiB
Python
Raw Normal View History

2019-05-08 13:47:52 +03:00
#!/usr/bin/env python
2008-10-15 19:38:22 +04:00
"""
2020-12-31 13:46:27 +03:00
Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/)
2017-10-11 15:50:46 +03:00
See the file 'LICENSE' for copying permission
2008-10-15 19:38:22 +04:00
"""
import copy
2017-11-24 14:20:57 +03:00
import logging
import random
import re
2010-05-21 17:36:49 +04:00
import socket
2016-12-20 01:47:39 +03:00
import subprocess
2018-08-10 15:01:55 +03:00
import sys
2008-10-15 19:38:22 +04:00
import time
2012-12-11 15:02:06 +04:00
from extra.beep.beep import beep
2008-10-15 19:38:22 +04:00
from lib.core.agent import agent
from lib.core.common import Backend
2010-12-06 18:50:19 +03:00
from lib.core.common import extractRegexResult
from lib.core.common import extractTextTagContent
2019-03-29 04:28:16 +03:00
from lib.core.common import filterNone
2010-12-29 22:39:32 +03:00
from lib.core.common import findDynamicContent
from lib.core.common import Format
2017-02-06 15:28:33 +03:00
from lib.core.common import getFilteredPageContent
2012-01-14 00:56:06 +04:00
from lib.core.common import getLastRequestHTTPError
2013-01-25 15:34:57 +04:00
from lib.core.common import getPublicTypeMembers
from lib.core.common import getSafeExString
from lib.core.common import getSortedInjectionTests
from lib.core.common import hashDBRetrieve
from lib.core.common import hashDBWrite
from lib.core.common import intersect
2020-11-27 01:41:35 +03:00
from lib.core.common import isDigit
2019-11-13 01:32:09 +03:00
from lib.core.common import joinValue
from lib.core.common import listToStrValue
from lib.core.common import parseFilePaths
2010-12-04 18:47:02 +03:00
from lib.core.common import popValue
from lib.core.common import pushValue
2008-10-15 19:38:22 +04:00
from lib.core.common import randomInt
from lib.core.common import randomStr
2010-10-11 15:47:07 +04:00
from lib.core.common import readInput
from lib.core.common import showStaticWords
from lib.core.common import singleTimeLogMessage
2011-06-08 18:35:23 +04:00
from lib.core.common import singleTimeWarnMessage
2017-11-29 17:59:00 +03:00
from lib.core.common import unArrayizeValue
2013-01-29 23:53:11 +04:00
from lib.core.common import wasLastResponseDBMSError
from lib.core.common import wasLastResponseHTTPError
2019-03-28 18:04:38 +03:00
from lib.core.compat import xrange
2019-12-01 01:10:20 +03:00
from lib.core.convert import getBytes
2019-05-06 01:54:21 +03:00
from lib.core.convert import getUnicode
2008-10-15 19:38:22 +04:00
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
2011-07-08 10:02:31 +04:00
from lib.core.datatype import AttribDict
from lib.core.datatype import InjectionDict
from lib.core.decorators import stackedmethod
2013-01-25 15:34:57 +04:00
from lib.core.dicts import FROM_DUMMY_TABLE
2020-01-27 19:32:31 +03:00
from lib.core.dicts import HEURISTIC_NULL_EVAL
2013-01-25 15:34:57 +04:00
from lib.core.enums import DBMS
from lib.core.enums import HASHDB_KEYS
from lib.core.enums import HEURISTIC_TEST
from lib.core.enums import HTTP_HEADER
2010-11-08 12:49:57 +03:00
from lib.core.enums import HTTPMETHOD
2016-05-06 14:06:59 +03:00
from lib.core.enums import NOTE
2010-11-08 12:49:57 +03:00
from lib.core.enums import NULLCONNECTION
from lib.core.enums import PAYLOAD
from lib.core.enums import PLACE
2014-12-04 12:06:15 +03:00
from lib.core.enums import REDIRECTION
2018-12-21 13:29:57 +03:00
from lib.core.enums import WEB_PLATFORM
from lib.core.exception import SqlmapConnectionException
2018-12-04 01:18:52 +03:00
from lib.core.exception import SqlmapDataException
from lib.core.exception import SqlmapNoneDataException
from lib.core.exception import SqlmapSilentQuitException
2017-07-26 00:32:30 +03:00
from lib.core.exception import SqlmapSkipTargetException
from lib.core.exception import SqlmapUserQuitException
from lib.core.settings import BOUNDED_INJECTION_MARKER
2017-02-06 15:28:33 +03:00
from lib.core.settings import CANDIDATE_SENTENCE_MIN_LENGTH
2017-05-08 00:12:42 +03:00
from lib.core.settings import CHECK_INTERNET_ADDRESS
from lib.core.settings import CHECK_INTERNET_VALUE
2019-10-14 11:42:10 +03:00
from lib.core.settings import DEFAULT_COOKIE_DELIMITER
from lib.core.settings import DEFAULT_GET_POST_DELIMITER
from lib.core.settings import DUMMY_NON_SQLI_CHECK_APPENDIX
2016-05-30 17:06:39 +03:00
from lib.core.settings import FI_ERROR_REGEX
from lib.core.settings import FORMAT_EXCEPTION_STRINGS
2012-10-28 03:42:08 +04:00
from lib.core.settings import HEURISTIC_CHECK_ALPHABET
2019-06-04 15:44:06 +03:00
from lib.core.settings import INFERENCE_EQUALS_CHAR
2019-01-26 14:36:03 +03:00
from lib.core.settings import IPS_WAF_CHECK_PAYLOAD
2019-06-04 15:44:06 +03:00
from lib.core.settings import IPS_WAF_CHECK_RATIO
from lib.core.settings import IPS_WAF_CHECK_TIMEOUT
from lib.core.settings import MAX_DIFFLIB_SEQUENCE_LENGTH
2019-06-04 16:46:56 +03:00
from lib.core.settings import MAX_STABILITY_DELAY
2016-01-15 00:21:47 +03:00
from lib.core.settings import NON_SQLI_CHECK_PREFIX_SUFFIX_LENGTH
from lib.core.settings import PRECONNECT_INCOMPATIBLE_SERVERS
2018-10-16 13:23:07 +03:00
from lib.core.settings import SINGLE_QUOTE_MARKER
from lib.core.settings import SLEEP_TIME_MARKER
2013-01-16 05:31:03 +04:00
from lib.core.settings import SUHOSIN_MAX_VALUE_LENGTH
from lib.core.settings import SUPPORTED_DBMS
2018-08-10 15:01:55 +03:00
from lib.core.settings import UNICODE_ENCODING
2010-12-29 22:01:29 +03:00
from lib.core.settings import UPPER_RATIO_BOUND
2019-06-04 15:44:06 +03:00
from lib.core.settings import URI_HTTP_HEADER
from lib.core.threads import getCurrentThreadData
2020-01-23 12:53:06 +03:00
from lib.core.unescaper import unescaper
2008-10-15 19:38:22 +04:00
from lib.request.connect import Connect as Request
from lib.request.comparison import comparison
from lib.request.inject import checkBooleanExpression
from lib.request.templates import getPageTemplate
2011-06-18 16:34:41 +04:00
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
def checkSqlInjection(place, parameter, value):
# Store here the details about boundaries and payload used to
# successfully inject
2011-07-08 10:02:31 +04:00
injection = InjectionDict()
2011-03-17 12:23:46 +03:00
# Localized thread data needed for some methods
threadData = getCurrentThreadData()
# Favoring non-string specific boundaries in case of digit-like parameter values
2020-11-27 01:41:35 +03:00
if isDigit(value):
2016-09-20 11:03:00 +03:00
kb.cache.intBoundaries = kb.cache.intBoundaries or sorted(copy.deepcopy(conf.boundaries), key=lambda boundary: any(_ in (boundary.prefix or "") or _ in (boundary.suffix or "") for _ in ('"', '\'')))
boundaries = kb.cache.intBoundaries
2017-12-29 15:04:52 +03:00
elif value.isalpha():
kb.cache.alphaBoundaries = kb.cache.alphaBoundaries or sorted(copy.deepcopy(conf.boundaries), key=lambda boundary: not any(_ in (boundary.prefix or "") or _ in (boundary.suffix or "") for _ in ('"', '\'')))
boundaries = kb.cache.alphaBoundaries
else:
boundaries = conf.boundaries
2012-06-19 12:33:51 +04:00
# Set the flag for SQL injection test mode
2010-12-07 16:34:06 +03:00
kb.testMode = True
2010-12-04 18:47:02 +03:00
2014-11-21 13:20:54 +03:00
paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place
2014-10-07 14:00:11 +04:00
tests = getSortedInjectionTests()
seenPayload = set()
2014-10-07 14:00:11 +04:00
2016-05-19 20:46:12 +03:00
kb.data.setdefault("randomInt", str(randomInt(10)))
kb.data.setdefault("randomStr", str(randomStr(10)))
2014-10-07 14:00:11 +04:00
while tests:
test = tests.pop(0)
try:
2011-01-01 22:22:44 +03:00
if kb.endDetection:
break
2013-02-01 20:24:04 +04:00
if conf.dbms is None:
# If the DBMS has not yet been fingerprinted (via simple heuristic check
# or via DBMS-specific payload) and boolean-based blind has been identified
# then attempt to identify with a simple DBMS specific boolean-based
# test what the DBMS may be
2013-02-01 20:24:04 +04:00
if not injection.dbms and PAYLOAD.TECHNIQUE.BOOLEAN in injection.data:
2017-05-26 15:14:35 +03:00
if not Backend.getIdentifiedDbms() and kb.heuristicDbms is None and not kb.droppingRequests:
kb.heuristicDbms = heuristicCheckDbms(injection)
# If the DBMS has already been fingerprinted (via DBMS-specific
# error message, simple heuristic check or via DBMS-specific
# payload), ask the user to limit the tests to the fingerprinted
# DBMS
if kb.reduceTests is None and not conf.testFilter and (intersect(Backend.getErrorParsedDBMSes(), SUPPORTED_DBMS, True) or kb.heuristicDbms or injection.dbms):
2019-11-13 01:32:09 +03:00
msg = "it looks like the back-end DBMS is '%s'. " % (Format.getErrorParsedDBMSes() or kb.heuristicDbms or joinValue(injection.dbms, '/'))
msg += "Do you want to skip test payloads specific for other DBMSes? [Y/n]"
2017-04-18 16:48:05 +03:00
kb.reduceTests = (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) if readInput(msg, default='Y', boolean=True) else []
# If the DBMS has been fingerprinted (via DBMS-specific error
# message, via simple heuristic check or via DBMS-specific
# payload), ask the user to extend the tests to all DBMS-specific,
# regardless of --level and --risk values provided
if kb.extendTests is None and not conf.testFilter and (conf.level < 5 or conf.risk < 3) and (intersect(Backend.getErrorParsedDBMSes(), SUPPORTED_DBMS, True) or kb.heuristicDbms or injection.dbms):
2015-02-21 15:59:44 +03:00
msg = "for the remaining tests, do you want to include all tests "
2019-11-13 01:32:09 +03:00
msg += "for '%s' extending provided " % (Format.getErrorParsedDBMSes() or kb.heuristicDbms or joinValue(injection.dbms, '/'))
msg += "level (%d)" % conf.level if conf.level < 5 else ""
msg += " and " if conf.level < 5 and conf.risk < 3 else ""
msg += "risk (%d)" % conf.risk if conf.risk < 3 else ""
msg += " values? [Y/n]" if conf.level < 5 and conf.risk < 3 else " value? [Y/n]"
2017-04-18 16:48:05 +03:00
kb.extendTests = (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) if readInput(msg, default='Y', boolean=True) else []
2013-02-01 20:24:04 +04:00
2010-12-18 13:42:09 +03:00
title = test.title
kb.testType = stype = test.stype
2010-12-18 13:42:09 +03:00
clause = test.clause
2012-05-07 17:51:31 +04:00
unionExtended = False
trueCode, falseCode = None, None
2010-12-18 13:42:09 +03:00
if conf.httpCollector is not None:
conf.httpCollector.setExtendedArguments({
"_title": title,
"_place": place,
"_parameter": parameter,
})
if stype == PAYLOAD.TECHNIQUE.UNION:
configUnion(test.request.char)
if "[CHAR]" in title:
if conf.uChar is None:
continue
else:
title = title.replace("[CHAR]", conf.uChar)
elif "[RANDNUM]" in title or "(NULL)" in title:
title = title.replace("[RANDNUM]", "random number")
if test.request.columns == "[COLSTART]-[COLSTOP]":
if conf.uCols is None:
continue
else:
title = title.replace("[COLSTART]", str(conf.uColsStart))
title = title.replace("[COLSTOP]", str(conf.uColsStop))
2011-05-11 01:33:06 +04:00
elif conf.uCols is not None:
debugMsg = "skipping test '%s' because the user " % title
debugMsg += "provided custom column range %s" % conf.uCols
logger.debug(debugMsg)
continue
match = re.search(r"(\d+)-(\d+)", test.request.columns)
2018-03-16 17:11:04 +03:00
if match and injection.data:
lower, upper = int(match.group(1)), int(match.group(2))
for _ in (lower, upper):
if _ > 1:
2016-05-22 15:22:31 +03:00
__ = 2 * (_ - 1) + 1 if _ == lower else 2 * _
2012-05-07 17:51:31 +04:00
unionExtended = True
2016-05-22 15:22:31 +03:00
test.request.columns = re.sub(r"\b%d\b" % _, str(__), test.request.columns)
title = re.sub(r"\b%d\b" % _, str(__), title)
test.title = re.sub(r"\b%d\b" % _, str(__), test.title)
2010-12-18 13:42:09 +03:00
# Skip test if the user's wants to test only for a specific
# technique
2019-06-07 01:21:43 +03:00
if conf.technique and isinstance(conf.technique, list) and stype not in conf.technique:
2020-11-27 01:41:35 +03:00
debugMsg = "skipping test '%s' because user " % title
debugMsg += "specified testing of only "
2019-06-07 01:21:43 +03:00
debugMsg += "%s techniques" % " & ".join(PAYLOAD.SQLINJECTION[_] for _ in conf.technique)
logger.debug(debugMsg)
continue
2011-02-02 01:04:48 +03:00
# Skip test if it is the same SQL injection type already
# identified by another test
if injection.data and stype in injection.data:
debugMsg = "skipping test '%s' because " % title
debugMsg += "the payload for %s has " % PAYLOAD.SQLINJECTION[stype]
debugMsg += "already been identified"
logger.debug(debugMsg)
continue
# Parse DBMS-specific payloads' details
2013-02-01 20:24:04 +04:00
if "details" in test and "dbms" in test.details:
payloadDbms = test.details.dbms
2013-02-01 20:24:04 +04:00
else:
payloadDbms = None
2013-02-01 20:24:04 +04:00
# Skip tests if title, vector or DBMS is not included by the
# given test filter
if conf.testFilter and not any(conf.testFilter in str(item) or re.search(conf.testFilter, str(item), re.I) for item in (test.title, test.vector, payloadDbms)):
2019-05-30 22:25:31 +03:00
debugMsg = "skipping test '%s' because its " % title
debugMsg += "name/vector/DBMS is not included by the given filter"
logger.debug(debugMsg)
continue
2015-10-01 12:57:33 +03:00
# Skip tests if title, vector or DBMS is included by the
# given skip filter
if conf.testSkip and any(conf.testSkip in str(item) or re.search(conf.testSkip, str(item), re.I) for item in (test.title, test.vector, payloadDbms)):
2019-05-30 22:25:31 +03:00
debugMsg = "skipping test '%s' because its " % title
debugMsg += "name/vector/DBMS is included by the given skip filter"
logger.debug(debugMsg)
continue
2015-10-01 12:57:33 +03:00
if payloadDbms is not None:
# Skip DBMS-specific test if it does not match the user's
# provided DBMS
2017-09-11 11:38:19 +03:00
if conf.dbms and not intersect(payloadDbms, conf.dbms, True):
2010-12-18 13:42:09 +03:00
debugMsg = "skipping test '%s' because " % title
2017-09-11 11:41:50 +03:00
debugMsg += "its declared DBMS is different than provided"
2017-08-28 14:02:08 +03:00
logger.debug(debugMsg)
continue
2017-09-11 11:38:19 +03:00
if kb.dbmsFilter and not intersect(payloadDbms, kb.dbmsFilter, True):
2017-08-28 14:02:08 +03:00
debugMsg = "skipping test '%s' because " % title
2017-09-11 11:41:50 +03:00
debugMsg += "its declared DBMS is different than provided"
2010-12-18 13:42:09 +03:00
logger.debug(debugMsg)
continue
# Skip DBMS-specific test if it does not match the
# previously identified DBMS (via DBMS-specific payload)
2017-09-11 11:38:19 +03:00
if injection.dbms and not intersect(payloadDbms, injection.dbms, True):
2017-09-11 11:41:50 +03:00
debugMsg = "skipping test '%s' because " % title
debugMsg += "its declared DBMS is different than identified"
logger.debug(debugMsg)
continue
# Skip DBMS-specific test if it does not match the
# previously identified DBMS (via DBMS-specific error message)
if kb.reduceTests and not intersect(payloadDbms, kb.reduceTests, True):
2017-11-29 17:59:00 +03:00
debugMsg = "skipping test '%s' because the heuristic " % title
debugMsg += "tests showed that the back-end DBMS "
debugMsg += "could be '%s'" % unArrayizeValue(kb.reduceTests)
logger.debug(debugMsg)
continue
# If the user did not decide to extend the tests to all
# DBMS-specific or the test payloads is not specific to the
# identified DBMS, then only test for it if both level and risk
# are below the corrisponding configuration's level and risk
# values
if not conf.testFilter and not (kb.extendTests and intersect(payloadDbms, kb.extendTests, True)):
2015-02-13 12:59:03 +03:00
# Skip test if the risk is higher than the provided (or default)
# value
if test.risk > conf.risk:
2015-02-13 12:59:03 +03:00
debugMsg = "skipping test '%s' because the risk (%d) " % (title, test.risk)
debugMsg += "is higher than the provided (%d)" % conf.risk
logger.debug(debugMsg)
continue
# Skip test if the level is higher than the provided (or default)
# value
if test.level > conf.level:
2015-02-13 12:59:03 +03:00
debugMsg = "skipping test '%s' because the level (%d) " % (title, test.level)
debugMsg += "is higher than the provided (%d)" % conf.level
logger.debug(debugMsg)
continue
2010-12-18 13:42:09 +03:00
# Skip test if it does not match the same SQL injection clause
# already identified by another test
clauseMatch = False
2010-12-18 13:42:09 +03:00
for clauseTest in clause:
if injection.clause is not None and clauseTest in injection.clause:
clauseMatch = True
break
2012-02-22 19:53:36 +04:00
if clause != [0] and injection.clause and injection.clause != [0] and not clauseMatch:
2010-12-18 13:42:09 +03:00
debugMsg = "skipping test '%s' because the clauses " % title
debugMsg += "differ from the clause already identified"
2010-12-18 13:42:09 +03:00
logger.debug(debugMsg)
continue
# Skip test if the user provided custom character (for UNION-based payloads)
if conf.uChar is not None and ("random number" in title or "(NULL)" in title):
debugMsg = "skipping test '%s' because the user " % title
debugMsg += "provided a specific character, %s" % conf.uChar
2011-01-30 19:23:19 +03:00
logger.debug(debugMsg)
continue
2018-03-16 17:21:19 +03:00
if stype == PAYLOAD.TECHNIQUE.UNION:
match = re.search(r"(\d+)-(\d+)", test.request.columns)
if match and not injection.data:
_ = test.request.columns.split('-')[-1]
if conf.uCols is None and _.isdigit():
2018-03-16 17:21:19 +03:00
if kb.futileUnion is None:
msg = "it is recommended to perform "
msg += "only basic UNION tests if there is not "
2018-03-16 17:21:19 +03:00
msg += "at least one other (potential) "
msg += "technique found. Do you want to reduce "
2019-06-03 11:41:51 +03:00
msg += "the number of requests? [Y/n] "
kb.futileUnion = readInput(msg, default='Y', boolean=True)
2018-03-16 17:21:19 +03:00
if kb.futileUnion and int(_) > 10:
2018-03-16 17:21:19 +03:00
debugMsg = "skipping test '%s'" % title
logger.debug(debugMsg)
continue
2018-03-16 17:11:04 +03:00
2010-12-18 13:42:09 +03:00
infoMsg = "testing '%s'" % title
logger.info(infoMsg)
2015-02-21 16:12:30 +03:00
# Force back-end DBMS according to the current test DBMS value
# for proper payload unescaping
Backend.forceDbms(payloadDbms[0] if isinstance(payloadDbms, list) else payloadDbms)
2010-12-18 13:42:09 +03:00
# Parse test's <request>
comment = agent.getComment(test.request) if len(conf.boundaries) > 1 else None
fstPayload = agent.cleanupPayload(test.request.payload, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) and BOUNDED_INJECTION_MARKER not in (value or "") else None)
for boundary in boundaries:
2010-12-18 13:42:09 +03:00
injectable = False
# Skip boundary if the level is higher than the provided (or
# default) value
# Parse boundary's <level>
if boundary.level > conf.level and not (kb.extendTests and intersect(payloadDbms, kb.extendTests, True)):
2010-12-18 13:42:09 +03:00
continue
# Skip boundary if it does not match against test's <clause>
# Parse test's <clause> and boundary's <clause>
clauseMatch = False
for clauseTest in test.clause:
if clauseTest in boundary.clause:
clauseMatch = True
break
2012-02-22 19:53:36 +04:00
if test.clause != [0] and boundary.clause != [0] and not clauseMatch:
2010-12-18 13:42:09 +03:00
continue
# Skip boundary if it does not match against test's <where>
# Parse test's <where> and boundary's <where>
whereMatch = False
for where in test.where:
if where in boundary.where:
whereMatch = True
break
if not whereMatch:
continue
# Parse boundary's <prefix>, <suffix> and <ptype>
prefix = boundary.prefix if boundary.prefix else ""
suffix = boundary.suffix if boundary.suffix else ""
ptype = boundary.ptype
2012-09-25 11:25:35 +04:00
# Options --prefix/--suffix have a higher priority (if set by user)
prefix = conf.prefix if conf.prefix is not None else prefix
suffix = conf.suffix if conf.suffix is not None else suffix
comment = None if conf.suffix is not None else comment
2010-12-18 13:42:09 +03:00
# If the previous injections succeeded, we know which prefix,
# suffix and parameter type to use for further tests, no
# need to cycle through the boundaries for the following tests
condBound = (injection.prefix is not None and injection.suffix is not None)
condBound &= (injection.prefix != prefix or injection.suffix != suffix)
condType = injection.ptype is not None and injection.ptype != ptype
# If the payload is an inline query test for it regardless
# of previously identified injection types
if stype != PAYLOAD.TECHNIQUE.QUERY and (condBound or condType):
2010-12-18 13:42:09 +03:00
continue
# For each test's <where>
for where in test.where:
templatePayload = None
vector = None
2010-12-18 13:42:09 +03:00
origValue = value
if kb.customInjectionMark in origValue:
origValue = origValue.split(kb.customInjectionMark)[0]
origValue = re.search(r"(\w*)\Z", origValue).group(1)
2010-12-18 13:42:09 +03:00
# Threat the parameter original value according to the
# test's <where> tag
2013-12-27 14:02:59 +04:00
if where == PAYLOAD.WHERE.ORIGINAL or conf.prefix:
if kb.tamperFunctions:
templatePayload = agent.payload(place, parameter, value="", newValue=origValue, where=where)
2011-02-02 16:34:09 +03:00
elif where == PAYLOAD.WHERE.NEGATIVE:
# Use different page template than the original
# one as we are changing parameters value, which
# will likely result in a different content
2015-02-20 18:42:28 +03:00
if conf.invalidLogical:
_ = int(kb.data.randomInt[:2])
origValue = "%s AND %s LIKE %s" % (origValue, _, _ + 1)
elif conf.invalidBignum:
origValue = kb.data.randomInt[:6]
2014-01-24 00:56:06 +04:00
elif conf.invalidString:
origValue = kb.data.randomStr[:6]
else:
origValue = "-%s" % kb.data.randomInt[:4]
2015-02-20 18:42:28 +03:00
templatePayload = agent.payload(place, parameter, value="", newValue=origValue, where=where)
2011-02-02 16:34:09 +03:00
elif where == PAYLOAD.WHERE.REPLACE:
2010-12-18 13:42:09 +03:00
origValue = ""
kb.pageTemplate, kb.errorIsNone = getPageTemplate(templatePayload, place)
2010-12-18 13:42:09 +03:00
# Forge request payload by prepending with boundary's
# prefix and appending the boundary's suffix to the
# test's ' <payload><comment> ' string
2015-07-05 02:47:01 +03:00
if fstPayload:
boundPayload = agent.prefixQuery(fstPayload, prefix, where, clause)
boundPayload = agent.suffixQuery(boundPayload, comment, suffix, where)
reqPayload = agent.payload(place, parameter, newValue=boundPayload, where=where)
if reqPayload:
stripPayload = re.sub(r"(\A|\b|_)([A-Za-z]{4}((?<!LIKE))|\d+)(_|\b|\Z)", r"\g<1>.\g<4>", reqPayload)
if stripPayload in seenPayload:
continue
else:
seenPayload.add(stripPayload)
2015-07-05 02:47:01 +03:00
else:
reqPayload = None
2010-12-18 13:42:09 +03:00
# Perform the test's request and check whether or not the
# payload was successful
# Parse test's <response>
for method, check in test.response.items():
check = agent.cleanupPayload(check, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) and BOUNDED_INJECTION_MARKER not in (value or "") else None)
2010-12-18 13:42:09 +03:00
# In case of boolean-based blind SQL injection
if method == PAYLOAD.METHOD.COMPARISON:
# Generate payload used for comparison
def genCmpPayload():
sndPayload = agent.cleanupPayload(test.response.comparison, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) and BOUNDED_INJECTION_MARKER not in (value or "") else None)
# Forge response payload by prepending with
# boundary's prefix and appending the boundary's
# suffix to the test's ' <payload><comment> '
# string
boundPayload = agent.prefixQuery(sndPayload, prefix, where, clause)
boundPayload = agent.suffixQuery(boundPayload, comment, suffix, where)
cmpPayload = agent.payload(place, parameter, newValue=boundPayload, where=where)
2011-02-08 03:02:54 +03:00
return cmpPayload
2010-12-18 13:42:09 +03:00
# Useful to set kb.matchRatio at first based on False response content
2010-12-18 13:42:09 +03:00
kb.matchRatio = None
kb.negativeLogic = (where == PAYLOAD.WHERE.NEGATIVE)
2020-07-20 13:43:17 +03:00
suggestion = None
2012-02-22 19:53:36 +04:00
Request.queryPage(genCmpPayload(), place, raise404=False)
2016-06-03 16:51:52 +03:00
falsePage, falseHeaders, falseCode = threadData.lastComparisonPage or "", threadData.lastComparisonHeaders, threadData.lastComparisonCode
falseRawResponse = "%s%s" % (falseHeaders, falsePage)
2010-12-18 13:42:09 +03:00
# Checking if there is difference between current FALSE, original and heuristics page (i.e. not used parameter)
2020-05-04 01:06:03 +03:00
if not any((kb.negativeLogic, conf.string, conf.notString)):
try:
ratio = 1.0
seqMatcher = getCurrentThreadData().seqMatcher
for current in (kb.originalPage, kb.heuristicPage):
2019-05-20 20:41:12 +03:00
seqMatcher.set_seq1(current or "")
seqMatcher.set_seq2(falsePage or "")
ratio *= seqMatcher.quick_ratio()
if ratio == 1.0:
continue
2019-05-27 14:03:25 +03:00
except (MemoryError, OverflowError):
pass
2010-12-18 13:42:09 +03:00
# Perform the test's True request
trueResult = Request.queryPage(reqPayload, place, raise404=False)
2016-06-03 16:51:52 +03:00
truePage, trueHeaders, trueCode = threadData.lastComparisonPage or "", threadData.lastComparisonHeaders, threadData.lastComparisonCode
trueRawResponse = "%s%s" % (trueHeaders, truePage)
2021-02-03 13:52:50 +03:00
if trueResult and not(truePage == falsePage and not any((kb.nullConnection, conf.code))):
# Perform the test's False request
falseResult = Request.queryPage(genCmpPayload(), place, raise404=False)
2010-12-18 13:42:09 +03:00
if not falseResult:
if kb.negativeLogic:
boundPayload = agent.prefixQuery(kb.data.randomStr, prefix, where, clause)
boundPayload = agent.suffixQuery(boundPayload, comment, suffix, where)
errorPayload = agent.payload(place, parameter, newValue=boundPayload, where=where)
errorResult = Request.queryPage(errorPayload, place, raise404=False)
if errorResult:
continue
elif kb.heuristicPage and not any((conf.string, conf.notString, conf.regexp, conf.code, kb.nullConnection)):
_ = comparison(kb.heuristicPage, None, getRatioValue=True)
2019-05-09 11:16:10 +03:00
if (_ or 0) > (kb.matchRatio or 0):
kb.matchRatio = _
logger.debug("adjusting match ratio for current parameter to %.3f" % kb.matchRatio)
# Reducing false-positive "appears" messages in heavily dynamic environment
if kb.heavilyDynamic and not Request.queryPage(reqPayload, place, raise404=False):
continue
2016-06-03 16:51:52 +03:00
injectable = True
2019-05-10 10:30:21 +03:00
elif (threadData.lastComparisonRatio or 0) > UPPER_RATIO_BOUND and not any((conf.string, conf.notString, conf.regexp, conf.code, kb.nullConnection)):
2017-02-06 15:28:33 +03:00
originalSet = set(getFilteredPageContent(kb.pageTemplate, True, "\n").split("\n"))
trueSet = set(getFilteredPageContent(truePage, True, "\n").split("\n"))
falseSet = set(getFilteredPageContent(falsePage, True, "\n").split("\n"))
if threadData.lastErrorPage and threadData.lastErrorPage[1]:
errorSet = set(getFilteredPageContent(threadData.lastErrorPage[1], True, "\n").split("\n"))
else:
errorSet = set()
2017-02-06 15:28:33 +03:00
if originalSet == trueSet != falseSet:
candidates = trueSet - falseSet - errorSet
2017-02-06 15:28:33 +03:00
if candidates:
2019-05-30 23:40:51 +03:00
candidates = sorted(candidates, key=len)
2017-02-06 15:28:33 +03:00
for candidate in candidates:
2017-05-26 15:08:08 +03:00
if re.match(r"\A[\w.,! ]+\Z", candidate) and ' ' in candidate and candidate.strip() and len(candidate) > CANDIDATE_SENTENCE_MIN_LENGTH:
2020-07-20 13:43:17 +03:00
suggestion = conf.string = candidate
2017-02-06 15:28:33 +03:00
injectable = True
infoMsg = "%sparameter '%s' appears to be '%s' injectable (with --string=\"%s\")" % ("%s " % paramType if paramType != parameter else "", parameter, title, repr(conf.string).lstrip('u').strip("'"))
2017-02-06 15:28:33 +03:00
logger.info(infoMsg)
break
if injectable:
if kb.pageStable and not any((conf.string, conf.notString, conf.regexp, conf.code, kb.nullConnection)):
if all((falseCode, trueCode)) and falseCode != trueCode:
2020-07-20 13:43:17 +03:00
suggestion = conf.code = trueCode
2016-06-03 16:51:52 +03:00
infoMsg = "%sparameter '%s' appears to be '%s' injectable (with --code=%d)" % ("%s " % paramType if paramType != parameter else "", parameter, title, conf.code)
2016-06-03 16:51:52 +03:00
logger.info(infoMsg)
else:
trueSet = set(extractTextTagContent(trueRawResponse))
2018-06-07 01:55:32 +03:00
trueSet |= set(__ for _ in trueSet for __ in _.split())
2015-02-21 15:59:44 +03:00
falseSet = set(extractTextTagContent(falseRawResponse))
2018-06-07 01:55:32 +03:00
falseSet |= set(__ for _ in falseSet for __ in _.split())
if threadData.lastErrorPage and threadData.lastErrorPage[1]:
errorSet = set(extractTextTagContent(threadData.lastErrorPage[1]))
2018-06-07 01:55:32 +03:00
errorSet |= set(__ for _ in errorSet for __ in _.split())
else:
errorSet = set()
2019-03-29 04:28:16 +03:00
candidates = filterNone(_.strip() if _.strip() in trueRawResponse and _.strip() not in falseRawResponse else None for _ in (trueSet - falseSet - errorSet))
2016-06-03 16:51:52 +03:00
if candidates:
2019-05-30 23:40:51 +03:00
candidates = sorted(candidates, key=len)
2016-06-03 16:51:52 +03:00
for candidate in candidates:
2019-11-26 15:51:08 +03:00
if re.match(r"\A\w{2,}\Z", candidate): # Note: length of 1 (e.g. --string=5) could cause trouble, especially in error message pages with partially reflected payload content
2016-06-03 16:51:52 +03:00
break
2012-04-11 01:57:00 +04:00
2020-07-20 13:43:17 +03:00
suggestion = conf.string = candidate
2016-06-03 16:51:52 +03:00
infoMsg = "%sparameter '%s' appears to be '%s' injectable (with --string=\"%s\")" % ("%s " % paramType if paramType != parameter else "", parameter, title, repr(conf.string).lstrip('u').strip("'"))
2016-06-03 16:51:52 +03:00
logger.info(infoMsg)
if not any((conf.string, conf.notString)):
2019-03-29 04:28:16 +03:00
candidates = filterNone(_.strip() if _.strip() in falseRawResponse and _.strip() not in trueRawResponse else None for _ in (falseSet - trueSet))
if candidates:
2019-05-30 23:40:51 +03:00
candidates = sorted(candidates, key=len)
for candidate in candidates:
if re.match(r"\A\w+\Z", candidate):
break
2020-07-20 13:43:17 +03:00
suggestion = conf.notString = candidate
infoMsg = "%sparameter '%s' appears to be '%s' injectable (with --not-string=\"%s\")" % ("%s " % paramType if paramType != parameter else "", parameter, title, repr(conf.notString).lstrip('u').strip("'"))
logger.info(infoMsg)
2020-07-20 13:43:17 +03:00
if not suggestion:
infoMsg = "%sparameter '%s' appears to be '%s' injectable " % ("%s " % paramType if paramType != parameter else "", parameter, title)
singleTimeLogMessage(infoMsg)
# In case of error-based SQL injection
2010-12-18 13:42:09 +03:00
elif method == PAYLOAD.METHOD.GREP:
# Perform the test's request and grep the response
# body for the test's <grep> regular expression
try:
2017-06-05 17:28:19 +03:00
page, headers, _ = Request.queryPage(reqPayload, place, content=True, raise404=False)
output = extractRegexResult(check, page, re.DOTALL | re.IGNORECASE)
output = output or extractRegexResult(check, threadData.lastHTTPError[2] if wasLastResponseHTTPError() else None, re.DOTALL | re.IGNORECASE)
output = output or extractRegexResult(check, listToStrValue((headers[key] for key in headers if key.lower() != URI_HTTP_HEADER.lower()) if headers else None), re.DOTALL | re.IGNORECASE)
output = output or extractRegexResult(check, threadData.lastRedirectMsg[1] if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE)
if output:
result = output == "1"
if result:
infoMsg = "%sparameter '%s' is '%s' injectable " % ("%s " % paramType if paramType != parameter else "", parameter, title)
logger.info(infoMsg)
injectable = True
2019-01-22 03:20:27 +03:00
except SqlmapConnectionException as ex:
2013-07-31 11:22:45 +04:00
debugMsg = "problem occurred most likely because the "
debugMsg += "server hasn't recovered as expected from the "
2020-11-27 01:41:35 +03:00
debugMsg += "used error-based payload ('%s')" % getSafeExString(ex)
logger.debug(debugMsg)
2010-12-18 13:42:09 +03:00
# In case of time-based blind or stacked queries
# SQL injections
elif method == PAYLOAD.METHOD.TIME:
# Perform the test's request
trueResult = Request.queryPage(reqPayload, place, timeBasedCompare=True, raise404=False)
trueCode = threadData.lastCode
2010-12-18 13:42:09 +03:00
if trueResult:
# Extra validation step (e.g. to check for DROP protection mechanisms)
if SLEEP_TIME_MARKER in reqPayload:
falseResult = Request.queryPage(reqPayload.replace(SLEEP_TIME_MARKER, "0"), place, timeBasedCompare=True, raise404=False)
if falseResult:
continue
2010-12-18 13:42:09 +03:00
# Confirm test's results
trueResult = Request.queryPage(reqPayload, place, timeBasedCompare=True, raise404=False)
2010-12-18 13:42:09 +03:00
if trueResult:
infoMsg = "%sparameter '%s' appears to be '%s' injectable " % ("%s " % paramType if paramType != parameter else "", parameter, title)
2010-12-18 13:42:09 +03:00
logger.info(infoMsg)
injectable = True
# In case of UNION query SQL injection
elif method == PAYLOAD.METHOD.UNION:
# Test for UNION injection and set the sample
# payload as well as the vector.
# NOTE: vector is set to a tuple with 6 elements,
# used afterwards by Agent.forgeUnionQuery()
# method to forge the UNION query payload
2011-01-15 19:59:53 +03:00
2011-01-18 01:57:33 +03:00
configUnion(test.request.char, test.request.columns)
2017-08-28 14:02:08 +03:00
if len(kb.dbmsFilter or []) == 1:
Backend.forceDbms(kb.dbmsFilter[0])
elif not Backend.getIdentifiedDbms():
if kb.heuristicDbms is None:
2017-11-24 14:20:57 +03:00
if kb.heuristicTest == HEURISTIC_TEST.POSITIVE or injection.data:
warnMsg = "using unescaped version of the test "
warnMsg += "because of zero knowledge of the "
warnMsg += "back-end DBMS. You can try to "
warnMsg += "explicitly set it with option '--dbms'"
singleTimeWarnMessage(warnMsg)
2013-01-25 15:34:57 +04:00
else:
Backend.forceDbms(kb.heuristicDbms)
2012-05-07 17:51:31 +04:00
if unionExtended:
2015-02-21 15:59:44 +03:00
infoMsg = "automatically extending ranges for UNION "
infoMsg += "query injection technique tests as "
2013-08-22 13:11:30 +04:00
infoMsg += "there is at least one other (potential) "
infoMsg += "technique found"
2012-05-07 17:51:31 +04:00
singleTimeLogMessage(infoMsg)
# Test for UNION query SQL injection
2011-01-16 04:17:09 +03:00
reqPayload, vector = unionTest(comment, place, parameter, value, prefix, suffix)
if isinstance(reqPayload, six.string_types):
infoMsg = "%sparameter '%s' is '%s' injectable" % ("%s " % paramType if paramType != parameter else "", parameter, title)
logger.info(infoMsg)
injectable = True
2011-01-16 03:15:30 +03:00
# Overwrite 'where' because it can be set
# by unionTest() directly
where = vector[6]
kb.previousMethod = method
if conf.offline:
2013-10-11 01:08:20 +04:00
injectable = False
2010-12-18 13:42:09 +03:00
# If the injection test was successful feed the injection
# object with the test's details
if injectable is True:
# Feed with the boundaries details only the first time a
# test has been successful
if injection.place is None or injection.parameter is None:
2012-07-26 14:26:57 +04:00
if place in (PLACE.USER_AGENT, PLACE.REFERER, PLACE.HOST):
2011-02-12 02:36:23 +03:00
injection.parameter = place
2010-12-18 13:42:09 +03:00
else:
injection.parameter = parameter
injection.place = place
injection.ptype = ptype
injection.prefix = prefix
injection.suffix = suffix
injection.clause = clause
# Feed with test details every time a test is successful
if hasattr(test, "details"):
2016-12-20 01:47:39 +03:00
for key, value in test.details.items():
if key == "dbms":
injection.dbms = value
2015-02-21 15:59:44 +03:00
2016-12-20 01:47:39 +03:00
if not isinstance(value, list):
Backend.setDbms(value)
else:
2016-12-20 01:47:39 +03:00
Backend.forceDbms(value[0], True)
2015-02-21 15:59:44 +03:00
2016-12-20 01:47:39 +03:00
elif key == "dbms_version" and injection.dbms_version is None and not conf.testFilter:
injection.dbms_version = Backend.setVersion(value)
2015-02-21 15:59:44 +03:00
2016-12-20 01:47:39 +03:00
elif key == "os" and injection.os is None:
injection.os = Backend.setOs(value)
if vector is None and "vector" in test and test.vector is not None:
vector = test.vector
2010-12-18 13:42:09 +03:00
2011-07-08 10:02:31 +04:00
injection.data[stype] = AttribDict()
2010-12-18 13:42:09 +03:00
injection.data[stype].title = title
injection.data[stype].payload = agent.removePayloadDelimiters(reqPayload)
2010-12-18 13:42:09 +03:00
injection.data[stype].where = where
injection.data[stype].vector = vector
2010-12-18 13:42:09 +03:00
injection.data[stype].comment = comment
injection.data[stype].templatePayload = templatePayload
2011-01-14 17:55:59 +03:00
injection.data[stype].matchRatio = kb.matchRatio
injection.data[stype].trueCode = trueCode
injection.data[stype].falseCode = falseCode
2010-12-18 13:42:09 +03:00
2011-01-14 17:55:59 +03:00
injection.conf.textOnly = conf.textOnly
2011-06-11 12:33:36 +04:00
injection.conf.titles = conf.titles
2016-06-03 16:51:52 +03:00
injection.conf.code = conf.code
2011-01-16 02:11:36 +03:00
injection.conf.string = conf.string
2012-07-26 14:06:02 +04:00
injection.conf.notString = conf.notString
2011-01-16 02:11:36 +03:00
injection.conf.regexp = conf.regexp
injection.conf.optimize = conf.optimize
if not kb.alerted:
if conf.beep:
beep()
2010-12-18 13:42:09 +03:00
if conf.alert:
infoMsg = "executing alerting shell command(s) ('%s')" % conf.alert
logger.info(infoMsg)
2019-11-11 16:03:43 +03:00
try:
2019-12-01 01:10:20 +03:00
process = subprocess.Popen(getBytes(conf.alert, sys.getfilesystemencoding() or UNICODE_ENCODING), shell=True)
2019-11-11 16:03:43 +03:00
process.wait()
except Exception as ex:
errMsg = "error occurred while executing '%s' ('%s')" % (conf.alert, getSafeExString(ex))
logger.error(errMsg)
2012-12-11 15:48:58 +04:00
kb.alerted = True
2012-12-11 15:48:58 +04:00
2010-12-18 13:42:09 +03:00
# There is no need to perform this test for other
# <where> tags
break
if injectable is True:
2012-08-22 18:50:01 +04:00
kb.vulnHosts.add(conf.hostname)
break
2008-10-15 19:38:22 +04:00
# Reset forced back-end DBMS value
Backend.flushForcedDbms()
2010-12-18 13:42:09 +03:00
except KeyboardInterrupt:
2011-04-08 14:39:07 +04:00
warnMsg = "user aborted during detection phase"
2010-12-18 13:42:09 +03:00
logger.warn(warnMsg)
2017-07-26 00:32:30 +03:00
if conf.multipleTargets:
msg = "how do you want to proceed? [ne(X)t target/(s)kip current test/(e)nd detection phase/(n)ext parameter/(c)hange verbosity/(q)uit]"
2018-05-28 12:07:06 +03:00
choice = readInput(msg, default='X', checkBatch=False).upper()
2017-07-26 00:32:30 +03:00
else:
msg = "how do you want to proceed? [(S)kip current test/(e)nd detection phase/(n)ext parameter/(c)hange verbosity/(q)uit]"
choice = readInput(msg, default='S', checkBatch=False).upper()
2017-07-26 00:32:30 +03:00
if choice == 'X':
if conf.multipleTargets:
raise SqlmapSkipTargetException
elif choice == 'C':
choice = None
while not ((choice or "").isdigit() and 0 <= int(choice) <= 6):
if choice:
logger.warn("invalid value")
msg = "enter new verbosity level: [0-6] "
2017-04-19 15:46:27 +03:00
choice = readInput(msg, default=str(conf.verbose), checkBatch=False)
conf.verbose = int(choice)
setVerbosity()
2014-10-07 14:00:11 +04:00
tests.insert(0, test)
2017-04-18 16:48:05 +03:00
elif choice == 'N':
return None
2017-04-18 16:48:05 +03:00
elif choice == 'E':
2011-06-08 18:44:11 +04:00
kb.endDetection = True
2017-04-18 16:48:05 +03:00
elif choice == 'Q':
raise SqlmapUserQuitException
2010-12-04 18:47:02 +03:00
finally:
# Reset forced back-end DBMS value
Backend.flushForcedDbms()
2011-06-02 03:00:18 +04:00
Backend.flushForcedDbms(True)
# Return the injection object
if injection.place is not None and injection.parameter is not None:
2011-06-03 19:43:50 +04:00
if not conf.dropSetCookie and PAYLOAD.TECHNIQUE.BOOLEAN in injection.data and injection.data[PAYLOAD.TECHNIQUE.BOOLEAN].vector.startswith('OR'):
2016-05-22 15:30:32 +03:00
warnMsg = "in OR boolean-based injection cases, please consider usage "
2012-02-01 18:49:42 +04:00
warnMsg += "of switch '--drop-set-cookie' if you experience any "
2011-06-03 19:43:50 +04:00
warnMsg += "problems during data retrieval"
logger.warn(warnMsg)
2016-05-06 14:06:59 +03:00
if not checkFalsePositives(injection):
2020-12-01 00:25:56 +03:00
if conf.hostname in kb.vulnHosts:
kb.vulnHosts.remove(conf.hostname)
2016-05-24 15:55:19 +03:00
if NOTE.FALSE_POSITIVE_OR_UNEXPLOITABLE not in injection.notes:
injection.notes.append(NOTE.FALSE_POSITIVE_OR_UNEXPLOITABLE)
2016-05-06 14:06:59 +03:00
else:
2012-10-25 15:21:32 +04:00
injection = None
2016-05-30 16:20:21 +03:00
if injection and NOTE.FALSE_POSITIVE_OR_UNEXPLOITABLE not in injection.notes:
2013-01-16 05:31:03 +04:00
checkSuhosinPatch(injection)
checkFilteredChars(injection)
2012-10-25 15:21:32 +04:00
return injection
2008-10-15 19:38:22 +04:00
@stackedmethod
2013-01-25 15:34:57 +04:00
def heuristicCheckDbms(injection):
"""
This functions is called when boolean-based blind is identified with a
generic payload and the DBMS has not yet been fingerprinted to attempt
to identify with a simple DBMS specific boolean-based test what the DBMS
may be
"""
2020-11-10 00:11:11 +03:00
retVal = False
2013-01-25 15:34:57 +04:00
2020-11-10 00:11:11 +03:00
if conf.skipHeuristics:
return retVal
2013-02-01 14:24:17 +04:00
pushValue(kb.injection)
kb.injection = injection
2013-01-25 15:34:57 +04:00
2013-02-01 14:24:17 +04:00
for dbms in getPublicTypeMembers(DBMS, True):
2020-01-23 01:41:06 +03:00
randStr1, randStr2 = randomStr(), randomStr()
2020-01-23 12:53:06 +03:00
2020-01-23 01:41:06 +03:00
Backend.forceDbms(dbms)
2020-02-02 16:51:24 +03:00
if dbms in HEURISTIC_NULL_EVAL:
result = checkBooleanExpression("(SELECT %s%s) IS NULL" % (HEURISTIC_NULL_EVAL[dbms], FROM_DUMMY_TABLE.get(dbms, "")))
elif not ((randStr1 in unescaper.escape("'%s'" % randStr1)) and list(FROM_DUMMY_TABLE.values()).count(FROM_DUMMY_TABLE.get(dbms, "")) != 1):
result = checkBooleanExpression("(SELECT '%s'%s)=%s%s%s" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), SINGLE_QUOTE_MARKER, randStr1, SINGLE_QUOTE_MARKER))
else:
result = False
if result:
if not checkBooleanExpression("(SELECT '%s'%s)=%s%s%s" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), SINGLE_QUOTE_MARKER, randStr2, SINGLE_QUOTE_MARKER)):
2013-02-01 14:24:17 +04:00
retVal = dbms
break
2013-01-25 15:34:57 +04:00
2013-02-01 14:24:17 +04:00
Backend.flushForcedDbms()
kb.injection = popValue()
2013-01-25 15:34:57 +04:00
2013-01-25 15:52:31 +04:00
if retVal:
infoMsg = "heuristic (extended) test shows that the back-end DBMS " # Not as important as "parsing" counter-part (because of false-positives)
2013-01-25 15:52:31 +04:00
infoMsg += "could be '%s' " % retVal
logger.info(infoMsg)
2017-06-05 17:48:14 +03:00
kb.heuristicExtendedDbms = retVal
2013-01-25 15:34:57 +04:00
return retVal
@stackedmethod
def checkFalsePositives(injection):
"""
Checks for false positives (only in single special cases)
"""
2016-05-06 14:06:59 +03:00
retVal = True
if all(_ in (PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) for _ in injection.data) or (len(injection.data) == 1 and PAYLOAD.TECHNIQUE.UNION in injection.data and "Generic" in injection.data[PAYLOAD.TECHNIQUE.UNION].title):
pushValue(kb.injection)
2011-04-25 04:36:09 +04:00
infoMsg = "checking if the injection point on %s " % injection.place
infoMsg += "parameter '%s' is a false positive" % injection.parameter
logger.info(infoMsg)
def _():
return int(randomInt(2)) + 1
kb.injection = injection
2018-09-14 11:01:31 +03:00
for level in xrange(conf.level):
2015-03-10 11:23:26 +03:00
while True:
randInt1, randInt2, randInt3 = (_() for j in xrange(3))
2012-10-16 12:24:05 +04:00
2015-03-10 11:23:26 +03:00
randInt1 = min(randInt1, randInt2, randInt3)
randInt3 = max(randInt1, randInt2, randInt3)
2019-11-26 16:03:22 +03:00
if conf.string and any(conf.string in getUnicode(_) for _ in (randInt1, randInt2, randInt3)):
continue
2020-05-05 14:31:44 +03:00
if conf.notString and any(conf.notString in getUnicode(_) for _ in (randInt1, randInt2, randInt3)):
continue
2015-03-10 11:23:26 +03:00
if randInt3 > randInt2 > randInt1:
break
2019-04-29 12:01:40 +03:00
if not checkBooleanExpression("%d%s%d" % (randInt1, INFERENCE_EQUALS_CHAR, randInt1)):
2016-05-06 14:06:59 +03:00
retVal = False
2012-10-16 12:24:05 +04:00
break
if PAYLOAD.TECHNIQUE.BOOLEAN not in injection.data:
2019-04-29 12:01:40 +03:00
checkBooleanExpression("%d%s%d" % (randInt1, INFERENCE_EQUALS_CHAR, randInt2)) # just in case if DBMS hasn't properly recovered from previous delayed request
if checkBooleanExpression("%d%s%d" % (randInt1, INFERENCE_EQUALS_CHAR, randInt3)): # this must not be evaluated to True
2016-05-06 14:06:59 +03:00
retVal = False
2012-10-16 12:24:05 +04:00
break
elif checkBooleanExpression("%d%s%d" % (randInt3, INFERENCE_EQUALS_CHAR, randInt2)): # this must not be evaluated to True
2016-05-06 14:06:59 +03:00
retVal = False
2012-10-16 12:24:05 +04:00
break
elif not checkBooleanExpression("%d%s%d" % (randInt2, INFERENCE_EQUALS_CHAR, randInt2)): # this must be evaluated to True
2016-05-06 14:06:59 +03:00
retVal = False
2012-10-16 12:24:05 +04:00
break
2019-04-29 12:01:40 +03:00
elif checkBooleanExpression("%d %d" % (randInt3, randInt2)): # this must not be evaluated to True (invalid statement)
2016-05-06 14:06:59 +03:00
retVal = False
2016-01-14 15:16:44 +03:00
break
2016-05-06 14:06:59 +03:00
if not retVal:
2014-08-20 03:45:42 +04:00
warnMsg = "false positive or unexploitable injection point detected"
logger.warn(warnMsg)
kb.injection = popValue()
return retVal
@stackedmethod
2013-01-16 05:31:03 +04:00
def checkSuhosinPatch(injection):
2012-10-25 15:21:32 +04:00
"""
2013-01-16 05:31:03 +04:00
Checks for existence of Suhosin-patch (and alike) protection mechanism(s)
2012-10-25 15:21:32 +04:00
"""
if injection.place == PLACE.GET:
2014-08-20 03:45:42 +04:00
debugMsg = "checking for parameter length "
2017-12-13 15:49:55 +03:00
debugMsg += "constraining mechanisms"
2014-08-20 03:45:42 +04:00
logger.debug(debugMsg)
2012-10-25 15:21:32 +04:00
pushValue(kb.injection)
kb.injection = injection
randInt = randomInt()
2013-05-15 15:38:26 +04:00
if not checkBooleanExpression("%d=%s%d" % (randInt, ' ' * SUHOSIN_MAX_VALUE_LENGTH, randInt)):
2017-12-13 15:49:55 +03:00
warnMsg = "parameter length constraining "
2013-01-16 05:31:03 +04:00
warnMsg += "mechanism detected (e.g. Suhosin patch). "
2012-10-25 15:21:32 +04:00
warnMsg += "Potential problems in enumeration phase can be expected"
logger.warn(warnMsg)
kb.injection = popValue()
@stackedmethod
def checkFilteredChars(injection):
2014-08-20 03:45:42 +04:00
debugMsg = "checking for filtered characters"
logger.debug(debugMsg)
pushValue(kb.injection)
kb.injection = injection
randInt = randomInt()
2014-08-20 03:59:30 +04:00
# all other techniques are already using parentheses in tests
if len(injection.data) == 1 and PAYLOAD.TECHNIQUE.BOOLEAN in injection.data:
if not checkBooleanExpression("(%d)=%d" % (randInt, randInt)):
warnMsg = "it appears that some non-alphanumeric characters (i.e. ()) are "
warnMsg += "filtered by the back-end server. There is a strong "
warnMsg += "possibility that sqlmap won't be able to properly "
warnMsg += "exploit this vulnerability"
2014-08-20 15:35:41 +04:00
logger.warn(warnMsg)
2014-08-20 03:59:30 +04:00
# inference techniques depend on character '>'
if not any(_ in injection.data for _ in (PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.QUERY)):
if not checkBooleanExpression("%d>%d" % (randInt + 1, randInt)):
2014-08-20 03:59:30 +04:00
warnMsg = "it appears that the character '>' is "
warnMsg += "filtered by the back-end server. You are strongly "
warnMsg += "advised to rerun with the '--tamper=between'"
logger.warn(warnMsg)
kb.injection = popValue()
2011-01-15 16:15:10 +03:00
def heuristicCheckSqlInjection(place, parameter):
2020-11-10 00:11:11 +03:00
if conf.skipHeuristics:
return None
origValue = conf.paramDict[place][parameter]
2014-11-21 13:20:54 +03:00
paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place
2016-12-20 01:47:39 +03:00
2010-10-11 16:26:35 +04:00
prefix = ""
suffix = ""
2016-12-20 01:47:39 +03:00
randStr = ""
2010-10-11 16:26:35 +04:00
if conf.prefix or conf.suffix:
2010-10-11 16:26:35 +04:00
if conf.prefix:
prefix = conf.prefix
if conf.suffix:
suffix = conf.suffix
2010-10-11 16:26:35 +04:00
while randStr.count('\'') != 1 or randStr.count('\"') != 1:
2012-10-28 03:42:08 +04:00
randStr = randomStr(length=10, alphabet=HEURISTIC_CHECK_ALPHABET)
kb.heuristicMode = True
2012-10-28 03:42:08 +04:00
payload = "%s%s%s" % (prefix, randStr, suffix)
payload = agent.payload(place, parameter, newValue=payload)
2017-06-05 17:28:19 +03:00
page, _, _ = Request.queryPage(payload, place, content=True, raise404=False)
kb.heuristicPage = page
kb.heuristicMode = False
parseFilePaths(page)
2013-01-29 23:53:11 +04:00
result = wasLastResponseDBMSError()
2010-10-16 19:10:48 +04:00
infoMsg = "heuristic (basic) test shows that %sparameter '%s' might " % ("%s " % paramType if paramType != parameter else "", parameter)
2010-10-16 19:10:48 +04:00
def _(page):
2016-06-26 02:42:21 +03:00
return any(_ in (page or "") for _ in FORMAT_EXCEPTION_STRINGS)
2012-08-20 14:14:01 +04:00
casting = _(page) and not _(kb.originalPage)
2012-08-20 14:14:01 +04:00
if not casting and not result and kb.dynamicParameter and origValue.isdigit() and not kb.heavilyDynamic:
randInt = int(randomInt())
payload = "%s%s%s" % (prefix, "%d-%d" % (int(origValue) + randInt, randInt), suffix)
payload = agent.payload(place, parameter, newValue=payload, where=PAYLOAD.WHERE.REPLACE)
result = Request.queryPage(payload, place, raise404=False)
2012-08-22 13:27:58 +04:00
if not result:
randStr = randomStr()
payload = "%s%s%s" % (prefix, "%s.%d%s" % (origValue, random.randint(1, 9), randStr), suffix)
payload = agent.payload(place, parameter, newValue=payload, where=PAYLOAD.WHERE.REPLACE)
casting = Request.queryPage(payload, place, raise404=False)
2010-10-11 16:26:35 +04:00
2012-08-29 22:21:45 +04:00
kb.heuristicTest = HEURISTIC_TEST.CASTED if casting else HEURISTIC_TEST.NEGATIVE if not result else HEURISTIC_TEST.POSITIVE
if kb.heavilyDynamic:
debugMsg = "heuristic check stopped because of heavy dynamicity"
logger.debug(debugMsg)
return kb.heuristicTest
2012-08-22 13:27:58 +04:00
if casting:
2018-12-21 13:29:57 +03:00
errMsg = "possible %s casting detected (e.g. '" % ("integer" if origValue.isdigit() else "type")
platform = conf.url.split('.')[-1].lower()
if platform == WEB_PLATFORM.ASP:
errMsg += "%s=CInt(request.querystring(\"%s\"))" % (parameter, parameter)
elif platform == WEB_PLATFORM.ASPX:
errMsg += "int.TryParse(Request.QueryString[\"%s\"], out %s)" % (parameter, parameter)
elif platform == WEB_PLATFORM.JSP:
errMsg += "%s=Integer.parseInt(request.getParameter(\"%s\"))" % (parameter, parameter)
else:
errMsg += "$%s=intval($_REQUEST[\"%s\"])" % (parameter, parameter)
errMsg += "') at the back-end web application"
2012-08-22 13:27:58 +04:00
logger.error(errMsg)
if kb.ignoreCasted is None:
2012-08-22 18:10:56 +04:00
message = "do you want to skip those kind of cases (and save scanning time)? %s " % ("[Y/n]" if conf.multipleTargets else "[y/N]")
2017-04-19 15:46:27 +03:00
kb.ignoreCasted = readInput(message, default='Y' if conf.multipleTargets else 'N', boolean=True)
2012-08-22 13:27:58 +04:00
elif result:
2013-02-05 13:02:11 +04:00
infoMsg += "be injectable"
if Backend.getErrorParsedDBMSes():
infoMsg += " (possible DBMS: '%s')" % Format.getErrorParsedDBMSes()
logger.info(infoMsg)
else:
infoMsg += "not be injectable"
logger.warn(infoMsg)
2014-10-01 15:31:48 +04:00
kb.heuristicMode = True
kb.disableHtmlDecoding = True
2014-10-01 15:31:48 +04:00
2016-01-15 00:21:47 +03:00
randStr1, randStr2 = randomStr(NON_SQLI_CHECK_PREFIX_SUFFIX_LENGTH), randomStr(NON_SQLI_CHECK_PREFIX_SUFFIX_LENGTH)
value = "%s%s%s" % (randStr1, DUMMY_NON_SQLI_CHECK_APPENDIX, randStr2)
2014-10-01 15:42:10 +04:00
payload = "%s%s%s" % (prefix, "'%s" % value, suffix)
2014-10-01 15:31:48 +04:00
payload = agent.payload(place, parameter, newValue=payload)
2017-06-05 17:28:19 +03:00
page, _, _ = Request.queryPage(payload, place, content=True, raise404=False)
2014-10-01 15:31:48 +04:00
2014-11-21 13:20:54 +03:00
paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place
2020-12-31 14:09:15 +03:00
# Reference: https://bugs.python.org/issue18183
if value.upper() in (page or "").upper():
infoMsg = "heuristic (XSS) test shows that %sparameter '%s' might be vulnerable to cross-site scripting (XSS) attacks" % ("%s " % paramType if paramType != parameter else "", parameter)
logger.info(infoMsg)
2020-12-02 01:10:53 +03:00
if conf.beep:
beep()
2016-05-30 17:06:39 +03:00
for match in re.finditer(FI_ERROR_REGEX, page or ""):
2016-01-15 00:21:47 +03:00
if randStr1.lower() in match.group(0).lower():
infoMsg = "heuristic (FI) test shows that %sparameter '%s' might be vulnerable to file inclusion (FI) attacks" % ("%s " % paramType if paramType != parameter else "", parameter)
2016-01-15 00:21:47 +03:00
logger.info(infoMsg)
2020-12-02 01:10:53 +03:00
if conf.beep:
beep()
2016-01-15 00:21:47 +03:00
break
2014-10-01 15:31:48 +04:00
kb.disableHtmlDecoding = False
2014-10-01 15:31:48 +04:00
kb.heuristicMode = False
return kb.heuristicTest
2010-12-20 13:13:14 +03:00
2008-10-15 19:38:22 +04:00
def checkDynParam(place, parameter, value):
"""
This function checks if the URL parameter is dynamic. If it is
2008-10-15 19:38:22 +04:00
dynamic, the content of the page differs, otherwise the
dynamicity might depend on another parameter.
"""
2021-01-12 15:21:51 +03:00
if kb.choices.redirect:
return None
2010-12-18 12:51:34 +03:00
kb.matchRatio = None
2012-01-14 00:56:06 +04:00
dynResult = None
randInt = randomInt()
2010-11-10 01:44:23 +03:00
2014-11-21 13:20:54 +03:00
paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place
infoMsg = "testing if %sparameter '%s' is dynamic" % ("%s " % paramType if paramType != parameter else "", parameter)
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
2012-01-14 00:56:06 +04:00
try:
payload = agent.payload(place, parameter, value, getUnicode(randInt))
dynResult = Request.queryPage(payload, place, raise404=False)
except SqlmapConnectionException:
2012-01-14 00:56:06 +04:00
pass
2008-10-15 19:38:22 +04:00
2012-08-20 14:14:01 +04:00
result = None if dynResult is None else not dynResult
kb.dynamicParameter = result
return result
2008-10-15 19:38:22 +04:00
def checkDynamicContent(firstPage, secondPage):
"""
2010-12-29 22:39:32 +03:00
This function checks for the dynamic content in the provided pages
"""
2010-11-04 12:18:32 +03:00
2010-11-04 00:51:36 +03:00
if kb.nullConnection:
debugMsg = "dynamic content checking skipped "
2010-11-04 12:18:32 +03:00
debugMsg += "because NULL connection used"
logger.debug(debugMsg)
2010-11-04 00:51:36 +03:00
return
if any(page is None for page in (firstPage, secondPage)):
warnMsg = "can't check dynamic content "
warnMsg += "because of lack of page content"
logger.critical(warnMsg)
return
if firstPage and secondPage and any(len(_) > MAX_DIFFLIB_SEQUENCE_LENGTH for _ in (firstPage, secondPage)):
ratio = None
else:
try:
seqMatcher = getCurrentThreadData().seqMatcher
seqMatcher.set_seq1(firstPage)
seqMatcher.set_seq2(secondPage)
ratio = seqMatcher.quick_ratio()
except MemoryError:
ratio = None
if ratio is None:
kb.skipSeqMatcher = True
2010-10-07 02:29:52 +04:00
2010-12-29 22:39:32 +03:00
# In case of an intolerable difference turn on dynamicity removal engine
elif ratio <= UPPER_RATIO_BOUND:
2010-12-29 22:39:32 +03:00
findDynamicContent(firstPage, secondPage)
2010-10-25 23:45:53 +04:00
2010-12-29 22:39:32 +03:00
count = 0
while not Request.queryPage():
count += 1
2010-12-29 22:39:32 +03:00
if count > conf.retries:
2017-11-24 14:20:57 +03:00
warnMsg = "target URL content appears to be too dynamic. "
warnMsg += "Switching to '--text-only' "
logger.warn(warnMsg)
conf.textOnly = True
return
2010-10-25 23:45:53 +04:00
2017-11-24 14:20:57 +03:00
warnMsg = "target URL content appears to be heavily dynamic. "
warnMsg += "sqlmap is going to retry the request(s)"
singleTimeLogMessage(warnMsg, logging.CRITICAL)
2010-10-25 23:45:53 +04:00
kb.heavilyDynamic = True
2017-06-05 17:28:19 +03:00
secondPage, _, _ = Request.queryPage(content=True)
2010-12-29 22:39:32 +03:00
findDynamicContent(firstPage, secondPage)
2010-09-13 17:31:01 +04:00
2008-10-15 19:38:22 +04:00
def checkStability():
"""
This function checks if the URL content is stable requesting the
2010-09-13 19:19:47 +04:00
same page two times with a small delay within each request to
2008-10-15 19:38:22 +04:00
assume that it is stable.
In case the content of the page differs when requesting
the same page, the dynamicity might depend on other parameters,
like for instance string matching (--string).
"""
2017-11-24 14:20:57 +03:00
infoMsg = "testing if the target URL content is stable"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
2012-02-22 19:53:36 +04:00
firstPage = kb.originalPage # set inside checkConnection()
2015-07-10 10:24:14 +03:00
2019-06-04 16:46:56 +03:00
delay = MAX_STABILITY_DELAY - (time.time() - (kb.originalPageTime or 0))
delay = max(0, min(MAX_STABILITY_DELAY, delay))
2015-07-10 10:24:14 +03:00
time.sleep(delay)
2017-06-05 17:28:19 +03:00
secondPage, _, _ = Request.queryPage(content=True, noteResponseTime=False, raise404=False)
2008-10-15 19:38:22 +04:00
2021-01-12 15:21:51 +03:00
if kb.choices.redirect:
return None
2010-10-25 17:52:21 +04:00
kb.pageStable = (firstPage == secondPage)
2010-10-25 17:52:21 +04:00
if kb.pageStable:
if firstPage:
2017-11-24 14:20:57 +03:00
infoMsg = "target URL content is stable"
2011-04-30 19:29:59 +04:00
logger.info(infoMsg)
else:
errMsg = "there was an error checking the stability of page "
2012-05-28 18:04:17 +04:00
errMsg += "because of lack of content. Please check the "
errMsg += "page request results (and probable errors) by "
errMsg += "using higher verbosity levels"
logger.error(errMsg)
2010-10-25 17:52:21 +04:00
else:
2018-09-18 17:52:17 +03:00
warnMsg = "target URL content is not stable (i.e. content differs). sqlmap will base the page "
2010-10-16 19:10:48 +04:00
warnMsg += "comparison on a sequence matcher. If no dynamic nor "
warnMsg += "injectable parameters are detected, or in case of "
warnMsg += "junk results, refer to user's manual paragraph "
2017-11-24 14:20:57 +03:00
warnMsg += "'Page comparison'"
logger.warn(warnMsg)
2008-10-15 19:38:22 +04:00
message = "how do you want to proceed? [(C)ontinue/(s)tring/(r)egex/(q)uit] "
2017-04-19 15:46:27 +03:00
choice = readInput(message, default='C').upper()
2010-10-16 19:10:48 +04:00
2017-04-18 16:48:05 +03:00
if choice == 'Q':
raise SqlmapUserQuitException
2010-10-16 19:10:48 +04:00
2017-04-18 16:48:05 +03:00
elif choice == 'S':
showStaticWords(firstPage, secondPage)
2010-10-16 19:10:48 +04:00
message = "please enter value for parameter 'string': "
2017-04-18 16:48:05 +03:00
string = readInput(message)
2010-10-16 19:10:48 +04:00
2017-04-18 16:48:05 +03:00
if string:
conf.string = string
2010-11-04 12:18:32 +03:00
if kb.nullConnection:
debugMsg = "turning off NULL connection "
2010-11-04 12:18:32 +03:00
debugMsg += "support because of string checking"
logger.debug(debugMsg)
kb.nullConnection = None
else:
2010-11-10 22:44:51 +03:00
errMsg = "Empty value supplied"
raise SqlmapNoneDataException(errMsg)
2010-10-16 19:10:48 +04:00
2017-04-18 16:48:05 +03:00
elif choice == 'R':
message = "please enter value for parameter 'regex': "
2017-04-18 16:48:05 +03:00
regex = readInput(message)
2010-10-16 19:10:48 +04:00
2017-04-18 16:48:05 +03:00
if regex:
conf.regex = regex
2010-11-04 12:18:32 +03:00
if kb.nullConnection:
debugMsg = "turning off NULL connection "
2010-11-04 12:18:32 +03:00
debugMsg += "support because of regex checking"
logger.debug(debugMsg)
kb.nullConnection = None
else:
2010-11-10 22:44:51 +03:00
errMsg = "Empty value supplied"
raise SqlmapNoneDataException(errMsg)
2011-01-06 11:54:50 +03:00
else:
2010-12-29 22:39:32 +03:00
checkDynamicContent(firstPage, secondPage)
2010-11-29 18:25:45 +03:00
2010-10-25 17:52:21 +04:00
return kb.pageStable
2010-03-12 15:23:05 +03:00
2008-10-15 19:38:22 +04:00
def checkString():
if not conf.string:
return True
infoMsg = "testing if the provided string is within the "
infoMsg += "target URL page content"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
2017-06-05 17:28:19 +03:00
page, headers, _ = Request.queryPage(content=True)
rawResponse = "%s%s" % (listToStrValue(headers.headers if headers else ""), page)
2008-10-15 19:38:22 +04:00
if conf.string not in rawResponse:
warnMsg = "you provided '%s' as the string to " % conf.string
warnMsg += "match, but such a string is not within the target "
warnMsg += "URL raw response, sqlmap will carry on anyway"
logger.warn(warnMsg)
2008-10-15 19:38:22 +04:00
return True
2008-10-15 19:38:22 +04:00
def checkRegexp():
if not conf.regexp:
return True
infoMsg = "testing if the provided regular expression matches within "
infoMsg += "the target URL page content"
logger.info(infoMsg)
2017-06-05 17:28:19 +03:00
page, headers, _ = Request.queryPage(content=True)
rawResponse = "%s%s" % (listToStrValue(headers.headers if headers else ""), page)
if not re.search(conf.regexp, rawResponse, re.I | re.M):
2018-09-18 17:52:17 +03:00
warnMsg = "you provided '%s' as the regular expression " % conf.regexp
warnMsg += "which does not have any match within the target URL raw response. sqlmap "
warnMsg += "will carry on anyway"
logger.warn(warnMsg)
return True
@stackedmethod
def checkWaf():
"""
Reference: http://seclists.org/nmap-dev/2011/q2/att-1005/http-waf-detect.nse
"""
2015-09-21 15:57:44 +03:00
if any((conf.string, conf.notString, conf.regexp, conf.dummy, conf.offline, conf.skipWaf)):
return None
if kb.originalCode == _http_client.NOT_FOUND:
2019-03-20 13:33:10 +03:00
return None
_ = hashDBRetrieve(HASHDB_KEYS.CHECK_WAF_RESULT, True)
if _ is not None:
if _:
warnMsg = "previous heuristics detected that the target "
2018-09-14 11:01:31 +03:00
warnMsg += "is protected by some kind of WAF/IPS"
logger.critical(warnMsg)
return _
2017-07-29 04:35:05 +03:00
if not kb.originalPage:
return None
2015-09-21 15:57:44 +03:00
infoMsg = "checking if the target is protected by "
2018-09-14 11:01:31 +03:00
infoMsg += "some kind of WAF/IPS"
2015-09-21 15:57:44 +03:00
logger.info(infoMsg)
retVal = False
2019-01-26 14:36:03 +03:00
payload = "%d %s" % (randomInt(), IPS_WAF_CHECK_PAYLOAD)
2013-07-08 14:44:14 +04:00
if PLACE.URI in conf.parameters:
place = PLACE.POST
value = "%s=%s" % (randomStr(), agent.addPayloadDelimiters(payload))
else:
place = PLACE.GET
value = "" if not conf.parameters.get(PLACE.GET) else conf.parameters[PLACE.GET] + DEFAULT_GET_POST_DELIMITER
value += "%s=%s" % (randomStr(), agent.addPayloadDelimiters(payload))
2021-01-12 15:21:51 +03:00
pushValue(kb.choices.redirect)
2018-10-12 01:29:43 +03:00
pushValue(kb.resendPostOnRedirect)
2015-09-21 14:23:56 +03:00
pushValue(conf.timeout)
2018-08-29 12:06:45 +03:00
2021-01-12 15:21:51 +03:00
kb.choices.redirect = REDIRECTION.YES
2018-10-12 01:29:43 +03:00
kb.resendPostOnRedirect = False
2019-06-04 15:44:06 +03:00
conf.timeout = IPS_WAF_CHECK_TIMEOUT
2015-09-21 14:23:56 +03:00
try:
2019-06-04 15:44:06 +03:00
retVal = (Request.queryPage(place=place, value=value, getRatioValue=True, noteResponseTime=False, silent=True, raise404=False, disableTampering=True)[1] or 0) < IPS_WAF_CHECK_RATIO
except SqlmapConnectionException:
retVal = True
finally:
kb.matchRatio = None
2018-08-29 12:06:45 +03:00
2015-09-21 14:23:56 +03:00
conf.timeout = popValue()
2018-10-12 01:29:43 +03:00
kb.resendPostOnRedirect = popValue()
2021-01-12 15:21:51 +03:00
kb.choices.redirect = popValue()
2019-05-24 15:18:18 +03:00
hashDBWrite(HASHDB_KEYS.CHECK_WAF_RESULT, retVal, True)
if retVal:
2019-05-24 14:54:10 +03:00
if not kb.identifiedWafs:
warnMsg = "heuristics detected that the target "
warnMsg += "is protected by some kind of WAF/IPS"
logger.critical(warnMsg)
message = "are you sure that you want to "
2019-05-24 15:18:18 +03:00
message += "continue with further target testing? [Y/n] "
choice = readInput(message, default='Y', boolean=True)
2019-05-24 14:54:10 +03:00
if not choice:
raise SqlmapUserQuitException
2019-05-24 15:18:18 +03:00
else:
if not conf.tamper:
warnMsg = "please consider usage of tamper scripts (option '--tamper')"
singleTimeWarnMessage(warnMsg)
2013-02-26 14:08:06 +04:00
return retVal
@stackedmethod
2010-09-16 12:43:10 +04:00
def checkNullConnection():
2010-10-15 15:17:17 +04:00
"""
Reference: http://www.wisec.it/sectou.php?id=472f952d79293
"""
2012-08-20 12:41:43 +04:00
if conf.data:
return False
2019-03-25 13:42:16 +03:00
_ = hashDBRetrieve(HASHDB_KEYS.CHECK_NULL_CONNECTION_RESULT, True)
if _ is not None:
kb.nullConnection = _
2010-09-16 12:43:10 +04:00
2019-03-25 13:42:16 +03:00
if _:
dbgMsg = "resuming NULL connection method '%s'" % _
logger.debug(dbgMsg)
2015-07-18 18:01:34 +03:00
2019-03-25 13:42:16 +03:00
else:
infoMsg = "testing NULL connection to the target URL"
logger.info(infoMsg)
2010-12-06 18:50:19 +03:00
2019-03-25 13:42:16 +03:00
pushValue(kb.pageCompress)
kb.pageCompress = False
2019-03-25 13:42:16 +03:00
try:
page, headers, _ = Request.getPage(method=HTTPMETHOD.HEAD, raise404=False)
2010-12-06 18:50:19 +03:00
2019-03-25 13:42:16 +03:00
if not page and HTTP_HEADER.CONTENT_LENGTH in (headers or {}):
kb.nullConnection = NULLCONNECTION.HEAD
2010-09-16 12:43:10 +04:00
2019-03-25 13:42:16 +03:00
infoMsg = "NULL connection is supported with HEAD method ('Content-Length')"
logger.info(infoMsg)
2013-05-17 17:04:25 +04:00
else:
2019-03-25 13:42:16 +03:00
page, headers, _ = Request.getPage(auxHeaders={HTTP_HEADER.RANGE: "bytes=-1"})
2013-05-17 17:04:25 +04:00
2019-03-25 13:42:16 +03:00
if page and len(page) == 1 and HTTP_HEADER.CONTENT_RANGE in (headers or {}):
kb.nullConnection = NULLCONNECTION.RANGE
2013-05-17 17:04:25 +04:00
2019-03-25 13:42:16 +03:00
infoMsg = "NULL connection is supported with GET method ('Range')"
2013-05-17 17:04:25 +04:00
logger.info(infoMsg)
2019-03-25 13:42:16 +03:00
else:
_, headers, _ = Request.getPage(skipRead=True)
2010-12-06 18:50:19 +03:00
2019-03-25 13:42:16 +03:00
if HTTP_HEADER.CONTENT_LENGTH in (headers or {}):
kb.nullConnection = NULLCONNECTION.SKIP_READ
2010-09-16 12:43:10 +04:00
2019-03-25 13:42:16 +03:00
infoMsg = "NULL connection is supported with 'skip-read' method"
logger.info(infoMsg)
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)
2013-05-17 18:04:05 +04:00
2019-03-25 13:42:16 +03:00
return kb.nullConnection in getPublicTypeMembers(NULLCONNECTION, True)
2010-09-16 12:43:10 +04:00
2010-11-15 15:19:22 +03:00
def checkConnection(suppressOutput=False):
2019-03-20 13:33:10 +03:00
threadData = getCurrentThreadData()
2018-05-03 14:31:27 +03:00
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:
debugMsg = "resolving hostname '%s'" % conf.hostname
logger.debug(debugMsg)
socket.getaddrinfo(conf.hostname, None)
except socket.gaierror:
errMsg = "host '%s' does not exist" % conf.hostname
raise SqlmapConnectionException(errMsg)
2019-01-22 02:40:48 +03:00
except socket.error as ex:
2018-05-03 14:31:27 +03:00
errMsg = "problem occurred while "
errMsg += "resolving a host name '%s' ('%s')" % (conf.hostname, getSafeExString(ex))
raise SqlmapConnectionException(errMsg)
2019-01-22 02:40:48 +03:00
except UnicodeError as ex:
2018-12-04 01:18:52 +03:00
errMsg = "problem occurred while "
errMsg += "handling a host name '%s' ('%s')" % (conf.hostname, getSafeExString(ex))
raise SqlmapDataException(errMsg)
2010-05-21 17:36:49 +04:00
if not suppressOutput and not conf.dummy and not conf.offline:
infoMsg = "testing connection to the target URL"
2010-11-15 15:19:22 +03:00
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
try:
2015-07-10 10:51:11 +03:00
kb.originalPageTime = time.time()
2019-03-20 13:33:10 +03:00
Request.queryPage(content=True, noteResponseTime=False)
2011-01-01 23:19:55 +03:00
kb.errorIsNone = False
if any(_ in (kb.serverHeader or "") for _ in PRECONNECT_INCOMPATIBLE_SERVERS):
singleTimeWarnMessage("turning off pre-connect mechanism because of incompatible server ('%s')" % kb.serverHeader)
conf.disablePrecon = True
2013-01-29 23:53:11 +04:00
if not kb.originalPage and wasLastResponseHTTPError():
2019-11-18 18:17:17 +03:00
if getLastRequestHTTPError() not in (conf.ignoreCode or []):
errMsg = "unable to retrieve page content"
raise SqlmapConnectionException(errMsg)
2013-01-29 23:53:11 +04:00
elif wasLastResponseDBMSError():
2012-07-17 03:25:02 +04:00
warnMsg = "there is a DBMS error found in the HTTP response body "
warnMsg += "which could interfere with the results of the tests"
2011-01-01 23:19:55 +03:00
logger.warn(warnMsg)
2013-01-29 23:53:11 +04:00
elif wasLastResponseHTTPError():
2019-07-17 14:20:24 +03:00
if getLastRequestHTTPError() not in (conf.ignoreCode or []):
2017-08-23 14:17:37 +03:00
warnMsg = "the web server responded with an HTTP error code (%d) " % getLastRequestHTTPError()
warnMsg += "which could interfere with the results of the tests"
logger.warn(warnMsg)
2011-01-01 23:19:55 +03:00
else:
kb.errorIsNone = True
2012-05-25 12:30:24 +04:00
2021-01-12 15:21:51 +03:00
if kb.choices.redirect == REDIRECTION.YES and threadData.lastRedirectURL and threadData.lastRedirectURL[0] == threadData.lastRequestUID:
2019-04-18 17:06:19 +03:00
if (threadData.lastRedirectURL[1] or "").startswith("https://") and conf.hostname in getUnicode(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
2019-05-08 13:28:50 +03:00
conf.url = re.sub(r":\d+(/|\Z)", r":%s\g<1>" % port, conf.url)
2019-01-22 02:40:48 +03:00
except SqlmapConnectionException as ex:
2012-05-25 12:30:24 +04:00
if conf.ipv6:
warnMsg = "check connection to a provided "
warnMsg += "IPv6 address with a tool like ping6 "
2013-05-20 00:17:53 +04:00
warnMsg += "(e.g. 'ping6 -I eth0 %s') " % conf.hostname
2012-05-25 12:30:24 +04:00
warnMsg += "prior to running sqlmap to avoid "
warnMsg += "any addressing issues"
singleTimeWarnMessage(warnMsg)
if any(code in kb.httpErrorCodes for code in (_http_client.NOT_FOUND, )):
errMsg = getSafeExString(ex)
2014-06-16 11:51:24 +04:00
logger.critical(errMsg)
2012-02-07 15:16:03 +04:00
if conf.multipleTargets:
return False
2011-12-16 03:29:11 +04:00
msg = "it is not recommended to continue in this kind of cases. Do you want to quit and make sure that everything is set up properly? [Y/n] "
2017-04-18 16:48:05 +03:00
if readInput(msg, default='Y', boolean=True):
raise SqlmapSilentQuitException
2011-12-16 03:29:11 +04:00
else:
kb.ignoreNotFound = True
2011-12-05 13:25:56 +04:00
else:
2011-12-16 03:29:11 +04:00
raise
2019-03-20 13:33:10 +03:00
finally:
kb.originalPage = kb.pageTemplate = threadData.lastPage
kb.originalCode = threadData.lastCode
2008-10-15 19:38:22 +04:00
2020-08-20 14:54:52 +03:00
if conf.cj and not conf.cookie and not any(_[0] == HTTP_HEADER.COOKIE for _ in conf.httpHeaders) and not conf.dropSetCookie:
2019-10-14 11:42:10 +03:00
candidate = DEFAULT_COOKIE_DELIMITER.join("%s=%s" % (_.name, _.value) for _ in conf.cj)
message = "you have not declared cookie(s), while "
message += "server wants to set its own ('%s'). " % re.sub(r"(=[^=;]{10}[^=;])[^=;]+([^=;]{10})", r"\g<1>...\g<2>", candidate)
message += "Do you want to use those [Y/n] "
if readInput(message, default='Y', boolean=True):
2019-10-21 11:11:38 +03:00
kb.mergeCookies = True
2019-10-14 11:42:10 +03:00
conf.httpHeaders.append((HTTP_HEADER.COOKIE, candidate))
2008-10-15 19:38:22 +04:00
return True
2017-05-08 00:12:42 +03:00
def checkInternet():
content = Request.getPage(url=CHECK_INTERNET_ADDRESS, checking=True)[0]
return CHECK_INTERNET_VALUE in (content or "")
2017-05-08 00:12:42 +03:00
2018-03-21 16:29:54 +03:00
def setVerbosity(): # Cross-referenced function
raise NotImplementedError