2013-02-14 15:32:17 +04:00
|
|
|
#!/usr/bin/env python
|
2008-10-15 19:38:22 +04:00
|
|
|
|
|
|
|
"""
|
2013-01-18 18:07:51 +04:00
|
|
|
Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/)
|
2010-10-15 03:18:29 +04:00
|
|
|
See the file 'doc/COPYING' for copying permission
|
2008-10-15 19:38:22 +04:00
|
|
|
"""
|
|
|
|
|
2012-08-22 15:58:52 +04:00
|
|
|
import copy
|
2011-12-16 03:33:44 +04:00
|
|
|
import httplib
|
2008-12-12 22:06:31 +03:00
|
|
|
import re
|
2010-05-21 17:36:49 +04:00
|
|
|
import socket
|
2008-10-15 19:38:22 +04:00
|
|
|
import time
|
|
|
|
|
2012-12-11 15:48:58 +04:00
|
|
|
from subprocess import Popen as execute
|
|
|
|
|
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
|
2011-02-14 00:20:21 +03:00
|
|
|
from lib.core.common import arrayizeValue
|
2011-01-28 19:36:09 +03:00
|
|
|
from lib.core.common import Backend
|
2010-12-06 18:50:19 +03:00
|
|
|
from lib.core.common import extractRegexResult
|
2012-04-11 01:48:34 +04:00
|
|
|
from lib.core.common import extractTextTagContent
|
2010-12-29 22:39:32 +03:00
|
|
|
from lib.core.common import findDynamicContent
|
2011-01-28 19:36:09 +03:00
|
|
|
from lib.core.common import Format
|
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
|
2011-01-20 02:06:15 +03:00
|
|
|
from lib.core.common import getSortedInjectionTests
|
2010-06-02 16:45:40 +04:00
|
|
|
from lib.core.common import getUnicode
|
2011-02-14 00:20:21 +03:00
|
|
|
from lib.core.common import intersect
|
2011-01-31 15:21:17 +03:00
|
|
|
from lib.core.common import listToStrValue
|
2011-11-22 12:39:13 +04:00
|
|
|
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
|
2010-10-12 19:49:04 +04:00
|
|
|
from lib.core.common import showStaticWords
|
2012-04-23 17:41:36 +04:00
|
|
|
from lib.core.common import singleTimeLogMessage
|
2011-06-08 18:35:23 +04:00
|
|
|
from lib.core.common import singleTimeWarnMessage
|
2013-02-21 17:33:12 +04:00
|
|
|
from lib.core.common import urlencode
|
2013-01-29 23:53:11 +04:00
|
|
|
from lib.core.common import wasLastResponseDBMSError
|
|
|
|
from lib.core.common import wasLastResponseHTTPError
|
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
|
2013-02-21 17:33:12 +04:00
|
|
|
from lib.core.decorators import cachedmethod
|
2013-01-25 15:34:57 +04:00
|
|
|
from lib.core.dicts import FROM_DUMMY_TABLE
|
|
|
|
from lib.core.enums import DBMS
|
2012-08-22 13:56:30 +04:00
|
|
|
from lib.core.enums import HEURISTIC_TEST
|
2011-03-11 23:16:34 +03:00
|
|
|
from lib.core.enums import HTTPHEADER
|
2010-11-08 12:49:57 +03:00
|
|
|
from lib.core.enums import HTTPMETHOD
|
|
|
|
from lib.core.enums import NULLCONNECTION
|
2010-11-28 21:10:54 +03:00
|
|
|
from lib.core.enums import PAYLOAD
|
2010-12-01 20:09:52 +03:00
|
|
|
from lib.core.enums import PLACE
|
2012-12-06 17:14:19 +04:00
|
|
|
from lib.core.exception import SqlmapConnectionException
|
|
|
|
from lib.core.exception import SqlmapNoneDataException
|
|
|
|
from lib.core.exception import SqlmapSilentQuitException
|
|
|
|
from lib.core.exception import SqlmapUserQuitException
|
2012-08-22 17:51:47 +04:00
|
|
|
from lib.core.settings import FORMAT_EXCEPTION_STRINGS
|
2012-10-28 03:42:08 +04:00
|
|
|
from lib.core.settings import HEURISTIC_CHECK_ALPHABET
|
2013-01-16 05:31:03 +04:00
|
|
|
from lib.core.settings import SUHOSIN_MAX_VALUE_LENGTH
|
2013-01-25 15:34:57 +04:00
|
|
|
from lib.core.settings import UNKNOWN_DBMS
|
2011-01-21 21:32:10 +03:00
|
|
|
from lib.core.settings import LOWER_RATIO_BOUND
|
2010-12-29 22:01:29 +03:00
|
|
|
from lib.core.settings import UPPER_RATIO_BOUND
|
2011-07-06 09:44:47 +04:00
|
|
|
from lib.core.settings import IDS_WAF_CHECK_PAYLOAD
|
2011-01-16 13:52:42 +03:00
|
|
|
from lib.core.threads import getCurrentThreadData
|
2008-10-15 19:38:22 +04:00
|
|
|
from lib.request.connect import Connect as Request
|
2011-04-22 16:24:16 +04:00
|
|
|
from lib.request.inject import checkBooleanExpression
|
2010-12-07 17:35:31 +03:00
|
|
|
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
|
2010-11-28 21:10:54 +03:00
|
|
|
|
|
|
|
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()
|
2010-11-28 21:10:54 +03:00
|
|
|
|
2011-03-17 12:23:46 +03:00
|
|
|
# Localized thread data needed for some methods
|
|
|
|
threadData = getCurrentThreadData()
|
|
|
|
|
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
|
|
|
|
2011-01-20 02:06:15 +03:00
|
|
|
for test in getSortedInjectionTests():
|
2011-01-12 01:18:47 +03:00
|
|
|
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 not injection.dbms and PAYLOAD.TECHNIQUE.BOOLEAN in injection.data:
|
|
|
|
if not Backend.getIdentifiedDbms() and not kb.heuristicDbms:
|
|
|
|
kb.heuristicDbms = heuristicCheckDbms(injection) or UNKNOWN_DBMS
|
|
|
|
|
|
|
|
if not conf.testFilter and (Backend.getErrorParsedDBMSes() or kb.heuristicDbms) not in ([], None, UNKNOWN_DBMS):
|
|
|
|
if kb.reduceTests is None and Backend.getErrorParsedDBMSes():
|
|
|
|
msg = "heuristic (parsing) test showed that the "
|
|
|
|
msg += "back-end DBMS could be '%s'. " % (Format.getErrorParsedDBMSes() if Backend.getErrorParsedDBMSes() else kb.heuristicDbms)
|
|
|
|
msg += "Do you want to skip test payloads specific for other DBMSes? [Y/n]"
|
|
|
|
kb.reduceTests = [] if readInput(msg, default='Y').upper() != 'Y' else (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms])
|
|
|
|
|
|
|
|
if kb.extendTests is None:
|
|
|
|
_ = (Format.getErrorParsedDBMSes() if Backend.getErrorParsedDBMSes() else kb.heuristicDbms)
|
|
|
|
msg = "do you want to include all tests for '%s' " % _
|
|
|
|
msg += "ignoring provided level (%d) and risk (%s)? [Y/n]" % (conf.level, conf.risk)
|
|
|
|
kb.extendTests = [] if readInput(msg, default='Y').upper() != 'Y' else (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms])
|
|
|
|
|
2010-12-18 13:42:09 +03:00
|
|
|
title = test.title
|
|
|
|
stype = test.stype
|
|
|
|
clause = test.clause
|
2012-05-07 17:51:31 +04:00
|
|
|
unionExtended = False
|
2010-12-18 13:42:09 +03:00
|
|
|
|
2011-05-10 19:34:54 +04:00
|
|
|
if stype == PAYLOAD.TECHNIQUE.UNION:
|
2011-01-19 02:03:50 +03:00
|
|
|
configUnion(test.request.char)
|
|
|
|
|
2011-05-10 19:34:54 +04:00
|
|
|
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")
|
|
|
|
|
2011-01-19 02:03:50 +03:00
|
|
|
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
|
|
|
|
2011-05-10 19:34:54 +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
|
2011-04-07 15:10:35 +04:00
|
|
|
|
2012-04-23 17:41:36 +04:00
|
|
|
match = re.search(r"(\d+)-(\d+)", test.request.columns)
|
|
|
|
if injection.data and match:
|
|
|
|
lower, upper = int(match.group(1)), int(match.group(2))
|
|
|
|
for _ in (lower, upper):
|
|
|
|
if _ > 1:
|
2012-05-07 17:51:31 +04:00
|
|
|
unionExtended = True
|
2012-04-23 17:41:36 +04:00
|
|
|
test.request.columns = re.sub(r"\b%d\b" % _, str(2 * _), test.request.columns)
|
|
|
|
title = re.sub(r"\b%d\b" % _, str(2 * _), title)
|
|
|
|
test.title = re.sub(r"\b%d\b" % _, str(2 * _), test.title)
|
|
|
|
|
2010-12-18 13:42:09 +03:00
|
|
|
# Skip test if the user's wants to test only for a specific
|
|
|
|
# technique
|
2011-04-06 18:41:44 +04:00
|
|
|
if conf.tech and isinstance(conf.tech, list) and stype not in conf.tech:
|
2010-12-18 13:42:09 +03:00
|
|
|
debugMsg = "skipping test '%s' because the user " % title
|
|
|
|
debugMsg += "specified to test only for "
|
2011-05-03 01:48:08 +04:00
|
|
|
debugMsg += "%s techniques" % " & ".join(map(lambda x: PAYLOAD.SQLINJECTION[x], conf.tech))
|
2010-11-29 17:48:07 +03:00
|
|
|
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
|
|
|
|
|
2013-02-01 20:24:04 +04:00
|
|
|
|
|
|
|
# Skip DBMS-specific test if it does not match either the
|
|
|
|
# previously identified or the user's provided DBMS (either
|
|
|
|
# from program switch or from parsed error message(s))
|
|
|
|
if "details" in test and "dbms" in test.details:
|
|
|
|
dbms = test.details.dbms
|
|
|
|
else:
|
|
|
|
dbms = None
|
|
|
|
|
2011-09-27 18:09:25 +04:00
|
|
|
# Skip tests if title is not included by the given filter
|
2012-07-24 17:43:29 +04:00
|
|
|
if conf.testFilter:
|
2013-02-01 20:24:04 +04:00
|
|
|
if not any(re.search(conf.testFilter, str(item), re.I) for item in (test.title, test.vector, dbms)):
|
2011-09-27 18:31:58 +04:00
|
|
|
debugMsg = "skipping test '%s' because " % title
|
2012-03-14 02:03:23 +04:00
|
|
|
debugMsg += "its name/vector/dbms is not included by the given filter"
|
2011-09-27 18:31:58 +04:00
|
|
|
logger.debug(debugMsg)
|
|
|
|
continue
|
2013-02-01 20:24:04 +04:00
|
|
|
|
2013-02-05 13:58:02 +04:00
|
|
|
elif not (kb.extendTests and intersect(dbms, kb.extendTests)):
|
2011-09-27 18:31:58 +04:00
|
|
|
# Skip test if the risk is higher than the provided (or default)
|
|
|
|
# value
|
|
|
|
# Parse test's <risk>
|
|
|
|
if test.risk > conf.risk:
|
|
|
|
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
|
|
|
|
# Parse test's <level>
|
|
|
|
if test.level > conf.level:
|
|
|
|
debugMsg = "skipping test '%s' because the level (%d) " % (title, test.level)
|
|
|
|
debugMsg += "is higher than the provided (%d)" % conf.level
|
|
|
|
logger.debug(debugMsg)
|
|
|
|
continue
|
2011-09-27 18:09:25 +04:00
|
|
|
|
2010-12-18 13:42:09 +03:00
|
|
|
if dbms is not None:
|
2011-02-14 00:20:21 +03:00
|
|
|
if injection.dbms is not None and not intersect(injection.dbms, dbms):
|
2010-12-18 13:42:09 +03:00
|
|
|
debugMsg = "skipping test '%s' because " % title
|
|
|
|
debugMsg += "the back-end DBMS identified is "
|
|
|
|
debugMsg += "%s" % injection.dbms
|
|
|
|
logger.debug(debugMsg)
|
|
|
|
continue
|
2010-11-28 21:10:54 +03:00
|
|
|
|
2011-02-14 00:20:21 +03:00
|
|
|
if conf.dbms is not None and not intersect(conf.dbms.lower(), [value.lower() for value in arrayizeValue(dbms)]):
|
2010-12-18 13:42:09 +03:00
|
|
|
debugMsg = "skipping test '%s' because " % title
|
|
|
|
debugMsg += "the provided DBMS is %s" % conf.dbms
|
|
|
|
logger.debug(debugMsg)
|
|
|
|
continue
|
|
|
|
|
2013-02-01 20:24:04 +04:00
|
|
|
if kb.reduceTests and not intersect(dbms, kb.reduceTests):
|
2011-01-02 02:38:11 +03:00
|
|
|
debugMsg = "skipping test '%s' because " % title
|
2011-01-02 10:09:04 +03:00
|
|
|
debugMsg += "the parsed error message(s) showed "
|
|
|
|
debugMsg += "that the back-end DBMS could be "
|
2011-01-28 19:36:09 +03:00
|
|
|
debugMsg += "%s" % Format.getErrorParsedDBMSes()
|
2011-01-02 02:38:11 +03:00
|
|
|
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
|
2010-11-28 21:10:54 +03:00
|
|
|
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:
|
2010-11-28 21:10:54 +03:00
|
|
|
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 += "differs from the clause already identified"
|
|
|
|
logger.debug(debugMsg)
|
2010-11-28 21:10:54 +03:00
|
|
|
continue
|
|
|
|
|
2011-05-10 19:34:54 +04:00
|
|
|
# Skip test if the user provided custom character
|
|
|
|
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
|
|
|
|
|
2010-12-18 13:42:09 +03:00
|
|
|
infoMsg = "testing '%s'" % title
|
|
|
|
logger.info(infoMsg)
|
2010-11-28 21:10:54 +03:00
|
|
|
|
2011-02-07 02:27:56 +03:00
|
|
|
# Force back-end DBMS according to the current
|
|
|
|
# test value for proper payload unescaping
|
2011-02-14 00:20:21 +03:00
|
|
|
Backend.forceDbms(dbms[0] if isinstance(dbms, list) else dbms)
|
2011-02-07 02:27:56 +03:00
|
|
|
|
2010-12-18 13:42:09 +03:00
|
|
|
# Parse test's <request>
|
2011-08-02 22:20:21 +04:00
|
|
|
comment = agent.getComment(test.request) if len(conf.boundaries) > 1 else None
|
2011-02-07 02:27:56 +03:00
|
|
|
fstPayload = agent.cleanupPayload(test.request.payload, origValue=value)
|
2010-11-28 21:10:54 +03:00
|
|
|
|
2012-08-22 15:58:52 +04:00
|
|
|
# Favoring non-string specific boundaries in case of digit-like parameter values
|
|
|
|
if value.isdigit():
|
|
|
|
boundaries = sorted(copy.deepcopy(conf.boundaries), key=lambda x: any(_ in (x.prefix or "") or _ in (x.suffix or "") for _ in ('"', '\'')))
|
|
|
|
else:
|
|
|
|
boundaries = conf.boundaries
|
|
|
|
|
|
|
|
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:
|
|
|
|
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 ""
|
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
|
|
|
ptype = boundary.ptype
|
|
|
|
|
|
|
|
# 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 condBound or condType:
|
|
|
|
continue
|
|
|
|
|
|
|
|
# For each test's <where>
|
|
|
|
for where in test.where:
|
|
|
|
templatePayload = None
|
2011-01-12 03:47:39 +03:00
|
|
|
vector = None
|
2010-12-18 13:42:09 +03:00
|
|
|
|
|
|
|
# Threat the parameter original value according to the
|
|
|
|
# test's <where> tag
|
2011-02-02 16:34:09 +03:00
|
|
|
if where == PAYLOAD.WHERE.ORIGINAL:
|
2010-12-18 13:42:09 +03:00
|
|
|
origValue = value
|
2011-02-02 16:34:09 +03:00
|
|
|
elif where == PAYLOAD.WHERE.NEGATIVE:
|
2011-01-12 01:18:47 +03:00
|
|
|
# Use different page template than the original
|
|
|
|
# one as we are changing parameters value, which
|
|
|
|
# will likely result in a different content
|
2012-04-26 00:29:07 +04:00
|
|
|
if conf.invalidLogical:
|
2013-03-01 15:09:03 +04:00
|
|
|
_ = randomInt(2)
|
|
|
|
origValue = "%s AND %s=%s" % (value, _, _ + 1)
|
2012-04-26 00:29:07 +04:00
|
|
|
elif conf.invalidBignum:
|
|
|
|
origValue = "%d.%d" % (randomInt(6), randomInt(1))
|
|
|
|
else:
|
|
|
|
origValue = "-%s" % randomInt()
|
2011-01-12 01:18:47 +03:00
|
|
|
templatePayload = agent.payload(place, parameter, 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 = ""
|
|
|
|
|
2010-12-24 15:13:48 +03:00
|
|
|
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
|
2011-01-12 01:18:47 +03:00
|
|
|
boundPayload = agent.prefixQuery(fstPayload, prefix, where, clause)
|
2011-01-24 15:25:45 +03:00
|
|
|
boundPayload = agent.suffixQuery(boundPayload, comment, suffix, where)
|
2011-01-12 01:18:47 +03:00
|
|
|
reqPayload = agent.payload(place, parameter, newValue=boundPayload, where=where)
|
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():
|
2011-02-07 02:27:56 +03:00
|
|
|
check = agent.cleanupPayload(check, origValue=value)
|
2010-12-18 13:42:09 +03:00
|
|
|
|
|
|
|
# In case of boolean-based blind SQL injection
|
|
|
|
if method == PAYLOAD.METHOD.COMPARISON:
|
2011-02-08 00:53:05 +03:00
|
|
|
# Generate payload used for comparison
|
|
|
|
def genCmpPayload():
|
|
|
|
sndPayload = agent.cleanupPayload(test.response.comparison, origValue=value)
|
|
|
|
|
|
|
|
# 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
|
|
|
|
2011-02-08 00:53:05 +03:00
|
|
|
return cmpPayload
|
2010-12-18 13:42:09 +03:00
|
|
|
|
|
|
|
# Useful to set kb.matchRatio at first based on
|
|
|
|
# the False response content
|
|
|
|
kb.matchRatio = None
|
2012-03-29 18:33:27 +04:00
|
|
|
kb.negativeLogic = (where == PAYLOAD.WHERE.NEGATIVE)
|
2012-02-22 19:53:36 +04:00
|
|
|
Request.queryPage(genCmpPayload(), place, raise404=False)
|
2013-01-18 19:49:35 +04:00
|
|
|
falsePage = threadData.lastComparisonPage or ""
|
2010-12-18 13:42:09 +03:00
|
|
|
|
|
|
|
# Perform the test's True request
|
2010-12-24 15:36:00 +03:00
|
|
|
trueResult = Request.queryPage(reqPayload, place, raise404=False)
|
2013-01-18 19:49:35 +04:00
|
|
|
truePage = threadData.lastComparisonPage or ""
|
2010-11-28 21:10:54 +03:00
|
|
|
|
2010-12-18 13:42:09 +03:00
|
|
|
if trueResult:
|
2011-02-08 00:53:05 +03:00
|
|
|
falseResult = Request.queryPage(genCmpPayload(), place, raise404=False)
|
2010-11-28 21:10:54 +03:00
|
|
|
|
2010-12-18 13:42:09 +03:00
|
|
|
# Perform the test's False request
|
|
|
|
if not falseResult:
|
|
|
|
infoMsg = "%s parameter '%s' is '%s' injectable " % (place, parameter, title)
|
|
|
|
logger.info(infoMsg)
|
2010-11-28 21:10:54 +03:00
|
|
|
|
2010-12-18 13:42:09 +03:00
|
|
|
injectable = True
|
2012-04-11 01:57:00 +04:00
|
|
|
|
2012-07-26 14:06:02 +04:00
|
|
|
if not injectable and not any((conf.string, conf.notString, conf.regexp)) and kb.pageStable:
|
2013-01-18 19:49:35 +04:00
|
|
|
trueSet = set(extractTextTagContent(truePage))
|
|
|
|
falseSet = set(extractTextTagContent(falsePage))
|
2013-01-18 20:00:11 +04:00
|
|
|
candidates = filter(None, (_.strip() if _.strip() in (kb.pageTemplate or "") and _.strip() not in falsePage and _.strip() not in threadData.lastComparisonHeaders else None for _ in (trueSet - falseSet)))
|
2012-04-13 14:54:30 +04:00
|
|
|
if candidates:
|
2013-01-18 19:35:09 +04:00
|
|
|
conf.string = candidates[0]
|
2012-07-31 13:32:53 +04:00
|
|
|
infoMsg = "%s parameter '%s' seems to be '%s' injectable (with --string=\"%s\")" % (place, parameter, title, repr(conf.string).lstrip('u').strip("'"))
|
2012-04-11 01:48:34 +04:00
|
|
|
logger.info(infoMsg)
|
2012-04-11 01:57:00 +04:00
|
|
|
|
2012-04-11 01:48:34 +04:00
|
|
|
injectable = True
|
2010-11-28 21:10:54 +03:00
|
|
|
|
2011-01-12 01:18:47 +03:00
|
|
|
# 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
|
2011-03-30 22:32:10 +04:00
|
|
|
try:
|
|
|
|
page, headers = Request.queryPage(reqPayload, place, content=True, raise404=False)
|
|
|
|
output = extractRegexResult(check, page, re.DOTALL | re.IGNORECASE) \
|
|
|
|
or extractRegexResult(check, listToStrValue(headers.headers \
|
|
|
|
if headers else None), re.DOTALL | re.IGNORECASE) \
|
|
|
|
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 = "%s parameter '%s' is '%s' injectable " % (place, parameter, title)
|
|
|
|
logger.info(infoMsg)
|
|
|
|
|
|
|
|
injectable = True
|
|
|
|
|
2012-12-06 17:14:19 +04:00
|
|
|
except SqlmapConnectionException, msg:
|
2011-04-30 17:20:05 +04:00
|
|
|
debugMsg = "problem occured most likely because the "
|
2011-03-30 22:32:10 +04:00
|
|
|
debugMsg += "server hasn't recovered as expected from the "
|
|
|
|
debugMsg += "error-based payload used ('%s')" % msg
|
|
|
|
logger.debug(debugMsg)
|
2010-11-28 21:10:54 +03:00
|
|
|
|
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
|
2010-12-24 15:36:00 +03:00
|
|
|
trueResult = Request.queryPage(reqPayload, place, timeBasedCompare=True, raise404=False)
|
2010-12-18 13:42:09 +03:00
|
|
|
|
|
|
|
if trueResult:
|
|
|
|
# Confirm test's results
|
2010-12-24 15:36:00 +03:00
|
|
|
trueResult = Request.queryPage(reqPayload, place, timeBasedCompare=True, raise404=False)
|
2010-12-18 13:42:09 +03:00
|
|
|
|
|
|
|
if trueResult:
|
|
|
|
infoMsg = "%s parameter '%s' is '%s' injectable " % (place, parameter, title)
|
|
|
|
logger.info(infoMsg)
|
|
|
|
|
|
|
|
injectable = True
|
|
|
|
|
2011-01-12 01:18:47 +03:00
|
|
|
# In case of UNION query SQL injection
|
|
|
|
elif method == PAYLOAD.METHOD.UNION:
|
2011-01-12 15:01:32 +03:00
|
|
|
# Test for UNION injection and set the sample
|
|
|
|
# payload as well as the vector.
|
|
|
|
# NOTE: vector is set to a tuple with 6 elements,
|
2012-10-28 02:36:09 +04:00
|
|
|
# used afterwards by Agent.forgeUnionQuery()
|
2011-01-12 15:01:32 +03:00
|
|
|
# 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)
|
|
|
|
|
2011-04-20 03:04:10 +04:00
|
|
|
if not Backend.getIdentifiedDbms():
|
2013-02-01 14:24:17 +04:00
|
|
|
if kb.heuristicDbms in (None, UNKNOWN_DBMS):
|
2013-01-25 15:34:57 +04:00
|
|
|
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 using option '--dbms'"
|
|
|
|
singleTimeWarnMessage(warnMsg)
|
|
|
|
else:
|
|
|
|
Backend.forceDbms(kb.heuristicDbms)
|
2011-02-08 00:18:01 +03:00
|
|
|
|
2012-05-07 17:51:31 +04:00
|
|
|
if unionExtended:
|
|
|
|
infoMsg = "automatically extending ranges "
|
|
|
|
infoMsg += "for UNION query injection technique tests as "
|
2012-07-25 12:57:19 +04:00
|
|
|
infoMsg += "there is at least one other potential "
|
|
|
|
infoMsg += "injection technique found"
|
2012-05-07 17:51:31 +04:00
|
|
|
singleTimeLogMessage(infoMsg)
|
|
|
|
|
2011-01-19 02:03:50 +03:00
|
|
|
# Test for UNION query SQL injection
|
2011-01-16 04:17:09 +03:00
|
|
|
reqPayload, vector = unionTest(comment, place, parameter, value, prefix, suffix)
|
2011-01-12 01:18:47 +03:00
|
|
|
|
|
|
|
if isinstance(reqPayload, basestring):
|
|
|
|
infoMsg = "%s parameter '%s' is '%s' injectable" % (place, 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
|
2011-01-13 12:41:55 +03:00
|
|
|
where = vector[6]
|
2011-01-12 15:01:32 +03:00
|
|
|
|
2011-05-27 01:54:19 +04:00
|
|
|
kb.previousMethod = method
|
|
|
|
|
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
|
|
|
|
|
2011-02-06 18:20:44 +03:00
|
|
|
# Feed with test details every time a test is successful
|
|
|
|
if hasattr(test, "details"):
|
|
|
|
for dKey, dValue in test.details.items():
|
2011-06-02 02:47:54 +04:00
|
|
|
if dKey == "dbms":
|
2012-10-28 03:11:50 +04:00
|
|
|
injection.dbms = dValue
|
2011-06-02 02:47:54 +04:00
|
|
|
if not isinstance(dValue, list):
|
2012-10-28 03:11:50 +04:00
|
|
|
Backend.setDbms(dValue)
|
2011-06-02 02:47:54 +04:00
|
|
|
else:
|
2011-06-02 03:00:18 +04:00
|
|
|
Backend.forceDbms(dValue[0], True)
|
2012-07-24 17:43:29 +04:00
|
|
|
elif dKey == "dbms_version" and injection.dbms_version is None and not conf.testFilter:
|
2011-02-06 18:20:44 +03:00
|
|
|
injection.dbms_version = Backend.setVersion(dValue)
|
|
|
|
elif dKey == "os" and injection.os is None:
|
|
|
|
injection.os = Backend.setOs(dValue)
|
|
|
|
|
2011-01-12 03:47:39 +03:00
|
|
|
if vector is None and "vector" in test and test.vector is not None:
|
2012-09-26 13:27:43 +04:00
|
|
|
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
|
2011-01-27 22:44:24 +03:00
|
|
|
injection.data[stype].payload = agent.removePayloadDelimiters(reqPayload)
|
2010-12-18 13:42:09 +03:00
|
|
|
injection.data[stype].where = where
|
2011-01-12 03:47:39 +03:00
|
|
|
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
|
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
|
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
|
2011-07-25 15:05:49 +04:00
|
|
|
injection.conf.optimize = conf.optimize
|
2011-01-14 17:37:03 +03:00
|
|
|
|
2012-12-11 17:44:43 +04:00
|
|
|
if not kb.alerted:
|
|
|
|
if conf.beep:
|
|
|
|
beep()
|
2010-12-18 13:42:09 +03:00
|
|
|
|
2012-12-11 17:44:43 +04:00
|
|
|
if conf.alert:
|
|
|
|
infoMsg = "executing alerting shell command(s) ('%s')" % conf.alert
|
|
|
|
logger.info(infoMsg)
|
|
|
|
|
|
|
|
process = execute(conf.alert, shell=True)
|
|
|
|
process.wait()
|
2012-12-11 15:48:58 +04:00
|
|
|
|
2012-12-11 17:44:43 +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)
|
2010-12-01 01:40:25 +03:00
|
|
|
break
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2011-02-06 23:23:23 +03: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)
|
|
|
|
|
2013-02-01 20:24:04 +04:00
|
|
|
msg = "How do you want to proceed? [(S)kip current test/(e)nd detection phase/(n)ext parameter/(q)uit]"
|
|
|
|
choice = readInput(msg, default="S", checkBatch=False)
|
2011-06-27 18:14:49 +04:00
|
|
|
|
|
|
|
if choice[0] in ("s", "S"):
|
|
|
|
pass
|
|
|
|
elif choice[0] in ("n", "N"):
|
|
|
|
return None
|
|
|
|
elif choice[0] in ("e", "E"):
|
2011-06-08 18:44:11 +04:00
|
|
|
kb.endDetection = True
|
2011-06-27 18:14:49 +04:00
|
|
|
elif choice[0] in ("q", "Q"):
|
2012-12-06 17:14:19 +04:00
|
|
|
raise SqlmapUserQuitException
|
2010-12-04 18:47:02 +03:00
|
|
|
|
2011-02-06 23:23:23 +03:00
|
|
|
finally:
|
|
|
|
# Reset forced back-end DBMS value
|
|
|
|
Backend.flushForcedDbms()
|
|
|
|
|
2011-06-02 03:00:18 +04:00
|
|
|
Backend.flushForcedDbms(True)
|
|
|
|
|
2010-12-01 01:40:25 +03:00
|
|
|
# Return the injection object
|
2010-11-29 00:27:47 +03:00
|
|
|
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'):
|
2011-06-08 18:40:42 +04:00
|
|
|
warnMsg = "in OR boolean-based injections, 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)
|
|
|
|
|
2011-04-22 16:24:16 +04:00
|
|
|
injection = checkFalsePositives(injection)
|
2010-11-29 00:27:47 +03:00
|
|
|
else:
|
2012-10-25 15:21:32 +04:00
|
|
|
injection = None
|
|
|
|
|
|
|
|
if injection:
|
2013-01-16 05:31:03 +04:00
|
|
|
checkSuhosinPatch(injection)
|
2012-10-25 15:21:32 +04:00
|
|
|
|
|
|
|
return injection
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2013-01-25 15:34:57 +04:00
|
|
|
def heuristicCheckDbms(injection):
|
|
|
|
retVal = None
|
|
|
|
|
2013-02-01 14:24:17 +04:00
|
|
|
pushValue(kb.injection)
|
|
|
|
kb.injection = injection
|
|
|
|
randStr1, randStr2 = randomStr(), randomStr()
|
2013-01-25 15:34:57 +04:00
|
|
|
|
2013-02-01 14:24:17 +04:00
|
|
|
for dbms in getPublicTypeMembers(DBMS, True):
|
|
|
|
Backend.forceDbms(dbms)
|
2013-01-25 15:34:57 +04:00
|
|
|
|
2013-02-01 14:24:17 +04:00
|
|
|
if checkBooleanExpression("(SELECT '%s'%s)='%s'" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), randStr1)):
|
|
|
|
if not checkBooleanExpression("(SELECT '%s'%s)='%s'" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), randStr2)):
|
|
|
|
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:
|
2013-02-01 20:24:04 +04:00
|
|
|
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)
|
|
|
|
|
2013-01-25 15:34:57 +04:00
|
|
|
return retVal
|
|
|
|
|
2011-04-22 16:24:16 +04:00
|
|
|
def checkFalsePositives(injection):
|
|
|
|
"""
|
2011-05-27 00:48:18 +04:00
|
|
|
Checks for false positives (only in single special cases)
|
2011-04-22 16:24:16 +04:00
|
|
|
"""
|
|
|
|
|
|
|
|
retVal = injection
|
|
|
|
|
2011-10-10 21:29:54 +04:00
|
|
|
if len(injection.data) == 1 and any(map(lambda x: x in injection.data, [PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED]))\
|
2013-01-16 05:31:03 +04:00
|
|
|
or len(injection.data) == 2 and all(map(lambda x: x in injection.data, [PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED])):
|
|
|
|
# or len(injection.data) == 1 and 'Generic' in injection.data.values()[0].title and not Backend.getIdentifiedDbms():
|
2011-04-22 16:24:16 +04:00
|
|
|
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
|
2011-04-22 16:24:16 +04:00
|
|
|
logger.info(infoMsg)
|
|
|
|
|
2012-03-09 14:28:19 +04:00
|
|
|
def _():
|
|
|
|
return int(randomInt(2)) + 1
|
|
|
|
|
2011-04-22 16:24:16 +04:00
|
|
|
kb.injection = injection
|
|
|
|
|
2011-06-08 18:40:42 +04:00
|
|
|
# Simple arithmetic operations which should show basic
|
2011-05-27 00:48:18 +04:00
|
|
|
# arithmetic ability of the backend if it's really injectable
|
2012-10-16 12:24:05 +04:00
|
|
|
for i in xrange(1 + conf.level / 2):
|
|
|
|
randInt1, randInt2, randInt3 = (_() for j in xrange(3))
|
|
|
|
|
|
|
|
# Just in case (also, they have to be different than 0 because of the last test)
|
|
|
|
while randInt1 == randInt2:
|
|
|
|
randInt2 = _()
|
|
|
|
|
|
|
|
if not checkBooleanExpression("(%d+%d)=%d" % (randInt1, randInt2, randInt1 + randInt2)):
|
|
|
|
retVal = None
|
|
|
|
break
|
2013-03-01 13:59:04 +04:00
|
|
|
|
|
|
|
# Just in case if DBMS hasn't properly recovered from previous delayed request
|
|
|
|
if PAYLOAD.TECHNIQUE.BOOLEAN not in injection.data:
|
2013-03-02 01:51:34 +04:00
|
|
|
checkBooleanExpression("%d=%d" % (randInt1, randInt2))
|
2013-03-01 13:59:04 +04:00
|
|
|
|
|
|
|
if checkBooleanExpression("%d>(%d+%d)" % (min(randInt1, randInt2), randInt3, max(randInt1, randInt2))):
|
2012-10-16 12:24:05 +04:00
|
|
|
retVal = None
|
|
|
|
break
|
|
|
|
elif checkBooleanExpression("(%d+%d)>%d" % (randInt3, min(randInt1, randInt2), randInt1 + randInt2 + randInt3)):
|
|
|
|
retVal = None
|
|
|
|
break
|
|
|
|
elif not checkBooleanExpression("%d=(%d+%d)" % (randInt1 + randInt2, randInt1, randInt2)):
|
|
|
|
retVal = None
|
|
|
|
break
|
2011-04-22 16:24:16 +04:00
|
|
|
|
|
|
|
if retVal is None:
|
2012-03-09 18:21:41 +04:00
|
|
|
warnMsg = "false positive or unexploitable injection point detected"
|
2011-04-22 16:24:16 +04:00
|
|
|
logger.warn(warnMsg)
|
|
|
|
|
2013-02-22 14:12:41 +04:00
|
|
|
if PAYLOAD.TECHNIQUE.BOOLEAN in injection.data:
|
|
|
|
if all(_.__name__ != "between" for _ in kb.tamperFunctions):
|
|
|
|
warnMsg = "there is a possibility that the character '>' is "
|
|
|
|
warnMsg += "filtered by the back-end server. You can try "
|
|
|
|
warnMsg += "to rerun with '--tamper=between'"
|
|
|
|
logger.warn(warnMsg)
|
|
|
|
|
2011-04-22 16:24:16 +04:00
|
|
|
kb.injection = popValue()
|
|
|
|
|
|
|
|
return retVal
|
|
|
|
|
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:
|
|
|
|
pushValue(kb.injection)
|
|
|
|
|
|
|
|
kb.injection = injection
|
|
|
|
randInt = randomInt()
|
|
|
|
|
2013-01-16 05:31:03 +04:00
|
|
|
_ = " " * (SUHOSIN_MAX_VALUE_LENGTH / 2)
|
2012-10-25 15:35:44 +04:00
|
|
|
if not checkBooleanExpression("%d%s=%s%d" % (randInt, _, _, randInt)):
|
2012-10-25 15:21:32 +04:00
|
|
|
warnMsg = "parameter length constraint "
|
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()
|
|
|
|
|
2011-01-15 16:15:10 +03:00
|
|
|
def heuristicCheckSqlInjection(place, parameter):
|
2010-11-05 16:14:12 +03:00
|
|
|
if kb.nullConnection:
|
2011-01-19 02:03:50 +03:00
|
|
|
debugMsg = "heuristic checking skipped "
|
2010-11-05 16:14:12 +03:00
|
|
|
debugMsg += "because NULL connection used"
|
|
|
|
logger.debug(debugMsg)
|
2012-03-05 13:42:52 +04:00
|
|
|
return None
|
|
|
|
|
2013-01-29 23:53:11 +04:00
|
|
|
if wasLastResponseDBMSError():
|
2012-03-05 13:42:52 +04:00
|
|
|
debugMsg = "heuristic checking skipped "
|
|
|
|
debugMsg += "because original page content "
|
|
|
|
debugMsg += "contains DBMS error"
|
|
|
|
logger.debug(debugMsg)
|
|
|
|
return None
|
2010-11-05 16:14:12 +03:00
|
|
|
|
2012-08-22 17:51:47 +04:00
|
|
|
origValue = conf.paramDict[place][parameter]
|
|
|
|
|
2010-10-11 16:26:35 +04:00
|
|
|
prefix = ""
|
2010-11-18 01:00:09 +03:00
|
|
|
suffix = ""
|
2010-10-11 16:26:35 +04:00
|
|
|
|
2010-11-18 01:00:09 +03:00
|
|
|
if conf.prefix or conf.suffix:
|
2010-10-11 16:26:35 +04:00
|
|
|
if conf.prefix:
|
|
|
|
prefix = conf.prefix
|
|
|
|
|
2010-11-18 01:00:09 +03:00
|
|
|
if conf.suffix:
|
|
|
|
suffix = conf.suffix
|
2010-10-11 16:26:35 +04:00
|
|
|
|
2012-10-28 03:42:08 +04:00
|
|
|
randStr = ""
|
|
|
|
while '\'' not in randStr:
|
|
|
|
randStr = randomStr(length=10, alphabet=HEURISTIC_CHECK_ALPHABET)
|
|
|
|
|
|
|
|
payload = "%s%s%s" % (prefix, randStr, suffix)
|
2011-01-12 01:18:47 +03:00
|
|
|
payload = agent.payload(place, parameter, newValue=payload)
|
2011-11-22 12:39:13 +04:00
|
|
|
page, _ = Request.queryPage(payload, place, content=True, raise404=False)
|
|
|
|
|
|
|
|
parseFilePaths(page)
|
2013-01-29 23:53:11 +04:00
|
|
|
result = wasLastResponseDBMSError()
|
2010-10-16 19:10:48 +04:00
|
|
|
|
2013-02-01 20:24:04 +04:00
|
|
|
infoMsg = "heuristic (parsing) test shows that %s " % place
|
2010-11-16 13:52:49 +03:00
|
|
|
infoMsg += "parameter '%s' might " % parameter
|
2010-10-16 19:10:48 +04:00
|
|
|
|
2012-08-22 17:51:47 +04:00
|
|
|
def _(page):
|
|
|
|
return any(_ in (page or "") for _ in FORMAT_EXCEPTION_STRINGS)
|
2012-08-20 14:14:01 +04:00
|
|
|
|
2012-08-22 17:51:47 +04:00
|
|
|
casting = _(page) and not _(kb.originalPage)
|
2012-08-20 14:14:01 +04:00
|
|
|
|
2012-08-22 17:51:47 +04:00
|
|
|
if not casting and not result and kb.dynamicParameter and origValue.isdigit():
|
|
|
|
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
|
|
|
|
2012-08-22 17:51:47 +04:00
|
|
|
if not result:
|
|
|
|
randStr = randomStr()
|
|
|
|
payload = "%s%s%s" % (prefix, "%s%s" % (origValue, 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
|
|
|
|
|
2012-08-22 13:27:58 +04:00
|
|
|
if casting:
|
2012-08-22 17:53:40 +04:00
|
|
|
errMsg = "possible %s casting " % ("integer" if origValue.isdigit() else "type")
|
2012-08-22 13:27:58 +04:00
|
|
|
errMsg += "detected (e.g. %s=(int)$_REQUEST('%s')) " % (parameter, parameter)
|
|
|
|
errMsg += "at the back-end web application"
|
|
|
|
logger.error(errMsg)
|
|
|
|
|
2012-08-22 18:06:09 +04:00
|
|
|
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]")
|
2012-08-22 18:06:09 +04:00
|
|
|
kb.ignoreCasted = readInput(message, default='Y' if conf.multipleTargets else 'N').upper() != 'N'
|
2012-08-22 13:27:58 +04:00
|
|
|
|
2012-08-22 17:51:47 +04:00
|
|
|
elif result:
|
2013-02-05 13:02:11 +04:00
|
|
|
infoMsg += "be injectable"
|
|
|
|
if Backend.getErrorParsedDBMSes():
|
|
|
|
infoMsg += " (possible DBMS: '%s')" % Format.getErrorParsedDBMSes()
|
2012-08-22 17:51:47 +04:00
|
|
|
logger.info(infoMsg)
|
|
|
|
|
|
|
|
else:
|
|
|
|
infoMsg += "not be injectable"
|
|
|
|
logger.warn(infoMsg)
|
|
|
|
|
2012-08-22 13:56:30 +04:00
|
|
|
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
|
|
|
|
dynamic, the content of the page differs, otherwise the
|
|
|
|
dynamicity might depend on another parameter.
|
|
|
|
"""
|
|
|
|
|
2012-03-15 23:47:59 +04:00
|
|
|
if kb.redirectChoice:
|
|
|
|
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
|
|
|
|
2008-12-09 00:24:24 +03:00
|
|
|
infoMsg = "testing if %s parameter '%s' is dynamic" % (place, 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)
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2012-01-14 00:56:06 +04:00
|
|
|
if not dynResult:
|
|
|
|
infoMsg = "confirming that %s parameter '%s' is dynamic" % (place, parameter)
|
|
|
|
logger.info(infoMsg)
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2012-01-14 00:56:06 +04:00
|
|
|
randInt = randomInt()
|
|
|
|
payload = agent.payload(place, parameter, value, getUnicode(randInt))
|
|
|
|
dynResult = Request.queryPage(payload, place, raise404=False)
|
2012-12-06 17:14:19 +04:00
|
|
|
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
|
|
|
|
2010-10-25 14:41:37 +04:00
|
|
|
def checkDynamicContent(firstPage, secondPage):
|
2010-09-14 01:01:46 +04:00
|
|
|
"""
|
2010-12-29 22:39:32 +03:00
|
|
|
This function checks for the dynamic content in the provided pages
|
2010-09-14 01:01:46 +04:00
|
|
|
"""
|
2010-11-04 12:18:32 +03:00
|
|
|
|
2010-11-04 00:51:36 +03:00
|
|
|
if kb.nullConnection:
|
2011-01-19 02:03:50 +03:00
|
|
|
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
|
2011-01-26 01:26:28 +03:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2011-01-16 13:52:42 +03:00
|
|
|
seqMatcher = getCurrentThreadData().seqMatcher
|
|
|
|
seqMatcher.set_seq1(firstPage)
|
|
|
|
seqMatcher.set_seq2(secondPage)
|
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
|
2011-01-16 13:52:42 +03:00
|
|
|
if seqMatcher.quick_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-10-25 14:41:37 +04:00
|
|
|
|
2010-12-29 22:39:32 +03:00
|
|
|
if count > conf.retries:
|
2011-01-03 14:06:49 +03:00
|
|
|
warnMsg = "target url is too dynamic. "
|
2012-03-18 21:27:08 +04:00
|
|
|
warnMsg += "Switching to '--text-only' "
|
2011-01-03 14:06:49 +03:00
|
|
|
logger.warn(warnMsg)
|
|
|
|
|
|
|
|
conf.textOnly = True
|
|
|
|
return
|
2010-10-25 23:45:53 +04:00
|
|
|
|
2010-12-29 22:39:32 +03:00
|
|
|
warnMsg = "target url is heavily dynamic"
|
2012-10-04 20:28:36 +04:00
|
|
|
warnMsg += ". sqlmap is going to retry the request"
|
2010-12-29 22:39:32 +03:00
|
|
|
logger.critical(warnMsg)
|
2010-10-25 23:45:53 +04:00
|
|
|
|
2010-12-29 22:39:32 +03:00
|
|
|
secondPage, _ = Request.queryPage(content=True)
|
|
|
|
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).
|
|
|
|
"""
|
|
|
|
|
2008-12-09 00:24:24 +03:00
|
|
|
infoMsg = "testing if the url is stable, wait a few seconds"
|
|
|
|
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()
|
2008-12-21 19:35:03 +03:00
|
|
|
time.sleep(1)
|
2011-11-29 20:59:06 +04:00
|
|
|
secondPage, _ = Request.queryPage(content=True, raise404=False)
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2012-03-18 21:27:08 +04:00
|
|
|
if kb.redirectChoice:
|
|
|
|
return None
|
|
|
|
|
2010-10-25 17:52:21 +04:00
|
|
|
kb.pageStable = (firstPage == secondPage)
|
2008-12-18 23:48:23 +03:00
|
|
|
|
2010-10-25 17:52:21 +04:00
|
|
|
if kb.pageStable:
|
2010-02-10 12:27:34 +03:00
|
|
|
if firstPage:
|
2011-04-30 19:29:59 +04:00
|
|
|
infoMsg = "url is stable"
|
|
|
|
logger.info(infoMsg)
|
2010-02-10 12:27:34 +03:00
|
|
|
else:
|
2011-01-19 02:03:50 +03:00
|
|
|
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 "
|
2010-03-16 15:14:02 +03:00
|
|
|
errMsg += "page request results (and probable errors) by "
|
|
|
|
errMsg += "using higher verbosity levels"
|
2011-08-05 14:55:21 +04:00
|
|
|
logger.error(errMsg)
|
2009-02-09 13:28:03 +03:00
|
|
|
|
2010-10-25 17:52:21 +04:00
|
|
|
else:
|
2011-01-19 02:03:50 +03:00
|
|
|
warnMsg = "url is not stable, 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 "
|
|
|
|
warnMsg += "'Page comparison' and provide a string or regular "
|
|
|
|
warnMsg += "expression to match on"
|
2008-12-20 04:54:08 +03:00
|
|
|
logger.warn(warnMsg)
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2011-01-16 22:29:06 +03:00
|
|
|
message = "how do you want to proceed? [(C)ontinue/(s)tring/(r)egex/(q)uit] "
|
2012-08-20 13:40:49 +04:00
|
|
|
test = readInput(message, default="C")
|
2010-10-16 19:10:48 +04:00
|
|
|
|
2010-10-12 19:49:04 +04:00
|
|
|
if test and test[0] in ("q", "Q"):
|
2012-12-06 17:14:19 +04:00
|
|
|
raise SqlmapUserQuitException
|
2010-10-16 19:10:48 +04:00
|
|
|
|
2010-10-12 19:49:04 +04:00
|
|
|
elif test and test[0] in ("s", "S"):
|
|
|
|
showStaticWords(firstPage, secondPage)
|
2010-10-16 19:10:48 +04:00
|
|
|
|
2010-10-12 19:49:04 +04:00
|
|
|
message = "please enter value for parameter 'string': "
|
|
|
|
test = readInput(message)
|
2010-10-16 19:10:48 +04:00
|
|
|
|
2010-10-12 19:49:04 +04:00
|
|
|
if test:
|
|
|
|
conf.string = test
|
2010-11-04 12:18:32 +03:00
|
|
|
|
|
|
|
if kb.nullConnection:
|
2011-01-19 02:03:50 +03:00
|
|
|
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
|
2010-10-12 19:49:04 +04:00
|
|
|
else:
|
2010-11-10 22:44:51 +03:00
|
|
|
errMsg = "Empty value supplied"
|
2013-01-04 02:20:55 +04:00
|
|
|
raise SqlmapNoneDataException(errMsg)
|
2010-10-16 19:10:48 +04:00
|
|
|
|
2010-10-12 19:49:04 +04:00
|
|
|
elif test and test[0] in ("r", "R"):
|
|
|
|
message = "please enter value for parameter 'regex': "
|
|
|
|
test = readInput(message)
|
2010-10-16 19:10:48 +04:00
|
|
|
|
2010-10-12 19:49:04 +04:00
|
|
|
if test:
|
|
|
|
conf.regex = test
|
2010-11-04 12:18:32 +03:00
|
|
|
|
|
|
|
if kb.nullConnection:
|
2011-01-19 02:03:50 +03:00
|
|
|
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
|
2010-10-12 19:49:04 +04:00
|
|
|
else:
|
2010-11-10 22:44:51 +03:00
|
|
|
errMsg = "Empty value supplied"
|
2013-01-04 02:20:55 +04:00
|
|
|
raise SqlmapNoneDataException(errMsg)
|
2011-01-06 11:54:50 +03:00
|
|
|
|
2010-10-12 19:49:04 +04: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
|
|
|
|
|
2011-01-19 02:03:50 +03:00
|
|
|
infoMsg = "testing if the provided string is within the "
|
2008-12-09 00:24:24 +03:00
|
|
|
infoMsg += "target URL page content"
|
|
|
|
logger.info(infoMsg)
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2011-08-12 19:33:37 +04: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
|
|
|
|
2011-08-12 19:33:37 +04:00
|
|
|
if conf.string not in rawResponse:
|
2011-01-19 02:03:50 +03:00
|
|
|
warnMsg = "you provided '%s' as the string to " % conf.string
|
2010-11-18 01:00:09 +03:00
|
|
|
warnMsg += "match, but such a string is not within the target "
|
2011-08-12 19:33:37 +04:00
|
|
|
warnMsg += "URL raw response, sqlmap will carry on anyway"
|
2010-11-18 01:00:09 +03:00
|
|
|
logger.warn(warnMsg)
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2010-11-18 01:00:09 +03:00
|
|
|
return True
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2008-12-12 22:06:31 +03:00
|
|
|
def checkRegexp():
|
|
|
|
if not conf.regexp:
|
|
|
|
return True
|
|
|
|
|
2011-01-19 02:03:50 +03:00
|
|
|
infoMsg = "testing if the provided regular expression matches within "
|
2008-12-12 22:06:31 +03:00
|
|
|
infoMsg += "the target URL page content"
|
|
|
|
logger.info(infoMsg)
|
|
|
|
|
2011-08-12 19:33:37 +04:00
|
|
|
page, headers = Request.queryPage(content=True)
|
|
|
|
rawResponse = "%s%s" % (listToStrValue(headers.headers if headers else ""), page)
|
2008-12-12 22:06:31 +03:00
|
|
|
|
2011-08-12 19:33:37 +04:00
|
|
|
if not re.search(conf.regexp, rawResponse, re.I | re.M):
|
2011-01-19 02:03:50 +03:00
|
|
|
warnMsg = "you provided '%s' as the regular expression to " % conf.regexp
|
2010-11-18 01:00:09 +03:00
|
|
|
warnMsg += "match, but such a regular expression does not have any "
|
2011-08-12 19:33:37 +04:00
|
|
|
warnMsg += "match within the target URL raw response, sqlmap "
|
|
|
|
warnMsg += "will carry on anyway"
|
2010-11-18 01:00:09 +03:00
|
|
|
logger.warn(warnMsg)
|
2008-12-12 22:06:31 +03:00
|
|
|
|
2010-11-18 01:00:09 +03:00
|
|
|
return True
|
2008-12-12 22:06:31 +03:00
|
|
|
|
2011-07-06 09:44:47 +04:00
|
|
|
def checkWaf():
|
|
|
|
"""
|
|
|
|
Reference: http://seclists.org/nmap-dev/2011/q2/att-1005/http-waf-detect.nse
|
|
|
|
"""
|
|
|
|
|
|
|
|
if not conf.checkWaf:
|
|
|
|
return False
|
|
|
|
|
2013-02-21 17:48:19 +04:00
|
|
|
infoMsg = "heuristic checking if the target is protected by "
|
2011-07-06 09:44:47 +04:00
|
|
|
infoMsg += "some kind of WAF/IPS/IDS"
|
|
|
|
logger.info(infoMsg)
|
|
|
|
|
|
|
|
retVal = False
|
|
|
|
|
|
|
|
backup = dict(conf.parameters)
|
|
|
|
|
|
|
|
conf.parameters = dict(backup)
|
|
|
|
conf.parameters[PLACE.GET] = "" if not conf.parameters.get(PLACE.GET) else conf.parameters[PLACE.GET] + "&"
|
|
|
|
conf.parameters[PLACE.GET] += "%s=%d %s" % (randomStr(), randomInt(), IDS_WAF_CHECK_PAYLOAD)
|
|
|
|
|
|
|
|
kb.matchRatio = None
|
2012-02-22 14:40:11 +04:00
|
|
|
Request.queryPage()
|
2011-07-06 09:44:47 +04:00
|
|
|
|
|
|
|
if kb.errorIsNone and kb.matchRatio is None:
|
|
|
|
kb.matchRatio = LOWER_RATIO_BOUND
|
|
|
|
|
|
|
|
conf.parameters = dict(backup)
|
|
|
|
conf.parameters[PLACE.GET] = "" if not conf.parameters.get(PLACE.GET) else conf.parameters[PLACE.GET] + "&"
|
|
|
|
conf.parameters[PLACE.GET] += "%s=%d" % (randomStr(), randomInt())
|
|
|
|
|
|
|
|
trueResult = Request.queryPage()
|
|
|
|
|
|
|
|
if trueResult:
|
|
|
|
conf.parameters = dict(backup)
|
|
|
|
conf.parameters[PLACE.GET] = "" if not conf.parameters.get(PLACE.GET) else conf.parameters[PLACE.GET] + "&"
|
|
|
|
conf.parameters[PLACE.GET] += "%s=%d %s" % (randomStr(), randomInt(), IDS_WAF_CHECK_PAYLOAD)
|
|
|
|
|
|
|
|
falseResult = Request.queryPage()
|
|
|
|
|
|
|
|
if not falseResult:
|
|
|
|
retVal = True
|
|
|
|
|
|
|
|
conf.parameters = dict(backup)
|
|
|
|
|
|
|
|
if retVal:
|
2011-07-07 00:41:13 +04:00
|
|
|
warnMsg = "it appears that the target is protected. Please "
|
2012-02-01 18:49:42 +04:00
|
|
|
warnMsg += "consider usage of tamper scripts (option '--tamper')"
|
2011-07-06 09:44:47 +04:00
|
|
|
logger.warn(warnMsg)
|
|
|
|
else:
|
|
|
|
infoMsg = "it appears that the target is not protected"
|
|
|
|
logger.info(infoMsg)
|
|
|
|
|
|
|
|
return retVal
|
|
|
|
|
2013-02-21 14:14:57 +04:00
|
|
|
def identifyWaf():
|
|
|
|
if not conf.identifyWaf:
|
|
|
|
return None
|
|
|
|
|
2013-02-26 14:08:06 +04:00
|
|
|
kb.testMode = True
|
|
|
|
|
2013-02-21 14:14:57 +04:00
|
|
|
infoMsg = "using WAF scripts to detect "
|
|
|
|
infoMsg += "backend WAF/IPS/IDS protection"
|
|
|
|
logger.info(infoMsg)
|
|
|
|
|
2013-02-21 17:33:12 +04:00
|
|
|
@cachedmethod
|
|
|
|
def _(*args, **kwargs):
|
2013-03-19 22:06:51 +04:00
|
|
|
page, headers, code = None, None, None
|
2013-02-21 17:33:12 +04:00
|
|
|
try:
|
|
|
|
if kwargs.get("get"):
|
|
|
|
kwargs["get"] = urlencode(kwargs["get"])
|
|
|
|
kwargs["raise404"] = False
|
2013-03-19 22:06:51 +04:00
|
|
|
page, headers, code = Request.getPage(*args, **kwargs)
|
|
|
|
except Exception:
|
|
|
|
pass
|
|
|
|
return page or "", headers or {}, code
|
2013-02-21 17:33:12 +04:00
|
|
|
|
2013-02-21 14:14:57 +04:00
|
|
|
retVal = False
|
|
|
|
|
2013-02-21 17:39:22 +04:00
|
|
|
for function, product in kb.wafFunctions:
|
|
|
|
try:
|
2013-02-21 17:52:56 +04:00
|
|
|
logger.debug("checking for WAF/IDS/IPS product '%s'" % product)
|
2013-02-21 17:33:12 +04:00
|
|
|
found = function(_)
|
2013-02-21 17:39:22 +04:00
|
|
|
except Exception, ex:
|
|
|
|
errMsg = "exception occured while running "
|
|
|
|
errMsg += "WAF script for '%s' ('%s')" % (product, ex)
|
|
|
|
logger.critical(errMsg)
|
|
|
|
|
|
|
|
found = False
|
2013-02-21 17:33:12 +04:00
|
|
|
|
2013-02-21 14:14:57 +04:00
|
|
|
if found:
|
|
|
|
retVal = product
|
|
|
|
break
|
|
|
|
|
|
|
|
if retVal:
|
2013-02-26 14:16:09 +04:00
|
|
|
errMsg = "WAF/IDS/IPS identified '%s'. Please " % retVal
|
2013-02-21 17:39:22 +04:00
|
|
|
errMsg += "consider usage of tamper scripts (option '--tamper')"
|
|
|
|
logger.critical(errMsg)
|
2013-02-21 14:14:57 +04:00
|
|
|
else:
|
2013-02-22 15:15:38 +04:00
|
|
|
infoMsg = "no WAF/IDS/IPS product has been identified"
|
|
|
|
logger.info(infoMsg)
|
2013-02-21 14:14:57 +04:00
|
|
|
|
2013-02-26 14:08:06 +04:00
|
|
|
kb.testMode = False
|
|
|
|
|
2013-02-21 14:14:57 +04:00
|
|
|
return retVal
|
|
|
|
|
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
|
|
|
|
|
2010-09-16 12:43:10 +04:00
|
|
|
infoMsg = "testing NULL connection to the target url"
|
|
|
|
logger.info(infoMsg)
|
|
|
|
|
|
|
|
try:
|
2011-08-12 20:48:11 +04:00
|
|
|
page, headers, _ = Request.getPage(method=HTTPMETHOD.HEAD)
|
2010-12-06 18:50:19 +03:00
|
|
|
|
2013-02-18 14:38:01 +04:00
|
|
|
if not page and HTTPHEADER.CONTENT_LENGTH in (headers or {}):
|
2010-11-08 12:49:57 +03:00
|
|
|
kb.nullConnection = NULLCONNECTION.HEAD
|
2010-10-15 14:24:54 +04:00
|
|
|
|
2010-10-15 16:46:41 +04:00
|
|
|
infoMsg = "NULL connection is supported with HEAD header"
|
2010-10-15 14:24:54 +04:00
|
|
|
logger.info(infoMsg)
|
2010-09-16 12:43:10 +04:00
|
|
|
else:
|
2011-08-12 20:48:11 +04:00
|
|
|
page, headers, _ = Request.getPage(auxHeaders={HTTPHEADER.RANGE: "bytes=-1"})
|
2010-12-06 18:50:19 +03:00
|
|
|
|
2013-02-18 14:38:01 +04:00
|
|
|
if page and len(page) == 1 and HTTPHEADER.CONTENT_RANGE in (headers or {}):
|
2010-11-08 12:49:57 +03:00
|
|
|
kb.nullConnection = NULLCONNECTION.RANGE
|
2010-09-16 12:43:10 +04:00
|
|
|
|
2010-10-15 16:46:41 +04:00
|
|
|
infoMsg = "NULL connection is supported with GET header "
|
2010-10-15 14:24:54 +04:00
|
|
|
infoMsg += "'%s'" % kb.nullConnection
|
|
|
|
logger.info(infoMsg)
|
2010-12-06 18:50:19 +03:00
|
|
|
|
2012-12-06 17:14:19 +04:00
|
|
|
except SqlmapConnectionException, errMsg:
|
2010-09-16 12:43:10 +04:00
|
|
|
errMsg = getUnicode(errMsg)
|
2013-01-04 02:20:55 +04:00
|
|
|
raise SqlmapConnectionException(errMsg)
|
2010-09-16 12:43:10 +04:00
|
|
|
|
|
|
|
return kb.nullConnection is not None
|
|
|
|
|
2010-11-15 15:19:22 +03:00
|
|
|
def checkConnection(suppressOutput=False):
|
2013-02-28 23:20:08 +04:00
|
|
|
if not any((conf.proxy, conf.tor, conf.dummy)):
|
2011-06-24 21:19:24 +04:00
|
|
|
try:
|
|
|
|
socket.getaddrinfo(conf.hostname, None)
|
|
|
|
except socket.gaierror:
|
|
|
|
errMsg = "host '%s' does not exist" % conf.hostname
|
2013-01-04 02:20:55 +04:00
|
|
|
raise SqlmapConnectionException(errMsg)
|
2010-05-21 17:36:49 +04:00
|
|
|
|
2013-02-28 23:28:34 +04:00
|
|
|
if not suppressOutput and not conf.dummy:
|
2010-11-15 15:19:22 +03:00
|
|
|
infoMsg = "testing connection to the target url"
|
|
|
|
logger.info(infoMsg)
|
2008-10-15 19:38:22 +04:00
|
|
|
|
|
|
|
try:
|
2011-04-19 12:55:38 +04:00
|
|
|
page, _ = Request.queryPage(content=True, noteResponseTime=False)
|
2010-12-06 21:20:57 +03:00
|
|
|
kb.originalPage = kb.pageTemplate = page
|
2011-01-01 23:19:55 +03:00
|
|
|
|
|
|
|
kb.errorIsNone = False
|
2011-01-12 01:18:47 +03:00
|
|
|
|
2013-01-29 23:53:11 +04:00
|
|
|
if not kb.originalPage and wasLastResponseHTTPError():
|
2011-01-26 01:26:28 +03:00
|
|
|
errMsg = "unable to retrieve page content"
|
2013-01-04 02:20:55 +04:00
|
|
|
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 "
|
2011-01-12 01:18:47 +03:00
|
|
|
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():
|
2012-01-14 00:56:06 +04:00
|
|
|
warnMsg = "the web server responded with an HTTP error code (%d) " % getLastRequestHTTPError()
|
2011-01-12 01:18:47 +03:00
|
|
|
warnMsg += "which could interfere with the results of the tests"
|
2011-01-01 23:19:55 +03:00
|
|
|
logger.warn(warnMsg)
|
|
|
|
else:
|
|
|
|
kb.errorIsNone = True
|
2012-05-25 12:30:24 +04:00
|
|
|
|
2012-12-06 17:14:19 +04:00
|
|
|
except SqlmapConnectionException, errMsg:
|
2010-06-02 16:45:40 +04:00
|
|
|
errMsg = getUnicode(errMsg)
|
2011-11-21 03:11:18 +04:00
|
|
|
logger.critical(errMsg)
|
|
|
|
|
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 "
|
|
|
|
warnMsg += "(e.g. 'ping6 %s') " % conf.hostname
|
|
|
|
warnMsg += "prior to running sqlmap to avoid "
|
|
|
|
warnMsg += "any addressing issues"
|
|
|
|
singleTimeWarnMessage(warnMsg)
|
|
|
|
|
2011-12-16 03:33:44 +04:00
|
|
|
if any(code in kb.httpErrorCodes for code in (httplib.NOT_FOUND, )):
|
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] "
|
|
|
|
if readInput(msg, default="Y") not in ("n", "N"):
|
2012-12-06 17:14:19 +04:00
|
|
|
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
|
2008-10-15 19:38:22 +04:00
|
|
|
|
|
|
|
return True
|