From 8e49872d7c63d4242d0eea51dad491b883f3ed7f Mon Sep 17 00:00:00 2001 From: stamparm Date: Thu, 21 Feb 2013 14:33:12 +0100 Subject: [PATCH] Finalizing implementation for an Issue #290 --- lib/controller/checks.py | 19 ++++++++++++++++--- lib/core/enums.py | 6 +----- lib/core/option.py | 3 +++ lib/core/settings.py | 10 +++++++++- waf/__init__.py | 8 ++++++++ waf/airlock.py | 16 ++++++++++++++++ waf/barracuda.py | 16 ++++++++++++++++ waf/bigip.py | 26 ++++++++++++++++++++++++++ waf/binarysec.py | 16 ++++++++++++++++ waf/datapower.py | 14 ++++++++++++++ waf/denyall.py | 26 ++++++++++++++++++++++++++ waf/dotdefender.py | 21 +++++++++++++++++++++ waf/f5asm.py | 16 ---------------- waf/hyperguard.py | 16 ++++++++++++++++ waf/modsecurity.py | 24 ++++++++++++++++++++++++ waf/netcontinuum.py | 16 ++++++++++++++++ waf/netscaler.py | 26 ++++++++++++++++++++++++++ waf/profense.py | 16 ++++++++++++++++ waf/proventia.py | 21 +++++++++++++++++++++ waf/teros.py | 16 ++++++++++++++++ waf/trafficshield.py | 16 ++++++++++++++++ waf/webappsecure.py | 21 +++++++++++++++++++++ 22 files changed, 344 insertions(+), 25 deletions(-) create mode 100644 waf/__init__.py create mode 100644 waf/airlock.py create mode 100644 waf/barracuda.py create mode 100644 waf/bigip.py create mode 100644 waf/binarysec.py create mode 100644 waf/datapower.py create mode 100644 waf/denyall.py create mode 100644 waf/dotdefender.py delete mode 100644 waf/f5asm.py create mode 100644 waf/hyperguard.py create mode 100644 waf/modsecurity.py create mode 100644 waf/netcontinuum.py create mode 100644 waf/netscaler.py create mode 100644 waf/profense.py create mode 100644 waf/proventia.py create mode 100644 waf/teros.py create mode 100644 waf/trafficshield.py create mode 100644 waf/webappsecure.py diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 9cebc61fd..2c38ece1b 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -36,6 +36,7 @@ from lib.core.common import readInput from lib.core.common import showStaticWords from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage +from lib.core.common import urlencode from lib.core.common import wasLastResponseDBMSError from lib.core.common import wasLastResponseHTTPError from lib.core.data import conf @@ -43,6 +44,7 @@ from lib.core.data import kb from lib.core.data import logger from lib.core.datatype import AttribDict from lib.core.datatype import InjectionDict +from lib.core.decorators import cachedmethod from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.enums import DBMS from lib.core.enums import HEURISTIC_TEST @@ -1045,15 +1047,26 @@ def identifyWaf(): infoMsg += "backend WAF/IPS/IDS protection" logger.info(infoMsg) + @cachedmethod + def _(*args, **kwargs): + try: + if kwargs.get("get"): + kwargs["get"] = urlencode(kwargs["get"]) + kwargs["raise404"] = False + return Request.getPage(*args, **kwargs) + except Exception, ex: + return None, None, None + 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) + found = function(_) else: pass + if found: retVal = product break @@ -1063,7 +1076,7 @@ def identifyWaf(): warnMsg += "consider usage of tamper scripts (option '--tamper')" logger.critical(warnMsg) else: - warnMsg = "no WAF/IDS/IPS were identified" + warnMsg = "WAF/IDS/IPS product not identified" logger.warn(warnMsg) return retVal diff --git a/lib/core/enums.py b/lib/core/enums.py index d24fcee6c..1247735d8 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -150,13 +150,9 @@ class HTTPHEADER: PROXY_CONNECTION = "Proxy-Connection" RANGE = "Range" REFERER = "Referer" + SERVER = "Server" 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 5db03a850..4cd87bf3b 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -905,6 +905,9 @@ def _setWafFunctions(): dirname, filename = os.path.split(found) dirname = os.path.abspath(dirname) + if filename == "__init__.py": + continue + debugMsg = "loading WAF script '%s'" % filename[:-3] logger.debug(debugMsg) diff --git a/lib/core/settings.py b/lib/core/settings.py index 04bc42b5b..6b01a4b0c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -380,7 +380,15 @@ BRUTE_TABLE_EXISTS_TEMPLATE = "EXISTS(SELECT %d FROM %s)" BRUTE_COLUMN_EXISTS_TEMPLATE = "EXISTS(SELECT %s FROM %s)" # Payload used for checking of existence of IDS/WAF (dummier the better) -IDS_WAF_CHECK_PAYLOAD = "AND 1=1 UNION ALL SELECT 1,2,3,table_name FROM information_schema.tables" +IDS_WAF_CHECK_PAYLOAD = "AND 1=1 UNION ALL SELECT 1,2,3,table_name FROM information_schema.tables WHERE 2>1" + +# Vectors used for provoking specific WAF/IDS/IPS behavior(s) +WAF_ATTACK_VECTORS = ( + "search=", + "file=../../../../etc/passwd", + "q=foobar", + "id=1 %s" % IDS_WAF_CHECK_PAYLOAD + ) # Used for status representation in dictionary attack phase ROTATING_CHARS = ('\\', '|', '|', '/', '-') diff --git a/waf/__init__.py b/waf/__init__.py new file mode 100644 index 000000000..9e1072a9c --- /dev/null +++ b/waf/__init__.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +pass diff --git a/waf/airlock.py b/waf/airlock.py new file mode 100644 index 000000000..5e1fe68b4 --- /dev/null +++ b/waf/airlock.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__ = "Airlock (Phion/Ergon)" + +def detect(get_page): + page, headers, code = get_page() + return re.search(r"\AAL[_-]?(SESS|LB)=", headers.get(HTTPHEADER.SET_COOKIE, ""), re.I) is not None diff --git a/waf/barracuda.py b/waf/barracuda.py new file mode 100644 index 000000000..9a44346e9 --- /dev/null +++ b/waf/barracuda.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__ = "Barracuda Web Application Firewall (Barracuda Networks)" + +def detect(get_page): + page, headers, code = get_page() + return re.search(r"\Abarra_counter_session=", headers.get(HTTPHEADER.SET_COOKIE, ""), re.I) is not None diff --git a/waf/bigip.py b/waf/bigip.py new file mode 100644 index 000000000..322a234c9 --- /dev/null +++ b/waf/bigip.py @@ -0,0 +1,26 @@ +#!/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 +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "BIG-IP Application Security Manager (F5 Networks)" + +def detect(get_page): + page, headers, code = get_page() + retval = re.search(r"\ATS[a-zA-Z0-9]{3,6}=", headers.get(HTTPHEADER.SET_COOKIE, ""), re.I) is not None + + if not retval: + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = headers.get("X-Cnection", "").lower() == "close" + if retval: + break + + return retval diff --git a/waf/binarysec.py b/waf/binarysec.py new file mode 100644 index 000000000..a870d5192 --- /dev/null +++ b/waf/binarysec.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__ = "BinarySEC Web Application Firewall (BinarySEC)" + +def detect(get_page): + page, headers, code = get_page() + return re.search(r"BinarySec", headers.get(HTTPHEADER.SERVER, ""), re.I) is not None diff --git a/waf/datapower.py b/waf/datapower.py new file mode 100644 index 000000000..aea59b01c --- /dev/null +++ b/waf/datapower.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +__product__ = "IBM WebSphere DataPower (IBM)" + +def detect(get_page): + page, headers, code = get_page() + return re.search(r"\A(OK|FAIL)", headers.get("X-Backside-Transport", ""), re.I) is not None diff --git a/waf/denyall.py b/waf/denyall.py new file mode 100644 index 000000000..ebc7ebec0 --- /dev/null +++ b/waf/denyall.py @@ -0,0 +1,26 @@ +#!/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 +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Deny All Web Application Firewall (DenyAll)" + +def detect(get_page): + page, headers, code = get_page() + retval = re.search(r"\Asessioncookie=", headers.get(HTTPHEADER.SET_COOKIE, ""), re.I) is not None + + if not retval: + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = code == 200 and re.search(r"\ACondition Intercepted", page, re.I) is not None + if retval: + break + + return retval diff --git a/waf/dotdefender.py b/waf/dotdefender.py new file mode 100644 index 000000000..3f9a192c0 --- /dev/null +++ b/waf/dotdefender.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "dotDefender (Applicure Technologies)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retVal = headers.get("X-dotDefender-denied", "") == 1 + if retVal: + break + + return retval diff --git a/waf/f5asm.py b/waf/f5asm.py deleted file mode 100644 index 8aeaf19f7..000000000 --- a/waf/f5asm.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/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 diff --git a/waf/hyperguard.py b/waf/hyperguard.py new file mode 100644 index 000000000..5a72be2d3 --- /dev/null +++ b/waf/hyperguard.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__ = "Hyperguard Web Application Firewall (art of defence Inc.)" + +def detect(get_page): + page, headers, code = get_page() + return re.search(r"\AODSESSION=", headers.get(HTTPHEADER.SET_COOKIE, ""), re.I) is not None diff --git a/waf/modsecurity.py b/waf/modsecurity.py new file mode 100644 index 000000000..5fcf3cec4 --- /dev/null +++ b/waf/modsecurity.py @@ -0,0 +1,24 @@ +#!/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 +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "ModSecurity: Open Source Web Application Firewall (Trustwave)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + if code == 501: + retVal = True + break + + return retval diff --git a/waf/netcontinuum.py b/waf/netcontinuum.py new file mode 100644 index 000000000..60624b4a4 --- /dev/null +++ b/waf/netcontinuum.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__ = "NetContinuum Web Application Firewall (NetContinuum/Barracuda Networks)" + +def detect(get_page): + page, headers, code = get_page() + return re.search(r"\ANCI__SessionId=", headers.get(HTTPHEADER.SET_COOKIE, ""), re.I) is not None diff --git a/waf/netscaler.py b/waf/netscaler.py new file mode 100644 index 000000000..21b162459 --- /dev/null +++ b/waf/netscaler.py @@ -0,0 +1,26 @@ +#!/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 +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "NetScaler (Citrix Systems)" + +def detect(get_page): + page, headers, code = get_page() + retval = re.search(r"\A(ns_af=|citrix_ns_id|NSC_)", headers.get(HTTPHEADER.SET_COOKIE, ""), re.I) is not None + + if not retval: + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = re.search(r"\Aclose", headers.get("Cneonction", "") or headers.get("nnCoection", ""), re.I) is not None + if retval: + break + + return retval diff --git a/waf/profense.py b/waf/profense.py new file mode 100644 index 000000000..816b8f23e --- /dev/null +++ b/waf/profense.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__ = "Profense Web Application Firewall (Armorlogic)" + +def detect(get_page): + page, headers, code = get_page() + return re.search(r"Profense", headers.get(HTTPHEADER.SERVER, ""), re.I) is not None diff --git a/waf/proventia.py b/waf/proventia.py new file mode 100644 index 000000000..615c96a7b --- /dev/null +++ b/waf/proventia.py @@ -0,0 +1,21 @@ +#!/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.data import kb +from lib.core.enums import HTTPHEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Proventia Web Application Security (IBM)" + +def detect(get_page): + page, headers, code = get_page() + if page is None: + return False + page, headers, code = get_page(url="/Admin_Files/") + return page is None diff --git a/waf/teros.py b/waf/teros.py new file mode 100644 index 000000000..aaf1a2df8 --- /dev/null +++ b/waf/teros.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__ = "ISV Teros Web Application Firewall (Teros/Citrix Systems)" + +def detect(get_page): + page, headers, code = get_page() + return re.search(r"\Ast8id=", headers.get(HTTPHEADER.SET_COOKIE, ""), re.I) is not None diff --git a/waf/trafficshield.py b/waf/trafficshield.py new file mode 100644 index 000000000..b167f11e9 --- /dev/null +++ b/waf/trafficshield.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__ = "TrafficShield (F5 Networks)" + +def detect(get_page): + page, headers, code = get_page() + return (re.search(r"\AASINFO=", headers.get(HTTPHEADER.COOKIE, ""), re.I) or re.search(r"F5-TrafficShield", headers.get(HTTPHEADER.SERVER, ""), re.I)) is not None diff --git a/waf/webappsecure.py b/waf/webappsecure.py new file mode 100644 index 000000000..f51443808 --- /dev/null +++ b/waf/webappsecure.py @@ -0,0 +1,21 @@ +#!/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.data import kb +from lib.core.enums import HTTPHEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "webApp.secure (webScurity)" + +def detect(get_page): + page, headers, code = get_page() + if code == 403: + return False + page, headers, code = get_page(get="nx=@@") + return code == 403