diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 2b947f9f5..1d4d2d885 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -65,6 +65,7 @@ from lib.core.settings import LOWER_RATIO_BOUND from lib.core.settings import UPPER_RATIO_BOUND from lib.core.settings import IDS_WAF_CHECK_PAYLOAD from lib.core.threads import getCurrentThreadData +from lib.core.wafs import wafs from lib.request.connect import Connect as Request from lib.request.inject import checkBooleanExpression from lib.request.templates import getPageTemplate @@ -1027,6 +1028,19 @@ def checkWaf(): if not falseResult: retVal = True + if retVal: + if conf.identifyWaf: + infoMsg = "detecting the vendor of the waf" + logger.info(infoMsg) + for waf in wafs: + if waf.identify(): + kb.waf = waf.vendor + infoMsg = "the vendor of the waf seems to be " + kb.waf + logger.info(infoMsg) + break + infoMsg = "the vendor of the waf is not detected" + logger.info(infoMsg) + conf.parameters = dict(backup) if retVal: diff --git a/lib/core/option.py b/lib/core/option.py index 8244f2fc9..e50418420 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1632,6 +1632,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.timeValidCharsRun = 0 kb.uChar = NULL kb.unionDuplicates = False + kb.waf = None kb.xpCmdshellAvailable = False if flushAll: diff --git a/lib/core/wafs.py b/lib/core/wafs.py new file mode 100644 index 000000000..cedf738dd --- /dev/null +++ b/lib/core/wafs.py @@ -0,0 +1,313 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re +import httplib +from lib.request.connect import Connect as Request +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger +from lib.core.enums import PLACE +from lib.core.common import getHostHeader + +class Waf(object): + """ + This class defines waf vendors and attack vectors for identification + """ + attack_vectors = [ + '/Admin_Files/', + '', + '%3Cscript%3Ealert%281%29%3C/script%3E', + '../../../../etc/passwd', + 'hello' + ] + + def __init__(self): + self.vendor = None + + def identify(self): + pass + + @staticmethod + def matchheader(headers, field, match): + headers = dict(headers.items()) + if field in headers: + if field == 'set-cookie': + values = headers[field].split('; ') + else: + values = [headers[field]] + for value in values: + if re.match(match, value, re.IGNORECASE): + return True + return False + +class WafProfense(Waf): + def __init__(self): + self.vendor = 'Profense' + + def identify(self): + page, headers, code = Request.getPage(content=True) + return Waf.matchheader(headers, 'server', 'profense') + +class WafNetContinuum(Waf): + def __init__(self): + self.vendor = 'NetContinuum' + + def identify(self): + page, headers, code = Request.getPage(content=True) + return Waf.matchheader(headers, 'set-cookie', '^NCI__SessionId=') + +class WafBarracuda(Waf): + def __init__(self): + self.vendor = 'Barracuda' + + def identify(self): + page, headers, code = Request.getPage(content=True) + return Waf.matchheader(headers, 'set-cookie', '^barra_counter_session=') + +class WafHyperGuard(Waf): + def __init__(self): + self.vendor = 'HyperGuard' + + def identify(self): + page, headers, code = Request.getPage(content=True) + return Waf.matchheader(headers, 'set-cookie', '^WODSESSION=') + +class WafBinarySec(Waf): + def __init__(self): + self.vendor = 'BinarySec' + + def identify(self): + page, headers, code = Request.getPage(content=True) + return Waf.matchheader(headers, 'server', 'BinarySec') + +class WafTeros(Waf): + def __init__(self): + self.vendor = 'Teros' + + def identify(self): + page, headers, code = Request.getPage(content=True) + return Waf.matchheader(headers, 'set-cookie', '^st8id=') + +class WafF5Trafficshield(Waf): + def __init__(self): + self.vendor = 'F5 Trafficshield' + + def identify(self): + page, headers, code = Request.getPage(content=True) + if Waf.matchheader(headers, 'cookie', '^ASINFO='): + return True + if Waf.matchheader(headers, 'server', 'F5-TrafficShield'): + return True + return False + +class WafF5ASM(Waf): + def __init__(self): + self.vendor = 'F5 ASM' + + def identify(self): + page, headers, code = Request.getPage(content=True) + return Waf.matchheader(headers, 'set-cookie', '^TS[a-zA-Z0-9]{3,6}=') + +class WafAirlock(Waf): + def __init__(self): + self.vendor = 'Airlock' + + def identify(self): + page, headers, code = Request.getPage(content=True) + return Waf.matchheader(headers, 'set-cookie', '^AL[_-]?(SESS|LB)=') + +class WafCitrixNetScaler(Waf): + def __init__(self): + self.vendor = 'Citrix NetScaler' + + def identify(self): + page, headers, code = Request.getPage(content=True) + if Waf.matchheader(headers, 'set-cookie', '^(ns_af=|citrix_ns_id|NSC_)'): + return True + + for attack_vector in Waf.attack_vectors: + get = attack_vector + page, headers, code = Request.getPage(content=True, get=get) + if Waf.matchheader(headers, 'Cneonction', 'close') or Waf.matchheader(headers, 'nnCoection', 'close'): + return True + return False + +class WafModSecurity(Waf): + def __init__(self): + self.vendor = 'ModSecurity' + + def identify(self): + for attack_vector in Waf.attack_vectors: + get = attack_vector + page, headers, code = Request.getPage(content=True, get=get) + if code == 501: + return True + return False + +class WafIBMWebApplicationSecurity(Waf): + def __init__(self): + self.vendor = 'IBM Web Application Security' + + def identify(self): + get = '/Admin_Files/' + page, headers, code = Request.getPage(content=True, get=get) + if page is None: + return True + return False + +class WafIBMDataPower(Waf): + def __init__(self): + self.vendor = 'IBM DataPower' + + def identify(self): + page, headers, code = Request.getPage(content=True) + return Waf.matchheader(headers, 'X-Backside-Transport', '^(OK|FAIL)') + +class WafDenyALL(Waf): + def __init__(self): + self.vendor = 'DenyALL' + + def identify(self): + page, headers, code = Request.getPage(content=True) + if Waf.matchheader(headers, 'set-cookie', '^sessioncookie='): + return True + for attack_vector in Waf.attack_vectors: + get = attack_vector + conn, _, _ = Request.getPage(response=True, get=get) + if conn.code == 200: + if conn.msg == 'Condition Intercepted': + return True + return False + +class WafdotDefender(Waf): + def __init__(self): + self.vendor = 'dotDefender' + + def identify(self): + for attack_vector in Waf.attack_vectors: + get = attack_vector + page, headers, code = Request.getPage(content=True, get=get) + if Waf.matchheader(headers, 'X-dotDefender-denied', '^1$'): + return True + return False + +class WafwebAppSecure(Waf): + def __init__(self): + self.vendor = 'webApp.secure' + + def identify(self): + page, headers, code = Request.getPage(content=True) + if code == 403: + return False + get = 'nx=@@' + page, headers, code = Request.getPage(content=True, get=get) + if code == 403: + return True + return False + +class WafBIGIP(Waf): + def __init__(self): + self.vendor = 'BIG-IP' + + def identify(self): + for attack_vector in Waf.attack_vectors: + get = attack_vector + page, headers, code = Request.getPage(content=True, get=get) + if Waf.matchheader(headers, 'X-Cnection', '^close$'): + return True + return False + +class WafURLScan(Waf): + def __init__(self): + self.vendor = 'URLScan' + + def identify(self): + auxHeaders = dict() + auxHeaders['Translate'] = 'z'*10 + auxHeaders['If'] = 'z'*10 + auxHeaders['Lock-Token'] = 'z'*10 + auxHeaders['Transfer-Encoding'] = 'z'*10 + page, headers, code1 = Request.getPage(content=True) + page, headers, code2 = Request.getPage(content=True, auxHeaders=auxHeaders) + if code1 != code2 and code2 == 404: + return True + return False + +class WafWebKnight(Waf): + def __init__(self): + self.vendor = 'WebKnight' + + def identify(self): + for attack_vector in Waf.attack_vectors: + get = attack_vector + page, headers, code = Request.getPage(content=True, get=get) + if code == 999: + return True + return False + +class WafSecureIIS(Waf): + def __init__(self): + self.vendor = 'SecureIIS' + + def identify(self): + auxHeaders = dict() + auxHeaders['Transfer-Encoding'] = 'z' * 1025 + page, headers, code = Request.getPage(content=True, auxHeaders=auxHeaders) + if code == 404: + return True + return False + +class WafImperva(Waf): + def __init__(self): + self.vendor = 'Imperva' + + def identify(self): + for attack_vector in Waf.attack_vectors: + conn = httplib.HTTPConnection(getHostHeader(conf.url), conf.port) + conn.request('GET', '/' + attack_vector) + r = conn.getresponse() + if r.version == 10: + return True + return False + +class WafISAServer(Waf): + def __init__(self): + self.vendor = 'ISA Server' + + def identify(self): + auxHeaders = dict() + auxHeaders['Host'] = '123456' + conn, _, _ = Request.getPage(response=True, auxHeaders=auxHeaders) + if conn.msg == 'Forbidden ( The server denied the specified Uniform Resource Locator (URL). Contact the server administrator. )': + return True + return False + +wafs = [ + WafProfense(), + WafNetContinuum(), + WafBarracuda(), + WafHyperGuard(), + WafBinarySec(), + WafTeros(), + WafF5Trafficshield(), + WafF5ASM(), + WafAirlock(), + WafCitrixNetScaler(), + WafModSecurity(), + WafIBMWebApplicationSecurity(), + WafIBMDataPower(), + WafDenyALL(), + WafdotDefender(), + WafwebAppSecure(), + WafBIGIP(), + WafURLScan(), + WafWebKnight(), + WafSecureIIS(), + WafImperva(), + WafISAServer() +] diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index d2927b23b..e4a173284 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -616,6 +616,10 @@ def cmdLineParser(): action="store_true", help="Heuristically check for WAF/IPS/IDS protection") + miscellaneous.add_option("--identify-waf", dest="identifyWaf", + action="store_true", + help="Identify the vendor of WAF") + miscellaneous.add_option("--cleanup", dest="cleanup", action="store_true", help="Clean up the DBMS by sqlmap specific "