Ejemplo n.º 1
0
 def __init__(self, host):
     self.host = host
     self.cnames = host.dns_results.get('CNAME')
     self.request_handler = RequestHandler()
     self.web_server_validator = WebServerValidator()
     self.waf_present = False
     self.waf_cname_map = {
         "incapdns": "Incapsula",
         "edgekey": "Akamai",
         "akamai": "Akamai",
         "edgesuite": "Akamai",
         "distil": "Distil Networks",
         "cloudfront": "CloudFront",
         "netdna-cdn": "MaxCDN"
     }
     self.waf_app_method_map = {
         "CloudFront": WAFApplicationMethods.detect_cloudfront,
         "Cloudflare": WAFApplicationMethods.detect_cloudflare,
         "Incapsula": WAFApplicationMethods.detect_incapsula,
         "MaxCDN": WAFApplicationMethods.detect_maxcdn,
         "Edgecast": WAFApplicationMethods.detect_edgecast,
         "Distil Networks": WAFApplicationMethods.detect_distil,
         "Sucuri": WAFApplicationMethods.detect_sucuri,
         "Reblaze": WAFApplicationMethods.detect_reblaze
     }
     log_file = HelpUtilities.get_output_path("{}/WAF.txt".format(
         self.host.target))
     self.logger = Logger(log_file)
Ejemplo n.º 2
0
 def __init__(self, host):
     self.host = host
     self.request_handler = RequestHandler()
     self.web_server_validator = WebServerValidator()
     self.web_scan_results = []
     self.headers = None
     self.robots = None
     log_file = HelpUtilities.get_output_path("{}/web_scan.txt".format(
         self.host.target))
     self.target_dir = "/".join(log_file.split("/")[:-1])
     self.logger = Logger(log_file)
Ejemplo n.º 3
0
 def __init__(self, host):
     self.host = host
     self.request_handler = RequestHandler()
     self.web_server_validator = WebServerValidator()
     self.headers = None
     self.robots = None
     self.forms = None
     self.fuzzable_urls = set()
     self.emails = set()
     log_file = HelpUtilities.get_output_path("{}/web_scan.txt".format(
         self.host.target))
     self.target_dir = "/".join(log_file.split("/")[:-1])
     self.logger = Logger(log_file)
     self.storage_explorer = StorageExplorer(host, self.logger)
Ejemplo n.º 4
0
 def __init__(self, host):
     self.host = host
     self.cnames = host.dns_results.get('CNAME')
     self.request_handler = RequestHandler()
     self.web_server_validator = WebServerValidator()
     self.waf_present = False
     self.waf_cname_map = {
         "incapdns": "Incapsula",
         "edgekey": "Akamai",
         "akamai": "Akamai",
         "edgesuite": "Akamai",
         "distil": "Distil Networks",
         "cloudfront": "CloudFront",
         "netdna-cdn": "MaxCDN"
     }
     self.waf_app_method_map = {
         "CloudFront": WAFApplicationMethods.detect_cloudfront,
         "Cloudflare": WAFApplicationMethods.detect_cloudflare,
         "Incapsula": WAFApplicationMethods.detect_incapsula,
         "MaxCDN": WAFApplicationMethods.detect_maxcdn,
         "Edgecast": WAFApplicationMethods.detect_edgecast,
         "Distil Networks": WAFApplicationMethods.detect_distil,
         "Sucuri": WAFApplicationMethods.detect_sucuri,
         "Reblaze": WAFApplicationMethods.detect_reblaze
     }
     log_file = HelpUtilities.get_output_path("{}/WAF.txt".format(self.host.target))
     self.logger = Logger(log_file)
Ejemplo n.º 5
0
class WAF:
    def __init__(self, host):
        self.host = host
        self.cnames = host.dns_results.get('CNAME')
        self.request_handler = RequestHandler()
        self.web_server_validator = WebServerValidator()
        self.waf_present = False
        self.waf_cname_map = {
            "incapdns": "Incapsula",
            "edgekey": "Akamai",
            "akamai": "Akamai",
            "edgesuite": "Akamai",
            "distil": "Distil Networks",
            "cloudfront": "CloudFront",
            "netdna-cdn": "MaxCDN"
        }
        self.waf_app_method_map = {
            "CloudFront": WAFApplicationMethods.detect_cloudfront,
            "Cloudflare": WAFApplicationMethods.detect_cloudflare,
            "Incapsula": WAFApplicationMethods.detect_incapsula,
            "MaxCDN": WAFApplicationMethods.detect_maxcdn,
            "Edgecast": WAFApplicationMethods.detect_edgecast,
            "Distil Networks": WAFApplicationMethods.detect_distil,
            "Sucuri": WAFApplicationMethods.detect_sucuri,
            "Reblaze": WAFApplicationMethods.detect_reblaze
        }
        log_file = HelpUtilities.get_output_path("{}/WAF.txt".format(
            self.host.target))
        self.logger = Logger(log_file)

    def _waf_detected(self, name):
        self.logger.info(
            "{} Detected WAF presence in web application: {}{}{}".format(
                COLORED_COMBOS.BAD, COLOR.RED, name, COLOR.RESET))
        self.waf_present = True

    def _detect_by_cname(self):
        for waf in self.waf_cname_map:
            if any(waf in str(cname) for cname in self.cnames):
                self.logger.info(
                    "{} Detected WAF presence in CNAME: {}{}{}".format(
                        COLORED_COMBOS.BAD, COLOR.RED,
                        self.waf_cname_map.get(waf), COLOR.RESET))
                self.waf_present = True

    def _detect_by_application(self):
        try:
            session = self.request_handler.get_new_session()
            response = session.get(timeout=20,
                                   allow_redirects=True,
                                   url="{}://{}:{}".format(
                                       self.host.protocol, self.host.target,
                                       self.host.port))
            for waf, method in self.waf_app_method_map.items():
                result = method(response)
                if result:
                    self._waf_detected(waf)

        except (ConnectionError, TooManyRedirects) as e:
            raise WAFException("Couldn't get response from server.\n"
                               "Caused due to exception: {}".format(str(e)))

    async def detect(self):
        self.logger.info("{} Trying to detect WAF presence in {}".format(
            COLORED_COMBOS.INFO, self.host))
        if self.cnames:
            self._detect_by_cname()
        try:
            self.web_server_validator.validate_target_webserver(self.host)
            self._detect_by_application()

            if not self.waf_present:
                self.logger.info(
                    "{} Did not detect WAF presence in target".format(
                        COLORED_COMBOS.GOOD))
        except WebServerValidatorException:
            self.logger.info(
                "{} Target does not seem to have an active web server on port {}. "
                "No WAF could be detected on an application level.".format(
                    COLORED_COMBOS.NOTIFY, self.host.port))
Ejemplo n.º 6
0
class WebApplicationScanner:
    def __init__(self, host):
        self.host = host
        self.request_handler = RequestHandler()
        self.web_server_validator = WebServerValidator()
        self.headers = None
        self.robots = None
        self.forms = None
        self.fuzzable_urls = set()
        log_file = HelpUtilities.get_output_path("{}/web_scan.txt".format(
            self.host.target))
        self.target_dir = "/".join(log_file.split("/")[:-1])
        self.logger = Logger(log_file)

    def _detect_cms(self, tries=0):
        """
        Detect CMS using whatcms.org.
        Has a re-try mechanism because false negatives may occur
        :param tries: Count of tries for CMS discovery
        """
        # WhatCMS is under CloudFlare which detects and blocks proxied/Tor traffic, hence normal request.
        page = requests.get(
            url="https://whatcms.org/?s={}".format(self.host.target))
        soup = BeautifulSoup(page.text, "lxml")
        found = soup.select(".panel.panel-success")
        if found:
            try:
                cms = [a for a in soup.select("a")
                       if "/c/" in a.get("href")][0]
                self.logger.info(
                    "{} CMS detected: target is using {}{}{}".format(
                        COLORED_COMBOS.GOOD, COLOR.GREEN, cms.get("title"),
                        COLOR.RESET))
            except IndexError:
                if tries >= 4:
                    return
                else:
                    self._detect_cms(tries=tries + 1)
        else:
            if tries >= 4:
                return
            else:
                self._detect_cms(tries=tries + 1)

    def _cookie_info(self, jar):
        for cookie in jar:
            key = cookie.__dict__.get("name")
            value = cookie.__dict__.get("value")
            domain = cookie.__dict__.get("domain")
            secure = cookie.__dict__.get("secure")
            http_only = cookie.has_nonstandard_attr("HttpOnly")
            try:
                if domain in self.host.target or self.host.target in domain:
                    if not secure or not http_only:
                        current = "%s Cookie: {%s: %s} -" % (
                            COLORED_COMBOS.GOOD, key, value)
                        if not secure and not http_only:
                            current += " both secure and HttpOnly flags are not set"
                        elif not secure:
                            current += " secure flag not set"
                        else:
                            current += " HttpOnly flag not set"
                        self.logger.info(current)

            except TypeError:
                continue

    def _server_info(self):
        if self.headers.get("server"):
            self.logger.info("{} Web server detected: {}{}{}".format(
                COLORED_COMBOS.GOOD, COLOR.GREEN, self.headers.get("server"),
                COLOR.RESET))

    def _x_powered_by(self):
        if self.headers.get("X-Powered-By"):
            self.logger.info("{} X-Powered-By header detected: {}{}{}".format(
                COLORED_COMBOS.GOOD, COLOR.GREEN,
                self.headers.get("X-Powered-By"), COLOR.RESET))

    def _anti_clickjacking(self):
        if not self.headers.get("X-Frame-Options"):
            self.logger.info(
                "{} X-Frame-Options header not detected - target might be vulnerable to clickjacking"
                .format(COLORED_COMBOS.GOOD))

    def _xss_protection(self):
        xss_header = self.headers.get("X-XSS-PROTECTION")
        if xss_header and "1" in xss_header:
            self.logger.info("{} Found X-XSS-PROTECTION header".format(
                COLORED_COMBOS.BAD))

    def _cors_wildcard(self):
        if self.headers.get("Access-Control-Allow-Origin") == "*":
            self.logger.info("{} CORS wildcard detected".format(
                COLORED_COMBOS.GOOD))

    def _robots(self):
        res = self.request_handler.send("GET",
                                        url="{}://{}:{}/robots.txt".format(
                                            self.host.protocol,
                                            self.host.target, self.host.port))
        if res.status_code != 404 and res.text and "<!DOCTYPE html>" not in res.text:
            self.logger.info("{} Found robots.txt".format(COLORED_COMBOS.GOOD))
            with open("{}/robots.txt".format(self.target_dir), "w") as file:
                file.write(res.text)

    def _sitemap(self):
        res = self.request_handler.send("GET",
                                        url="{}://{}:{}/sitemap.xml".format(
                                            self.host.protocol,
                                            self.host.target, self.host.port))
        if res.status_code != 404 and res.text and "<!DOCTYPE html>" not in res.text:
            self.logger.info("{} Found sitemap.xml".format(
                COLORED_COMBOS.GOOD))
            with open("{}/sitemap.xml".format(self.target_dir), "w") as file:
                file.write(res.text)

    def _find_fuzzable_urls(self, soup):
        urls = soup.select("a")
        if urls:
            for url in urls:
                href = url.get("href")
                if href and "?" in href and "=" in href:
                    self.fuzzable_urls.add(href)
            if self.fuzzable_urls:
                self.logger.info("{} {} fuzzable URLs discovered".format(
                    COLORED_COMBOS.NOTIFY, len(self.fuzzable_urls)))

                base_target = "{}://{}:{}".format(self.host.protocol,
                                                  self.host.target,
                                                  self.host.port)
                for url in self.fuzzable_urls:
                    if url.startswith("/"):
                        self.logger.debug("\t{}{}".format(base_target, url))
                    else:
                        self.logger.debug("\t{}".format(url))

    def _find_forms(self, soup):
        self.forms = soup.select("form")
        if self.forms:
            self.logger.info("{} {} HTML forms discovered".format(
                COLORED_COMBOS.NOTIFY, len(self.forms)))
            for form in self.forms:
                form_id = form.get("id")
                form_class = form.get("class")
                form_method = form.get("method")
                form_action = form.get("action")
                self.logger.debug(
                    "Form details: ID: {}, Class: {}, Method: {}, action: {}".
                    format(form_id, form_class, form_method, form_action))

    def _find_emails(self, soup):
        pass

    def get_web_application_info(self):
        session = self.request_handler.get_new_session()
        try:
            with session:
                # Test if target is serving HTTP requests
                response = session.get(timeout=20,
                                       url="{}://{}:{}".format(
                                           self.host.protocol,
                                           self.host.target, self.host.port))
                self.headers = response.headers
                self._detect_cms()
                self._robots()
                self._sitemap()
                self._server_info()
                self._x_powered_by()
                self._cors_wildcard()
                self._xss_protection()
                self._anti_clickjacking()
                self._cookie_info(session.cookies)

                soup = BeautifulSoup(response.text, "lxml")
                self._find_fuzzable_urls(soup)
                self._find_forms(soup)

        except (ConnectionError, TooManyRedirects) as e:
            raise WebAppScannerException("Couldn't get response from server.\n"
                                         "Caused due to exception: {}".format(
                                             str(e)))

    async def run_scan(self):
        self.logger.info("{} Trying to collect {} web application data".format(
            COLORED_COMBOS.INFO, self.host))
        try:
            self.web_server_validator.validate_target_webserver(self.host)
            self.get_web_application_info()
        except WebServerValidatorException:
            self.logger.info(
                "{} Target does not seem to have an active web server on port: {}. "
                "No web application data will be gathered.".format(
                    COLORED_COMBOS.NOTIFY, self.host.port))
            return
Ejemplo n.º 7
0
class WAF:

    def __init__(self, host):
        self.host = host
        self.cnames = host.dns_results.get('CNAME')
        self.request_handler = RequestHandler()
        self.web_server_validator = WebServerValidator()
        self.waf_present = False
        self.waf_cname_map = {
            "incapdns": "Incapsula",
            "edgekey": "Akamai",
            "akamai": "Akamai",
            "edgesuite": "Akamai",
            "distil": "Distil Networks",
            "cloudfront": "CloudFront",
            "netdna-cdn": "MaxCDN"
        }
        self.waf_app_method_map = {
            "CloudFront": WAFApplicationMethods.detect_cloudfront,
            "Cloudflare": WAFApplicationMethods.detect_cloudflare,
            "Incapsula": WAFApplicationMethods.detect_incapsula,
            "MaxCDN": WAFApplicationMethods.detect_maxcdn,
            "Edgecast": WAFApplicationMethods.detect_edgecast,
            "Distil Networks": WAFApplicationMethods.detect_distil,
            "Sucuri": WAFApplicationMethods.detect_sucuri,
            "Reblaze": WAFApplicationMethods.detect_reblaze
        }
        log_file = HelpUtilities.get_output_path("{}/WAF.txt".format(self.host.target))
        self.logger = Logger(log_file)

    def _waf_detected(self, name):
        self.logger.info(
            "{} Detected WAF presence in web application: {}{}{}".format(
                COLORED_COMBOS.BAD, COLOR.RED, name, COLOR.RESET))
        self.waf_present = True

    def _detect_by_cname(self):
        for waf in self.waf_cname_map:
            if any(waf in str(cname) for cname in self.cnames):
                self.logger.info("{} Detected WAF presence in CNAME: {}{}{}".format(
                    COLORED_COMBOS.BAD, COLOR.RED, self.waf_cname_map.get(waf), COLOR.RESET)
                )
                self.waf_present = True

    def _detect_by_application(self):
        try:
            session = self.request_handler.get_new_session()
            response = session.get(
                timeout=20,
                allow_redirects=True,
                url="{}://{}:{}".format(
                    self.host.protocol,
                    self.host.target,
                    self.host.port
                )
            )
            for waf, method in self.waf_app_method_map.items():
                result = method(response)
                if result:
                    self._waf_detected(waf)

        except (ConnectionError, TooManyRedirects) as e:
            raise WAFException("Couldn't get response from server.\n"
                               "Caused due to exception: {}".format(str(e)))

    async def detect(self):
        self.logger.info("{} Trying to detect WAF presence in {}".format(COLORED_COMBOS.INFO, self.host))
        if self.cnames:
            self._detect_by_cname()
        try:
            self.web_server_validator.validate_target_webserver(self.host)
            self._detect_by_application()

            if not self.waf_present:
                self.logger.info("{} Did not detect WAF presence in target".format(COLORED_COMBOS.GOOD))
        except WebServerValidatorException:
            self.logger.info(
                "{} Target does not seem to have an active web server on port {}. "
                "No WAF could be detected on an application level.".format(COLORED_COMBOS.NOTIFY, self.host.port))