diff --git a/lib/controller/checks.py b/lib/controller/checks.py index c3855842e..9cebc61fd 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1037,6 +1037,37 @@ def checkWaf(): return retVal +def identifyWaf(): + if not conf.identifyWaf: + return None + + infoMsg = "using WAF scripts to detect " + infoMsg += "backend WAF/IPS/IDS protection" + logger.info(infoMsg) + + retVal = False + page, headers, code = Request.getPage() + + for function, product, request in kb.wafFunctions: + found = False + if not request: + found = function(page or "", headers or {}, code) + else: + pass + if found: + retVal = product + break + + if retVal: + warnMsg = "WAF/IDS/IPS identified ('%s'). Please " % retVal + warnMsg += "consider usage of tamper scripts (option '--tamper')" + logger.critical(warnMsg) + else: + warnMsg = "no WAF/IDS/IPS were identified" + logger.warn(warnMsg) + + return retVal + def checkNullConnection(): """ Reference: http://www.wisec.it/sectou.php?id=472f952d79293 diff --git a/lib/controller/controller.py b/lib/controller/controller.py index c6c20282e..f21a243db 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -18,6 +18,7 @@ from lib.controller.checks import checkConnection from lib.controller.checks import checkNullConnection from lib.controller.checks import checkWaf from lib.controller.checks import heuristicCheckSqlInjection +from lib.controller.checks import identifyWaf from lib.core.agent import agent from lib.core.common import extractRegexResult from lib.core.common import getFilteredPageContent @@ -360,6 +361,9 @@ def start(): if conf.checkWaf: checkWaf() + if conf.identifyWaf: + identifyWaf() + if conf.nullConnection: checkNullConnection() diff --git a/lib/core/common.py b/lib/core/common.py index 53a2b5866..f4c926b30 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -973,6 +973,7 @@ def setPaths(): paths.SQLMAP_PROCS_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "procs") paths.SQLMAP_SHELL_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "shell") paths.SQLMAP_TAMPER_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "tamper") + paths.SQLMAP_WAF_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "waf") paths.SQLMAP_TXT_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "txt") paths.SQLMAP_UDF_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "udf") paths.SQLMAP_XML_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "xml") diff --git a/lib/core/enums.py b/lib/core/enums.py index 60e3bfd9c..d24fcee6c 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -152,6 +152,11 @@ class HTTPHEADER: REFERER = "Referer" USER_AGENT = "User-Agent" +class WAF_REQUEST: + GET = 1 + POST = 2 + HEADERS = 3 + class EXPECTED: BOOL = "bool" INT = "int" diff --git a/lib/core/option.py b/lib/core/option.py index c5bbc0090..5db03a850 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -6,6 +6,7 @@ See the file 'doc/COPYING' for copying permission """ import cookielib +import glob import inspect import logging import os @@ -894,6 +895,35 @@ def _setTamperingFunctions(): for _, function in priorities: kb.tamperFunctions.append(function) +def _setWafFunctions(): + """ + Loads WAF/IDS/IPS detecting functions from script(s) + """ + + if conf.identifyWaf: + for found in glob.glob(os.path.join(paths.SQLMAP_WAF_PATH, "*.py")): + dirname, filename = os.path.split(found) + dirname = os.path.abspath(dirname) + + debugMsg = "loading WAF script '%s'" % filename[:-3] + logger.debug(debugMsg) + + if dirname not in sys.path: + sys.path.insert(0, dirname) + + try: + module = __import__(filename[:-3]) + except ImportError, msg: + raise SqlmapSyntaxException("cannot import WAF script '%s' (%s)" % (filename[:-3], msg)) + + _ = dict(inspect.getmembers(module)) + if "detect" not in _: + errMsg = "missing function 'detect(page, headers, code)' " + errMsg += "in WAF script '%s'" % found + raise SqlmapGenericException(errMsg) + else: + kb.wafFunctions.append((_["detect"], _.get("__product__", filename[:-3]), _.get("__request__"))) + def _setThreads(): if not isinstance(conf.threads, int) or conf.threads <= 0: conf.threads = 1 @@ -1611,6 +1641,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.userAgents = None kb.vainRun = True kb.vulnHosts = set() + kb.wafFunctions = [] kb.wordlists = None def _useWizardInterface(): @@ -2080,6 +2111,7 @@ def init(): _adjustLoggingFormatter() _setMultipleTargets() _setTamperingFunctions() + _setWafFunctions() _setTrafficOutputFP() _resolveCrossReferences() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 36a516f26..88b17aa5c 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -214,6 +214,7 @@ optDict = { "profile": "boolean", "cpuThrottle": "integer", "forceDns": "boolean", + "identifyWaf": "boolean", "smokeTest": "boolean", "liveTest": "boolean", "stopFail": "boolean", diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 432c6bd63..ad578d73b 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -674,6 +674,9 @@ def cmdLineParser(): parser.add_option("--force-dns", dest="forceDns", action="store_true", help=SUPPRESS_HELP) + parser.add_option("--identify-waf", dest="identifyWaf", action="store_true", + help=SUPPRESS_HELP) + parser.add_option("--smoke-test", dest="smokeTest", action="store_true", help=SUPPRESS_HELP) diff --git a/waf/f5asm.py b/waf/f5asm.py new file mode 100644 index 000000000..8aeaf19f7 --- /dev/null +++ b/waf/f5asm.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.enums import HTTPHEADER + +__product__ = "F5 Networks BIG-IP Application Security Manager (ASM)" +__request__ = () + +def detect(page, headers, code): + return re.search(r"^TS[a-zA-Z0-9]{3,6}=", headers.get(HTTPHEADER.SET_COOKIE, "")) is not None