Пример #1
0
    async def attack(self, request: Request):
        page = request.path

        for mutated_request, parameter, _payload, _flags in self.mutator.mutate(
                request):
            log_verbose(f"[¨] {mutated_request.url}")

            try:
                response = await self.crawler.async_send(mutated_request)
            except ReadTimeout:
                self.network_errors += 1
                await self.add_anom_medium(request_id=request.path_id,
                                           category=Messages.RES_CONSUMPTION,
                                           request=mutated_request,
                                           parameter=parameter,
                                           info="Timeout (" + parameter + ")",
                                           wstg=RESOURCE_CONSUMPTION_WSTG_CODE)

                log_orange("---")
                log_orange(Messages.MSG_TIMEOUT, page)
                log_orange(Messages.MSG_EVIL_REQUEST)
                log_orange(mutated_request.http_repr())
                log_orange("---")
            except HTTPStatusError:
                self.network_errors += 1
                logging.error(
                    _("Error: The server did not understand this request"))
            except RequestError:
                self.network_errors += 1
            else:
                if "wapiti" in response.headers:
                    await self.add_vuln_low(
                        request_id=request.path_id,
                        category=NAME,
                        request=mutated_request,
                        parameter=parameter,
                        info=_(
                            "{0} via injection in the parameter {1}").format(
                                self.MSG_VULN, parameter),
                        wstg=WSTG_CODE)

                    if parameter == "QUERY_STRING":
                        injection_msg = Messages.MSG_QS_INJECT
                    else:
                        injection_msg = Messages.MSG_PARAM_INJECT

                    log_red("---")
                    log_red(injection_msg, self.MSG_VULN, page, parameter)
                    log_red(Messages.MSG_EVIL_REQUEST)
                    log_red(mutated_request.http_repr())
                    log_red("---")
Пример #2
0
    async def attack(self, request: Request):
        page = request.path
        self.excluded_path.add(page)

        option_request = Request(page,
                                 "OPTIONS",
                                 referer=request.referer,
                                 link_depth=request.link_depth)

        log_verbose(f"[+] {option_request}")

        try:
            response = await self.crawler.async_send(option_request)
        except RequestError:
            self.network_errors += 1
            return

        if response.is_success or response.is_redirect:
            methods = response.headers.get("allow", '').upper().split(',')
            methods = {method.strip() for method in methods if method.strip()}
            interesting_methods = sorted(methods - self.KNOWN_METHODS)

            if interesting_methods:
                log_orange("---")
                log_orange(
                    _("Interesting methods allowed on {}: {}").format(
                        page, ", ".join(interesting_methods)))
                await self.add_addition(
                    category=NAME,
                    request=option_request,
                    info=_("Interesting methods allowed on {}: {}").format(
                        page, ", ".join(interesting_methods)),
                    wstg=WSTG_CODE)
                log_orange("---")
Пример #3
0
    async def error_based_attack(self, request: Request):
        page = request.path
        saw_internal_error = False
        current_parameter = None
        vulnerable_parameter = False
        vulnerable_parameters = set()

        for mutated_request, parameter, __, __ in self.mutator.mutate(request):
            if current_parameter != parameter:
                # Forget what we know about current parameter
                current_parameter = parameter
                vulnerable_parameter = False
            elif vulnerable_parameter:
                # If parameter is vulnerable, just skip till next parameter
                continue

            log_verbose(f"[¨] {mutated_request}")

            try:
                response = await self.crawler.async_send(mutated_request)
            except RequestError:
                self.network_errors += 1
            else:
                vuln_info = self._find_pattern_in_response(response.content)
                if vuln_info and not await self.is_false_positive(request):
                    # An error message implies that a vulnerability may exists

                    if parameter == "QUERY_STRING":
                        vuln_message = Messages.MSG_QS_INJECT.format(vuln_info, page)
                    else:
                        vuln_message = _("{0} via injection in the parameter {1}").format(vuln_info, parameter)

                    await self.add_vuln_critical(
                        request_id=request.path_id,
                        category=NAME,
                        request=mutated_request,
                        info=vuln_message,
                        parameter=parameter,
                        wstg=WSTG_CODE
                    )

                    log_red("---")
                    log_red(
                        Messages.MSG_QS_INJECT if parameter == "QUERY_STRING" else Messages.MSG_PARAM_INJECT,
                        vuln_info,
                        page,
                        parameter
                    )
                    log_red(Messages.MSG_EVIL_REQUEST)
                    log_red(mutated_request.http_repr())
                    log_red("---")

                    # We reached maximum exploitation for this parameter, don't send more payloads
                    vulnerable_parameter = True
                    vulnerable_parameters.add(parameter)

                elif response.status == 500 and not saw_internal_error:
                    saw_internal_error = True
                    if parameter == "QUERY_STRING":
                        anom_msg = Messages.MSG_QS_500
                    else:
                        anom_msg = Messages.MSG_PARAM_500.format(parameter)

                    await self.add_anom_high(
                        request_id=request.path_id,
                        category=Messages.ERROR_500,
                        request=mutated_request,
                        info=anom_msg,
                        parameter=parameter,
                        wstg=INTERNAL_ERROR_WSTG_CODE
                    )

                    log_orange("---")
                    log_orange(Messages.MSG_500, page)
                    log_orange(Messages.MSG_EVIL_REQUEST)
                    log_orange(mutated_request.http_repr())
                    log_orange("---")

        return vulnerable_parameters
Пример #4
0
    async def attempt_exploit(self, method, payloads, injection_request,
                              parameter, taint, output_request):
        timeouted = False
        page = injection_request.path
        saw_internal_error = False
        output_url = output_request.url

        attack_mutator = Mutator(methods=method,
                                 payloads=payloads,
                                 qs_inject=self.must_attack_query_string,
                                 parameters=[parameter],
                                 skip=self.options.get("skipped_parameters"))

        for evil_request, xss_param, _xss_payload, xss_flags in attack_mutator.mutate(
                injection_request):
            log_verbose(f"[¨] {evil_request}")

            try:
                await self.crawler.async_send(evil_request)
            except ReadTimeout:
                self.network_errors += 1
                if timeouted:
                    continue

                log_orange("---")
                log_orange(Messages.MSG_TIMEOUT, page)
                log_orange(Messages.MSG_EVIL_REQUEST)
                log_orange(evil_request.http_repr())
                log_orange("---")

                if xss_param == "QUERY_STRING":
                    anom_msg = Messages.MSG_QS_TIMEOUT
                else:
                    anom_msg = Messages.MSG_PARAM_TIMEOUT.format(xss_param)

                await self.add_anom_medium(
                    request_id=injection_request.path_id,
                    category=Messages.RES_CONSUMPTION,
                    request=evil_request,
                    info=anom_msg,
                    parameter=xss_param,
                    wstg=RESOURCE_CONSUMPTION_WSTG_CODE)
                timeouted = True
            except RequestError:
                self.network_errors += 1
                continue
            else:
                try:
                    response = await self.crawler.async_send(output_request)
                except RequestError:
                    self.network_errors += 1
                    continue

                if (response.status not in (301, 302, 303)
                        and valid_xss_content_type(evil_request)
                        and check_payload(self.DATA_DIR, self.PAYLOADS_FILE,
                                          self.external_endpoint,
                                          self.proto_endpoint, response,
                                          xss_flags, taint)):

                    if page == output_request.path:
                        description = _(
                            "Permanent XSS vulnerability found via injection in the parameter {0}"
                        ).format(xss_param)
                    else:
                        description = _(
                            "Permanent XSS vulnerability found in {0} by injecting"
                            " the parameter {1} of {2}").format(
                                output_request.url, parameter, page)

                    if has_strong_csp(response):
                        description += ".\n" + _(
                            "Warning: Content-Security-Policy is present!")

                    await self.add_vuln_high(
                        request_id=injection_request.path_id,
                        category=NAME,
                        request=evil_request,
                        parameter=xss_param,
                        info=description,
                        wstg=WSTG_CODE)

                    if xss_param == "QUERY_STRING":
                        injection_msg = Messages.MSG_QS_INJECT
                    else:
                        injection_msg = Messages.MSG_PARAM_INJECT

                    log_red("---")
                    # TODO: a last parameter should give URL used to pass the vulnerable parameter
                    log_red(injection_msg, self.MSG_VULN, output_url,
                            xss_param)

                    if has_strong_csp(response):
                        log_red(
                            _("Warning: Content-Security-Policy is present!"))

                    log_red(Messages.MSG_EVIL_REQUEST)
                    log_red(evil_request.http_repr())
                    log_red("---")

                    # stop trying payloads and jump to the next parameter
                    break
                if response.status == 500 and not saw_internal_error:
                    if xss_param == "QUERY_STRING":
                        anom_msg = Messages.MSG_QS_500
                    else:
                        anom_msg = Messages.MSG_PARAM_500.format(xss_param)

                    await self.add_anom_high(
                        request_id=injection_request.path_id,
                        category=Messages.ERROR_500,
                        request=evil_request,
                        info=anom_msg,
                        parameter=xss_param,
                        wstg=INTERNAL_ERROR_WSTG_CODE)

                    log_orange("---")
                    log_orange(Messages.MSG_500, page)
                    log_orange(Messages.MSG_EVIL_REQUEST)
                    log_orange(evil_request.http_repr())
                    log_orange("---")
                    saw_internal_error = True
Пример #5
0
    async def attack(self, request: Request):
        timeouted = False
        page = request.path
        saw_internal_error = False
        current_parameter = None
        vulnerable_parameter = False

        if request.url not in self.attacked_urls:
            await self.attack_body(request)
            self.attacked_urls.add(request.url)

        if request.path_id in self.vulnerables:
            return

        if request.is_multipart:
            await self.attack_upload(request)
            if request.path_id in self.vulnerables:
                return

        for mutated_request, parameter, __, flags in self.mutator.mutate(
                request):
            if current_parameter != parameter:
                # Forget what we know about current parameter
                current_parameter = parameter
                vulnerable_parameter = False
            elif vulnerable_parameter:
                # If parameter is vulnerable, just skip till next parameter
                continue

            log_verbose(f"[¨] {mutated_request}")

            try:
                response = await self.crawler.async_send(mutated_request)
            except ReadTimeout:
                self.network_errors += 1
                if timeouted:
                    continue

                log_orange("---")
                log_orange(Messages.MSG_TIMEOUT, page)
                log_orange(Messages.MSG_EVIL_REQUEST)
                log_orange(mutated_request.http_repr())
                log_orange("---")

                if parameter == "QUERY_STRING":
                    anom_msg = Messages.MSG_QS_TIMEOUT
                else:
                    anom_msg = Messages.MSG_PARAM_TIMEOUT.format(parameter)

                await self.add_anom_medium(request_id=request.path_id,
                                           category=Messages.RES_CONSUMPTION,
                                           request=mutated_request,
                                           info=anom_msg,
                                           parameter=parameter,
                                           wstg=RESOURCE_CONSUMPTION_WSTG_CODE)
                timeouted = True
            except RequestError:
                self.network_errors += 1
                continue
            else:
                pattern = search_pattern(response.content,
                                         self.flag_to_patterns(flags))
                if pattern and not await self.false_positive(request, pattern):
                    # An error message implies that a vulnerability may exists
                    if parameter == "QUERY_STRING":
                        vuln_message = Messages.MSG_QS_INJECT.format(
                            self.MSG_VULN, page)
                    else:
                        vuln_message = _(
                            "{0} via injection in the parameter {1}").format(
                                self.MSG_VULN, parameter)

                    await self.add_vuln_high(request_id=request.path_id,
                                             category=NAME,
                                             request=mutated_request,
                                             info=vuln_message,
                                             parameter=parameter,
                                             wstg=WSTG_CODE)

                    log_red("---")
                    log_red(
                        Messages.MSG_QS_INJECT if parameter == "QUERY_STRING"
                        else Messages.MSG_PARAM_INJECT, self.MSG_VULN, page,
                        parameter)
                    log_red(Messages.MSG_EVIL_REQUEST)
                    log_red(mutated_request.http_repr())
                    log_red("---")

                    # We reached maximum exploitation for this parameter, don't send more payloads
                    vulnerable_parameter = True
                    continue

                if response.status == 500 and not saw_internal_error:
                    saw_internal_error = True
                    if parameter == "QUERY_STRING":
                        anom_msg = Messages.MSG_QS_500
                    else:
                        anom_msg = Messages.MSG_PARAM_500.format(parameter)

                    await self.add_anom_high(request_id=request.path_id,
                                             category=Messages.ERROR_500,
                                             request=mutated_request,
                                             info=anom_msg,
                                             parameter=parameter,
                                             wstg=INTERNAL_ERROR_WSTG_CODE)

                    log_orange("---")
                    log_orange(Messages.MSG_500, page)
                    log_orange(Messages.MSG_EVIL_REQUEST)
                    log_orange(mutated_request.http_repr())
                    log_orange("---")
Пример #6
0
    async def attack(self, request: Request):
        warned = False
        timeouted = False
        page = request.path
        saw_internal_error = False
        current_parameter = None
        vulnerable_parameter = False

        for mutated_request, parameter, __, flags in self.mutator.mutate(
                request):
            if current_parameter != parameter:
                # Forget what we know about current parameter
                current_parameter = parameter
                vulnerable_parameter = False
            elif vulnerable_parameter:
                # If parameter is vulnerable, just skip till next parameter
                continue

            if flags.payload_type == PayloadType.time and request.path_id in self.false_positive_timeouts:
                # If the original request is known to gives timeout and payload is time-based, just skip
                # and move to next payload
                continue

            log_verbose(f"[¨] {mutated_request}")

            try:
                response = await self.crawler.async_send(mutated_request)
            except ReadTimeout:
                if flags.payload_type == PayloadType.time:
                    if await self.does_timeout(request):
                        self.network_errors += 1
                        self.false_positive_timeouts.add(request.path_id)
                        continue

                    vuln_info = _("Blind command execution")
                    if parameter == "QUERY_STRING":
                        vuln_message = Messages.MSG_QS_INJECT.format(
                            vuln_info, page)
                    else:
                        vuln_message = _(
                            "{0} via injection in the parameter {1}").format(
                                vuln_info, parameter)

                    await self.add_vuln_critical(request_id=request.path_id,
                                                 category=NAME,
                                                 request=mutated_request,
                                                 info=vuln_message,
                                                 parameter=parameter,
                                                 wstg=WSTG_CODE)

                    log_red("---")
                    log_red(
                        Messages.MSG_QS_INJECT if parameter == "QUERY_STRING"
                        else Messages.MSG_PARAM_INJECT, vuln_info, page,
                        parameter)
                    log_red(Messages.MSG_EVIL_REQUEST)
                    log_red(mutated_request.http_repr())
                    log_red("---")
                    vulnerable_parameter = True
                    continue

                self.network_errors += 1
                if timeouted:
                    continue

                log_orange("---")
                log_orange(Messages.MSG_TIMEOUT, page)
                log_orange(Messages.MSG_EVIL_REQUEST)
                log_orange(mutated_request.http_repr())
                log_orange("---")

                if parameter == "QUERY_STRING":
                    anom_msg = Messages.MSG_QS_TIMEOUT
                else:
                    anom_msg = Messages.MSG_PARAM_TIMEOUT.format(parameter)

                await self.add_anom_medium(request_id=request.path_id,
                                           category=Messages.RES_CONSUMPTION,
                                           request=mutated_request,
                                           info=anom_msg,
                                           parameter=parameter,
                                           wstg=RESOURCE_CONSUMPTION_WSTG_CODE)
                timeouted = True
            except RequestError:
                self.network_errors += 1
            else:
                # No timeout raised
                vuln_info, executed, warned = self._find_pattern_in_response(
                    response.content, warned)
                if vuln_info:
                    # An error message implies that a vulnerability may exists

                    if parameter == "QUERY_STRING":
                        vuln_message = Messages.MSG_QS_INJECT.format(
                            vuln_info, page)
                        log_message = Messages.MSG_QS_INJECT
                    else:
                        vuln_message = _(
                            "{0} via injection in the parameter {1}").format(
                                vuln_info, parameter)
                        log_message = Messages.MSG_PARAM_INJECT

                    await self.add_vuln_critical(request_id=request.path_id,
                                                 category=NAME,
                                                 request=mutated_request,
                                                 info=vuln_message,
                                                 parameter=parameter,
                                                 wstg=WSTG_CODE)

                    log_red("---")
                    log_red(log_message, vuln_info, page, parameter)
                    log_red(Messages.MSG_EVIL_REQUEST)
                    log_red(mutated_request.http_repr())
                    log_red("---")

                    if executed:
                        # We reached maximum exploitation for this parameter, don't send more payloads
                        vulnerable_parameter = True
                        continue

                elif response.is_server_error and not saw_internal_error:
                    saw_internal_error = True
                    if parameter == "QUERY_STRING":
                        anom_msg = Messages.MSG_QS_500
                    else:
                        anom_msg = Messages.MSG_PARAM_500.format(parameter)

                    await self.add_anom_high(request_id=request.path_id,
                                             category=Messages.ERROR_500,
                                             request=mutated_request,
                                             info=anom_msg,
                                             parameter=parameter,
                                             wstg=INTERNAL_ERROR_WSTG_CODE)

                    log_orange("---")
                    log_orange(Messages.MSG_500, page)
                    log_orange(Messages.MSG_EVIL_REQUEST)
                    log_orange(mutated_request.http_repr())
                    log_orange("---")
Пример #7
0
    async def attack(self, request: Request):
        page = request.path
        saw_internal_error = False
        current_parameter = None
        vulnerable_parameter = False

        for mutated_request, parameter, _payload, _flags in self.mutator.mutate(
                request):
            if current_parameter != parameter:
                # Forget what we know about current parameter
                current_parameter = parameter
                vulnerable_parameter = False
            elif vulnerable_parameter:
                # If parameter is vulnerable, just skip till next parameter
                continue

            log_verbose(f"[¨] {mutated_request}")

            try:
                response = await self.crawler.async_send(mutated_request)
            except ReadTimeout:
                # The request with time based payload did timeout, what about a regular request?
                if await self.does_timeout(request):
                    self.network_errors += 1
                    logging.error(
                        "[!] Too much lag from website, can't reliably test time-based blind SQL"
                    )
                    break

                if parameter == "QUERY_STRING":
                    vuln_message = Messages.MSG_QS_INJECT.format(
                        self.MSG_VULN, page)
                    log_message = Messages.MSG_QS_INJECT
                else:
                    vuln_message = _(
                        "{0} via injection in the parameter {1}").format(
                            self.MSG_VULN, parameter)
                    log_message = Messages.MSG_PARAM_INJECT

                await self.add_vuln_critical(request_id=request.path_id,
                                             category=NAME,
                                             request=mutated_request,
                                             info=vuln_message,
                                             parameter=parameter,
                                             wstg=WSTG_CODE)

                log_red("---")
                log_red(log_message, self.MSG_VULN, page, parameter)
                log_red(Messages.MSG_EVIL_REQUEST)
                log_red(mutated_request.http_repr())
                log_red("---")

                # We reached maximum exploitation for this parameter, don't send more payloads
                vulnerable_parameter = True
                continue
            except RequestError:
                self.network_errors += 1
                continue
            else:
                if response.is_server_error and not saw_internal_error:
                    saw_internal_error = True
                    if parameter == "QUERY_STRING":
                        anom_msg = Messages.MSG_QS_500
                    else:
                        anom_msg = Messages.MSG_PARAM_500.format(parameter)

                    await self.add_anom_high(request_id=request.path_id,
                                             category=Messages.ERROR_500,
                                             request=mutated_request,
                                             info=anom_msg,
                                             parameter=parameter,
                                             wstg=INTERNAL_ERROR_WSTG_CODE)

                    log_orange("---")
                    log_orange(Messages.MSG_500, page)
                    log_orange(Messages.MSG_EVIL_REQUEST)
                    log_orange(mutated_request.http_repr())
                    log_orange("---")
Пример #8
0
    async def attack(self, request: Request):
        warned = False
        timeouted = False
        page = request.path
        saw_internal_error = False
        current_parameter = None
        vulnerable_parameter = False

        for mutated_request, parameter, payload, flags in self.mutator.mutate(
                request):
            if current_parameter != parameter:
                # Forget what we know about current parameter
                current_parameter = parameter
                vulnerable_parameter = False
            elif vulnerable_parameter:
                # If parameter is vulnerable, just skip till next parameter
                continue

            log_verbose(f"[¨] {mutated_request}")

            try:
                response = await self.crawler.async_send(mutated_request)
            except ReadTimeout:
                self.network_errors += 1
                if timeouted:
                    continue

                log_orange("---")
                log_orange(Messages.MSG_TIMEOUT, page)
                log_orange(Messages.MSG_EVIL_REQUEST)
                log_orange(mutated_request.http_repr())
                log_orange("---")

                if parameter == "QUERY_STRING":
                    anom_msg = Messages.MSG_QS_TIMEOUT
                else:
                    anom_msg = Messages.MSG_PARAM_TIMEOUT.format(parameter)

                await self.add_anom_medium(request_id=request.path_id,
                                           category=Messages.RES_CONSUMPTION,
                                           request=mutated_request,
                                           info=anom_msg,
                                           parameter=parameter,
                                           wstg=RESOURCE_CONSUMPTION_WSTG_CODE)
                timeouted = True
            except RequestError:
                self.network_errors += 1
                continue
            else:
                file_warning = None
                # original_payload = self.payload_to_rules[flags.section]
                for rule in self.payload_to_rules[flags.section]:
                    if rule in response.content:
                        found_pattern = rule
                        vulnerable_method = self.rules_to_messages[rule]
                        inclusion_succeed = True
                        break
                else:
                    # No successful inclusion or directory traversal but perhaps we can control something
                    inclusion_succeed = False
                    file_warning = find_warning_message(
                        response.content, payload)
                    if file_warning:
                        found_pattern = file_warning.pattern
                        vulnerable_method = file_warning.function
                    else:
                        found_pattern = vulnerable_method = None

                if found_pattern:
                    # Interesting pattern found, either inclusion or error message
                    if await self.is_false_positive(request, found_pattern):
                        continue

                    if not inclusion_succeed:
                        if warned:
                            # No need to warn more than once
                            continue

                        # Mark as eventuality
                        vulnerable_method = _("Possible {0} vulnerability"
                                              ).format(vulnerable_method)
                        warned = True

                    # An error message implies that a vulnerability may exists
                    if parameter == "QUERY_STRING":
                        vuln_message = Messages.MSG_QS_INJECT.format(
                            vulnerable_method, page)
                    else:
                        vuln_message = _(
                            "{0} via injection in the parameter {1}").format(
                                vulnerable_method, parameter)

                    constraint_message = ""
                    if file_warning and file_warning.uri:
                        constraints = has_prefix_or_suffix(
                            payload, file_warning.uri)
                        if constraints:
                            constraint_message += _("Constraints: {}").format(
                                ", ".join(constraints))
                            vuln_message += " (" + constraint_message + ")"

                    await self.add_vuln_critical(request_id=request.path_id,
                                                 category=NAME,
                                                 request=mutated_request,
                                                 info=vuln_message,
                                                 parameter=parameter,
                                                 wstg=WSTG_CODE)

                    log_red("---")
                    log_red(
                        Messages.MSG_QS_INJECT if parameter == "QUERY_STRING"
                        else Messages.MSG_PARAM_INJECT, vulnerable_method,
                        page, parameter)

                    if constraint_message:
                        log_red(constraint_message)

                    log_red(Messages.MSG_EVIL_REQUEST)
                    log_red(mutated_request.http_repr())
                    log_red("---")

                    if inclusion_succeed:
                        # We reached maximum exploitation for this parameter, don't send more payloads
                        vulnerable_parameter = True
                        continue

                elif response.is_server_error and not saw_internal_error:
                    saw_internal_error = True
                    if parameter == "QUERY_STRING":
                        anom_msg = Messages.MSG_QS_500
                    else:
                        anom_msg = Messages.MSG_PARAM_500.format(parameter)

                    await self.add_anom_high(request_id=request.path_id,
                                             category=Messages.ERROR_500,
                                             request=mutated_request,
                                             info=anom_msg,
                                             parameter=parameter,
                                             wstg=INTERNAL_ERROR_WSTG_CODE)

                    log_orange("---")
                    log_orange(Messages.MSG_500, page)
                    log_orange(Messages.MSG_EVIL_REQUEST)
                    log_orange(mutated_request.http_repr())
                    log_orange("---")
Пример #9
0
def process_certificate_info(certinfo_result):
    for cert_deployment in certinfo_result.certificate_deployments:

        leaf_certificate = cert_deployment.received_certificate_chain[0]
        message = _("Certificate subject: {0}").format(
            get_common_name(leaf_certificate.subject))
        log_blue(message)
        yield INFO_LEVEL, message

        message = _("Alt. names: {0}").format(
            extract_dns_subject_alternative_names(leaf_certificate))
        log_blue(message)
        yield INFO_LEVEL, message

        message = _("Issuer: {0}").format(
            get_common_name(leaf_certificate.issuer))
        log_blue(message)
        yield INFO_LEVEL, message

        if not cert_deployment.leaf_certificate_subject_matches_hostname:
            message = _(
                "Requested hostname doesn't match those in the certificate")
            log_red(message)
            yield CRITICAL_LEVEL, message

        if not cert_deployment.received_chain_has_valid_order:
            message = _("Certificate chain is in invalid order")
            log_orange(message)
            yield MEDIUM_LEVEL, message

        public_key = leaf_certificate.public_key()

        if isinstance(public_key, EllipticCurvePublicKey):
            key_size = public_key.curve.key_size
        else:
            key_size = public_key.key_size

        if public_key.__class__.__name__ == "_RSAPublicKey":
            algorithm = "RSA"
        elif public_key.__class__.__name__ == "_EllipticCurvePublicKey":
            algorithm = "ECC"
        else:
            algorithm = public_key.__class__.__name__

        message = _("Key: {0} {1} bits").format(algorithm, key_size)
        log_blue(message)
        yield INFO_LEVEL, message

        message = _("Signature Algorithm: {0}").format(
            leaf_certificate.signature_hash_algorithm.name)
        log_blue(message)
        yield INFO_LEVEL, message

        # print(f"Valid from {leaf_certificate.not_valid_before} to {leaf_certificate.not_valid_after}")
        if leaf_certificate.not_valid_after > datetime.utcnow():
            # We should add a method for humanize inside our language package
            # _t = humanize.i18n.activate("fr_FR")
            message = _("Certificate expires in ") + \
                      humanize.precisedelta(leaf_certificate.not_valid_after - datetime.utcnow())
            log_green(message)
            yield INFO_LEVEL, message
        else:
            message = _("Certificate has expired at"
                        ) + f" {leaf_certificate.not_valid_after}"
            log_red(message)
            yield CRITICAL_LEVEL, message

        if not cert_deployment.leaf_certificate_is_ev:
            message = _("Certificate doesn't use Extended Validation")
            log_orange(message)
            yield MEDIUM_LEVEL, message

        # https://en.wikipedia.org/wiki/OCSP_stapling
        if not cert_deployment.leaf_certificate_has_must_staple_extension:
            message = _("OCSP Must-Staple extension is missing")
            log_orange(message)
            yield MEDIUM_LEVEL, message

        if cert_deployment.leaf_certificate_signed_certificate_timestamps_count is None:
            message = _("Certificate transparency:") + " " + _(
                "Unknown (Your OpenSSL version is not recent enough)")
            log_orange(message)
            yield MEDIUM_LEVEL, message
        elif cert_deployment.leaf_certificate_signed_certificate_timestamps_count:
            message = _("Certificate transparency:") + " " + _("Yes") + \
                      f" ({cert_deployment.leaf_certificate_signed_certificate_timestamps_count} SCT)"
            log_green(message)
            yield INFO_LEVEL, message
        else:
            message = _("Certificate transparency:") + " " + _("No")
            log_red(message)
            yield HIGH_LEVEL, message

        if cert_deployment.verified_chain_has_sha1_signature:
            message = _(
                "One of the certificate in the chain is signed using SHA-1")
            log_red(message)
            yield HIGH_LEVEL, message

        for validation_result in cert_deployment.path_validation_results:
            if not validation_result.was_validation_successful:
                message = _(
                    "Certificate is invalid for {} trust store: {}").format(
                        validation_result.trust_store.name,
                        validation_result.openssl_error_string)
                log_red(message)
                yield CRITICAL_LEVEL, message

        # Currently we stop at the first certificate of the server, maybe improve later
        # Right now several certificates generates too much confusion in report
        break
Пример #10
0
def analyze(hostname: str, port: int) -> List[Tuple[int, str]]:
    results = []
    # Define the server that you want to scan
    try:
        server_location = ServerNetworkLocation(hostname, port)
    except ServerHostnameCouldNotBeResolved:
        log_red(_("Could not resolve {0}"), hostname)
        return results

    # Then queue some scan commands for the server
    scanner = Scanner()
    server_scan_req = ServerScanRequest(
        server_location=server_location,
        scan_commands={
            ScanCommand.CERTIFICATE_INFO, ScanCommand.SSL_2_0_CIPHER_SUITES,
            ScanCommand.SSL_3_0_CIPHER_SUITES,
            ScanCommand.TLS_1_0_CIPHER_SUITES,
            ScanCommand.TLS_1_1_CIPHER_SUITES,
            ScanCommand.TLS_1_2_CIPHER_SUITES,
            ScanCommand.TLS_1_3_CIPHER_SUITES, ScanCommand.ROBOT,
            ScanCommand.HEARTBLEED, ScanCommand.TLS_COMPRESSION,
            ScanCommand.TLS_FALLBACK_SCSV, ScanCommand.TLS_1_3_EARLY_DATA,
            ScanCommand.OPENSSL_CCS_INJECTION,
            ScanCommand.SESSION_RENEGOTIATION, ScanCommand.HTTP_HEADERS
        },
        network_configuration=ServerNetworkConfiguration(
            tls_server_name_indication=server_location.hostname,
            network_timeout=5,
            network_max_retries=2))
    scanner.queue_scans([server_scan_req])

    # TLS 1.2 / 1.3 results
    good_protocols = {
        ScanCommand.TLS_1_2_CIPHER_SUITES: "TLS v1.2",
        ScanCommand.TLS_1_3_CIPHER_SUITES: "TLS v1.3"
    }

    # https://blog.mozilla.org/security/2014/10/14/the-poodle-attack-and-the-end-of-ssl-3-0/
    # https://blog.qualys.com/product-tech/2018/11/19/grade-change-for-tls-1-0-and-tls-1-1-protocols
    bad_protocols = {
        ScanCommand.SSL_2_0_CIPHER_SUITES: "SSL v2",
        ScanCommand.SSL_3_0_CIPHER_SUITES: "SSL v3",
        ScanCommand.TLS_1_0_CIPHER_SUITES: "TLS v1.0",
        ScanCommand.TLS_1_1_CIPHER_SUITES: "TLS v1.1"
    }

    # Then retrieve the results
    for result in scanner.get_results():
        log_blue("\n" + _("Results for") +
                 f" {result.server_location.hostname}:")
        deprecated_protocols = []

        if result.connectivity_error_trace:
            # Stuff like connection timeout
            log_red(result.connectivity_error_trace)
            continue

        for scan_command in result.scan_result.__annotations__:
            scan_results = getattr(result.scan_result, scan_command)

            if scan_results.error_reason:
                log_red(scan_results.error_reason)
                continue

            if scan_results.status != ScanCommandAttemptStatusEnum.COMPLETED:
                continue

            if scan_command == ScanCommand.CERTIFICATE_INFO:
                for level, message in process_certificate_info(
                        scan_results.result):
                    results.append((level, message))
            elif scan_command in bad_protocols:
                if scan_results.result.accepted_cipher_suites:
                    deprecated_protocols.append(bad_protocols[scan_command])
            elif scan_command == ScanCommand.ROBOT:
                if scan_results.result.robot_result in (
                        RobotScanResultEnum.VULNERABLE_WEAK_ORACLE,
                        RobotScanResultEnum.VULNERABLE_STRONG_ORACLE):
                    message = _("Server is vulnerable to ROBOT attack")
                    log_red(message)
                    results.append((CRITICAL_LEVEL, message))
            elif scan_command == ScanCommand.HEARTBLEED:
                if scan_results.result.is_vulnerable_to_heartbleed:
                    message = _("Server is vulnerable to Heartbleed attack")
                    log_red(message)
                    results.append((CRITICAL_LEVEL, message))
            elif scan_command == ScanCommand.TLS_COMPRESSION:
                if scan_results.result.supports_compression:
                    message = _(
                        "Server is vulnerable to CRIME attack (compression is supported)"
                    )
                    log_red(message)
                    results.append((CRITICAL_LEVEL, message))
            elif scan_command == ScanCommand.TLS_FALLBACK_SCSV:
                if not scan_results.result.supports_fallback_scsv:
                    message = _(
                        "Server is vulnerable to downgrade attacks (support for TLS_FALLBACK_SCSV is missing)"
                    )
                    log_red(message)
                    results.append((CRITICAL_LEVEL, message))
            elif scan_command == ScanCommand.TLS_1_3_EARLY_DATA:
                # https://blog.trailofbits.com/2019/03/25/what-application-developers-need-to-know-about-tls-early-data-0rtt/
                if scan_results.result.supports_early_data:
                    message = _(
                        "TLS 1.3 Early Data (0RTT) is vulnerable to replay attacks"
                    )
                    log_orange(message)
                    results.append((MEDIUM_LEVEL, message))
            elif scan_command == ScanCommand.OPENSSL_CCS_INJECTION:
                if scan_results.result.is_vulnerable_to_ccs_injection:
                    message = _(
                        "Server is vulnerable to OpenSSL CCS (CVE-2014-0224)")
                    log_red(message)
                    results.append((CRITICAL_LEVEL, message))
            elif scan_command == ScanCommand.SESSION_RENEGOTIATION:
                if scan_results.result.is_vulnerable_to_client_renegotiation_dos:
                    message = _(
                        "Server honors client-initiated renegotiations (vulnerable to DoS attacks)"
                    )
                    log_red(message)
                    results.append((HIGH_LEVEL, message))
                if not scan_results.result.supports_secure_renegotiation:
                    message = _("Server doesn't support secure renegotiations")
                    log_orange(message)
                    results.append((MEDIUM_LEVEL, message))
            elif scan_command == ScanCommand.HTTP_HEADERS:
                if scan_results.result.strict_transport_security_header is None:
                    message = _("Strict Transport Security (HSTS) is not set")
                    log_red(message)
                    results.append((HIGH_LEVEL, message))
            elif scan_command in good_protocols:
                for level, message in process_cipher_suites(
                        scan_results.result, good_protocols[scan_command]):
                    results.append((level, message))

        if deprecated_protocols:
            message = _("The following protocols are deprecated and/or insecure and should be deactivated:") + \
                      " " + ", ".join(deprecated_protocols)
            log_red(message)
            results.append((CRITICAL_LEVEL, message))

    return results
Пример #11
0
    async def attempt_exploit(self, method, payloads, original_request,
                              parameter, taint):
        timeouted = False
        page = original_request.path
        saw_internal_error = False

        attack_mutator = Mutator(methods=method,
                                 payloads=payloads,
                                 qs_inject=self.must_attack_query_string,
                                 parameters=[parameter],
                                 skip=self.options.get("skipped_parameters"))

        for evil_request, xss_param, xss_payload, xss_flags in attack_mutator.mutate(
                original_request):
            log_verbose(f"[¨] {evil_request}")

            try:
                response = await self.crawler.async_send(evil_request)
            except ReadTimeout:
                self.network_errors += 1
                if timeouted:
                    continue

                log_orange("---")
                log_orange(Messages.MSG_TIMEOUT, page)
                log_orange(Messages.MSG_EVIL_REQUEST)
                log_orange(evil_request.http_repr())
                log_orange("---")

                if xss_param == "QUERY_STRING":
                    anom_msg = Messages.MSG_QS_TIMEOUT
                else:
                    anom_msg = Messages.MSG_PARAM_TIMEOUT.format(xss_param)

                await self.add_anom_medium(request_id=original_request.path_id,
                                           category=Messages.RES_CONSUMPTION,
                                           request=evil_request,
                                           info=anom_msg,
                                           parameter=xss_param,
                                           wstg=RESOURCE_CONSUMPTION_WSTG_CODE)
                timeouted = True
            except RequestError:
                self.network_errors += 1
            else:
                if (not response.is_redirect
                        and valid_xss_content_type(evil_request)
                        and check_payload(self.DATA_DIR, self.PAYLOADS_FILE,
                                          self.external_endpoint,
                                          self.proto_endpoint, response,
                                          xss_flags, taint)):
                    self.successful_xss[taint] = (xss_payload, xss_flags)
                    message = _(
                        "XSS vulnerability found via injection in the parameter {0}"
                    ).format(xss_param)
                    if has_strong_csp(response):
                        message += ".\n" + _(
                            "Warning: Content-Security-Policy is present!")

                    await self.add_vuln_medium(
                        request_id=original_request.path_id,
                        category=NAME,
                        request=evil_request,
                        parameter=xss_param,
                        info=message,
                        wstg=WSTG_CODE)

                    if xss_param == "QUERY_STRING":
                        injection_msg = Messages.MSG_QS_INJECT
                    else:
                        injection_msg = Messages.MSG_PARAM_INJECT

                    log_red("---")
                    log_red(injection_msg, self.MSG_VULN, page, xss_param)

                    if has_strong_csp(response):
                        log_red(
                            _("Warning: Content-Security-Policy is present!"))

                    log_red(Messages.MSG_EVIL_REQUEST)
                    log_red(evil_request.http_repr())
                    log_red("---")

                    # stop trying payloads and jump to the next parameter
                    break

                if response.is_server_error and not saw_internal_error:
                    if xss_param == "QUERY_STRING":
                        anom_msg = Messages.MSG_QS_500
                    else:
                        anom_msg = Messages.MSG_PARAM_500.format(xss_param)

                    await self.add_anom_high(
                        request_id=original_request.path_id,
                        category=Messages.ERROR_500,
                        request=evil_request,
                        info=anom_msg,
                        parameter=xss_param,
                        wstg=INTERNAL_ERROR_WSTG_CODE)

                    log_orange("---")
                    log_orange(Messages.MSG_500, page)
                    log_orange(Messages.MSG_EVIL_REQUEST)
                    log_orange(evil_request.http_repr())
                    log_orange("---")
                    saw_internal_error = True