示例#1
0
    async def attack(self, request: Request):
        page = request.path

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

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

            if any(
                    url.startswith("https://openbugbounty.org/")
                    for url in response.all_redirections):
                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):
        url = request.path
        self.attacked_get.append(url)

        # We can't see anything by printing requests because payload is in headers so let's print nothing :)
        evil_req = Request(url)

        try:
            resp = await self.crawler.async_send(evil_req, headers=self.hdrs)
        except RequestError:
            self.network_errors += 1
            return

        if resp:
            data = resp.content
            if self.rand_string in data:
                log_red(
                    _("URL {0} seems vulnerable to Shellshock attack!").format(
                        url))

                await self.add_vuln_high(
                    request_id=request.path_id,
                    category=NAME,
                    request=evil_req,
                    info=_("URL {0} seems vulnerable to Shellshock attack"
                           ).format(url),
                    wstg=WSTG_CODE)
示例#3
0
文件: mod_csp.py 项目: devl00p/wapiti
    async def attack(self, request: Request):
        self.finished = True
        request_to_root = Request(request.url)

        try:
            response = await self.crawler.async_get(request_to_root,
                                                    follow_redirects=True)
        except RequestError:
            self.network_errors += 1
            return

        if "Content-Security-Policy" not in response.headers:
            log_red(MSG_NO_CSP)
            await self.add_vuln_low(category=NAME,
                                    request=request_to_root,
                                    info=MSG_NO_CSP,
                                    wstg=WSTG_CODE)
        else:
            csp_dict = csp_header_to_dict(
                response.headers["Content-Security-Policy"])

            for policy_name in CSP_CHECK_LISTS:
                result = check_policy_values(policy_name, csp_dict)

                if result <= 0:
                    if result == -1:
                        info = MSG_CSP_MISSING.format(policy_name)
                    else:  # result == 0
                        info = MSG_CSP_UNSAFE.format(policy_name)

                    log_red(info)
                    await self.add_vuln_low(category=NAME,
                                            request=request_to_root,
                                            info=info,
                                            wstg=WSTG_CODE)
示例#4
0
 async def check_header(self, response: Page, request: Request, header: str,
                        check_list: List[str], info: str, log: str,
                        wstg: str):
     log_blue(log)
     if not self.is_set(response, header, check_list):
         log_red(info)
         await self.add_vuln_low(category=NAME,
                                 request=request,
                                 info=info,
                                 wstg=wstg)
     else:
         log_green("OK")
示例#5
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("---")
示例#6
0
文件: mod_csrf.py 项目: jinxka/wapiti
    async def attack(self, request: Request):
        csrf_value = self.is_csrf_present(request)

        # check if token is present
        if not csrf_value:
            vuln_message = _("Lack of anti CSRF token")
        elif not await self.is_csrf_verified(request):
            vuln_message = _(
                "CSRF token '{}' is not properly checked in backend").format(
                    self.csrf_string)
        elif not self.is_csrf_robust(csrf_value):
            vuln_message = _(
                "CSRF token '{}' might be easy to predict").format(
                    self.csrf_string)
        else:
            return

        self.already_vulnerable.append((request.url, request.post_keys))

        log_red("---")
        log_red(vuln_message)
        log_red(request.http_repr())
        log_red("---")

        await self.add_vuln_medium(request_id=request.path_id,
                                   category=NAME,
                                   request=request,
                                   info=vuln_message,
                                   wstg=WSTG_CODE)
示例#7
0
    async def attack(self, request: Request):
        page = request.path

        for payload, __ in self.payloads:
            if self._stop_event.is_set():
                break

            if request.file_name:
                if "[FILE_" not in payload:
                    continue

                payload = payload.replace("[FILE_NAME]", request.file_name)
                payload = payload.replace("[FILE_NOEXT]", splitext(request.file_name)[0])
                url = page.replace(request.file_name, payload)
            else:
                if "[FILE_" in payload:
                    continue

                url = urljoin(request.path, payload)

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

            self.attacked_get.append(page)
            evil_req = Request(url)

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

            if response and response.status == 200:
                # FIXME: Right now we cannot remove the pylint: disable line because the current I18N system
                # uses the string as a token so we cannot use f string
                # pylint: disable=consider-using-f-string
                log_red(_("Found backup file {}".format(evil_req.url)))

                await self.add_vuln_low(
                    request_id=request.path_id,
                    category=NAME,
                    request=evil_req,
                    info=_("Backup file {0} found for {1}").format(url, page),
                    wstg=WSTG_CODE
                )
示例#8
0
文件: mod_xxe.py 项目: jinxka/wapiti
    async def attack_upload(self, original_request):
        mutator = FileMutator(payloads=self.payloads)
        current_parameter = None
        vulnerable_parameter = False

        for mutated_request, parameter, _payload, flags in mutator.mutate(
                original_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:
                pattern = search_pattern(response.content,
                                         self.flag_to_patterns(flags))
                if pattern and not await self.false_positive(
                        original_request, pattern):
                    await self.add_vuln_high(
                        request_id=original_request.path_id,
                        category=NAME,
                        request=mutated_request,
                        info="XXE vulnerability leading to file disclosure",
                        parameter=parameter,
                        wstg=WSTG_CODE)

                    log_red("---")
                    log_red(Messages.MSG_PARAM_INJECT, self.MSG_VULN,
                            original_request.url, parameter)
                    log_red(Messages.MSG_EVIL_REQUEST)
                    log_red(mutated_request.http_repr())
                    log_red("---")
                    vulnerable_parameter = True
                    self.vulnerables.add(original_request.path_id)
示例#9
0
    async def worker(self, queue: asyncio.Queue, resolvers: Iterator[str],
                     root_domain: str, bad_responses: Set[str]):
        while True:
            try:
                domain = queue.get_nowait().strip()
            except asyncio.QueueEmpty:
                await asyncio.sleep(.05)
            else:
                queue.task_done()
                if domain == "__exit__":
                    break

                try:
                    resolver = dns.asyncresolver.Resolver()
                    resolver.timeout = 10.
                    resolver.nameservers = [
                        next(resolvers) for __ in range(10)
                    ]
                    answers = await resolver.resolve(domain,
                                                     'CNAME',
                                                     raise_on_no_answer=False)
                except (socket.gaierror, UnicodeError):
                    continue
                except (dns.asyncresolver.NXDOMAIN, dns.exception.Timeout):
                    continue
                except (dns.name.EmptyLabel,
                        dns.resolver.NoNameservers) as exception:
                    logging.warning(f"{domain}: {exception}")
                    continue

                for answer in answers:
                    cname = answer.to_text().strip(".")

                    if cname in bad_responses:
                        continue

                    log_verbose(_(f"Record {domain} points to {cname}"))

                    try:
                        if get_root_domain(cname) == root_domain:
                            # If it is an internal CNAME (like www.target.tld to target.tld) just ignore
                            continue
                    except (TldDomainNotFound, TldBadUrl):
                        logging.warning(f"{cname} is not a valid domain name")
                        continue

                    if await self.takeover.check(domain, cname):
                        log_red("---")
                        log_red(
                            _(f"CNAME {domain} to {cname} seems vulnerable to takeover"
                              ))
                        log_red("---")

                        await self.add_vuln_high(
                            category=NAME,
                            info=
                            _(f"CNAME {domain} to {cname} seems vulnerable to takeover"
                              ),
                            request=Request(f"https://{domain}/"),
                            wstg=WSTG_CODE)
示例#10
0
文件: mod_xxe.py 项目: jinxka/wapiti
    async def attack_body(self, original_request):
        for payload, tags in self.payloads:
            payload = payload.replace("[PATH_ID]",
                                      str(original_request.path_id))
            payload = payload.replace("[PARAM_AS_HEX]",
                                      "72617720626f6479")  # raw body
            mutated_request = Request(original_request.url,
                                      method="POST",
                                      enctype="text/xml",
                                      post_params=payload)

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

            try:
                response = await self.crawler.async_send(mutated_request)
            except RequestError:
                self.network_errors += 1
                continue
            else:
                pattern = search_pattern(response.content,
                                         self.flag_to_patterns(tags))
                if pattern and not await self.false_positive(
                        original_request, pattern):
                    await self.add_vuln_high(
                        request_id=original_request.path_id,
                        category=NAME,
                        request=mutated_request,
                        info="XXE vulnerability leading to file disclosure",
                        parameter="raw body",
                        wstg=WSTG_CODE)

                    log_red("---")
                    log_red("{0} in {1} leading to file disclosure",
                            self.MSG_VULN, original_request.url)
                    log_red(Messages.MSG_EVIL_REQUEST)
                    log_red(mutated_request.http_repr())
                    log_red("---")
                    self.vulnerables.add(original_request.path_id)
                    break
示例#11
0
    async def attack(self, request: Request):
        self.finished = True
        cookies = self.crawler.session_cookies

        for cookie in cookies.jar:
            log_blue(_("Checking cookie : {}").format(cookie.name))
            if not self.check_httponly_flag(cookie):
                log_red(INFO_COOKIE_HTTPONLY.format(cookie.name))
                await self.add_vuln_low(
                    category=COOKIE_HTTPONLY_DISABLED,
                    request=request,
                    info=INFO_COOKIE_HTTPONLY.format(cookie.name),
                    wstg=COOKIE_HTTPONLY_WSTG
                )

            if not self.check_secure_flag(cookie):
                log_red(INFO_COOKIE_SECURE.format(cookie.name))
                await self.add_vuln_low(
                    category=COOKIE_SECURE_DISABLED,
                    request=request,
                    info=INFO_COOKIE_SECURE.format(cookie.name),
                    wstg=COOKIE_SECURE_WSTG
                )
示例#12
0
    async def _verify_url_vulnerability(self, request: Request,
                                        param_uuid: uuid.UUID):
        if not await self._verify_dns(str(param_uuid)):
            return

        await self.add_vuln_critical(
            category=NAME,
            request=request,
            info=_("URL {0} seems vulnerable to Log4Shell attack") \
                .format(request.url),
            parameter="",
            wstg=WSTG_CODE
        )

        log_red("---")
        log_red(_("URL {0} seems vulnerable to Log4Shell attack"), request.url)
        log_red(request.http_repr())
        log_red("---")
示例#13
0
    async def _verify_header_vulnerability(self, modified_request: Request,
                                           header: str, payload: str,
                                           unique_id: uuid.UUID):
        if await self._verify_dns(str(unique_id)) is True:
            await self.add_vuln_critical(
                category=NAME,
                request=modified_request,
                info=_("URL {0} seems vulnerable to Log4Shell attack by using the {1} {2}") \
                    .format(modified_request.url, "header", header),
                parameter=f"{header}: {payload}",
                wstg=WSTG_CODE
            )

            log_red("---")
            log_red(
                _("URL {0} seems vulnerable to Log4Shell attack by using the {1} {2}"
                  ), modified_request.url, "header", header)
            log_red(modified_request.http_repr())
            log_red("---")
示例#14
0
    async def _verify_param_vulnerability(self, request: Request,
                                          param_uuid: uuid.UUID,
                                          param_name: str):
        if not await self._verify_dns(str(param_uuid)):
            return
        element_type = "query parameter" if request.method == "GET" else "body parameter"

        await self.add_vuln_critical(
            category=NAME,
            request=request,
            info=_("URL {0} seems vulnerable to Log4Shell attack by using the {1} {2}") \
                .format(request.url, element_type, param_name),
            parameter=f"{param_name}",
            wstg=WSTG_CODE
        )

        log_red("---")
        log_red(
            _("URL {0} seems vulnerable to Log4Shell attack by using the {1} {2}"
              ), request.url, element_type, param_name)
        log_red(request.http_repr())
        log_red("---")
示例#15
0
    async def attack(self, request: Request):
        url = request.path
        referer = request.referer
        headers = {}
        if referer:
            headers["referer"] = referer

        evil_req = Request(url, method="ABC")
        try:
            response = await self.crawler.async_send(evil_req, headers=headers)
        except RequestError:
            self.network_errors += 1
            return

        if response.status == 404 or response.status < 400 or response.status >= 500:
            # Every 4xx status should be uninteresting (specially bad request in our case)

            unblocked_content = response.content

            log_red("---")
            await self.add_vuln_medium(
                request_id=request.path_id,
                category=NAME,
                request=evil_req,
                info=_("{0} bypassable weak restriction").format(evil_req.url),
                wstg=WSTG_CODE)
            log_red(_("Weak restriction bypass vulnerability: {0}"),
                    evil_req.url)
            log_red(
                _("HTTP status code changed from {0} to {1}").format(
                    request.status, response.status))

            log_verbose(_("Source code:"))
            log_verbose(unblocked_content)
            log_red("---")

        self.attacked_get.append(url)
示例#16
0
    async def check_path(self, url):
        page = Request(url)
        try:
            response = await self.crawler.async_send(page)
        except RequestError:
            self.network_errors += 1
            return False

        if response.redirection_url:
            loc = response.redirection_url
            if response.is_directory_redirection:
                log_red(f"Found webpage {loc}")
                self.new_resources.append(loc)
            else:
                log_red(f"Found webpage {page.path}")
                self.new_resources.append(page.path)
            return True

        if response.status not in [403, 404, 429]:
            log_red(f"Found webpage {page.path}")
            self.new_resources.append(page.path)
            return True

        return False
示例#17
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
示例#18
0
    async def attack(self, request: Request):
        try:
            page = await self.crawler.async_get(Request(request.referer),
                                                follow_redirects=True)
        except RequestError:
            self.network_errors += 1
            return

        login_form, username_field_idx, password_field_idx = page.find_login_form(
        )
        if not login_form:
            return

        try:
            failure_text = await self.send_credentials(login_form,
                                                       username_field_idx,
                                                       password_field_idx,
                                                       "invalid", "invalid")

            if self.check_success_auth(failure_text):
                # Ignore this case as it raises false positives
                return
        except RequestError:
            self.network_errors += 1
            return

        tasks = set()
        pending_count = 0
        found = False

        creds_iterator = product(self.get_usernames(), self.get_passwords())
        while True:
            if pending_count < self.options[
                    "tasks"] and not self._stop_event.is_set() and not found:
                try:
                    username, password = next(creds_iterator)
                except StopIteration:
                    pass
                else:
                    task = asyncio.create_task(
                        self.test_credentials(login_form, username_field_idx,
                                              password_field_idx, username,
                                              password, failure_text))
                    tasks.add(task)

            if not tasks:
                break

            done_tasks, pending_tasks = await asyncio.wait(
                tasks, timeout=0.01, return_when=asyncio.FIRST_COMPLETED)
            pending_count = len(pending_tasks)
            for task in done_tasks:
                try:
                    result = await task
                except RequestError:
                    self.network_errors += 1
                else:
                    if result:
                        found = True
                        username, password = result
                        vuln_message = _(
                            "Credentials found for URL {} : {} / {}").format(
                                request.referer, username, password)

                        # Recreate the request that succeed in order to print and store it
                        post_params = login_form.post_params
                        get_params = login_form.get_params

                        if login_form.method == "POST":
                            post_params[username_field_idx][1] = username
                            post_params[password_field_idx][1] = password
                        else:
                            get_params[username_field_idx][1] = username
                            get_params[password_field_idx][1] = password

                        evil_request = Request(
                            path=login_form.url,
                            method=login_form.method,
                            post_params=post_params,
                            get_params=get_params,
                            referer=login_form.referer,
                            link_depth=login_form.link_depth)

                        await self.add_vuln_low(request_id=request.path_id,
                                                category=NAME,
                                                request=evil_request,
                                                info=vuln_message,
                                                wstg=WSTG_CODE)

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

                tasks.remove(task)

            if self._stop_event.is_set() or found:
                # If we found valid credentials we need to stop pending tasks as they may generate false positives
                # because the session is opened on the website and next attempts may appear as logged in
                for task in pending_tasks:
                    task.cancel()
                    tasks.remove(task)
示例#19
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
示例#20
0
文件: mod_ssrf.py 项目: jinxka/wapiti
    async def finish(self):
        endpoint_url = f"{self.internal_endpoint}get_ssrf.php?session_id={self._session_id}"
        logging.info(_("[*] Asking endpoint URL {} for results, please wait...").format(endpoint_url))
        await sleep(2)
        # A la fin des attaques on questionne le endpoint pour savoir s'il a été contacté
        endpoint_request = Request(endpoint_url)
        try:
            response = await self.crawler.async_send(endpoint_request)
        except RequestError:
            self.network_errors += 1
            logging.error(_("[!] Unable to request endpoint URL '{}'").format(self.internal_endpoint))
        else:
            data = response.json
            if isinstance(data, dict):
                for request_id in data:
                    original_request = await self.persister.get_path_by_id(request_id)
                    if original_request is None:
                        raise ValueError("Could not find the original request with that ID")

                    page = original_request.path
                    for hex_param in data[request_id]:
                        parameter = unhexlify(hex_param).decode("utf-8")

                        for infos in data[request_id][hex_param]:
                            request_url = infos["url"]
                            # Date in ISO format
                            request_date = infos["date"]
                            request_ip = infos["ip"]
                            request_method = infos["method"]
                            # request_size = infos["size"]

                            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}.\n"
                                    "The target performed an outgoing HTTP {2} request at {3} with IP {4}.\n"
                                    "Full request can be seen at {5}"
                                ).format(
                                    self.MSG_VULN,
                                    parameter,
                                    request_method,
                                    request_date,
                                    request_ip,
                                    request_url
                                )

                            mutator = Mutator(
                                methods="G" if original_request.method == "GET" else "PF",
                                payloads=[("http://external.url/page", Flags())],
                                qs_inject=self.must_attack_query_string,
                                parameters=[parameter],
                                skip=self.options.get("skipped_parameters")
                            )

                            mutated_request, __, __, __ = next(mutator.mutate(original_request))

                            await self.add_vuln_critical(
                                request_id=original_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("---")
示例#21
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("---")
示例#22
0
文件: mod_xxe.py 项目: jinxka/wapiti
    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("---")
示例#23
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("---")
示例#24
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
示例#25
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
示例#26
0
    async def attack(self, request: Request):
        """This method searches XSS which could be permanently stored in the web application"""
        url = request.url
        target_req = Request(url)
        referer = request.referer
        headers = {}

        if referer:
            headers["referer"] = referer

        try:
            response = await self.crawler.async_send(target_req,
                                                     headers=headers)
            data = response.content
        except RequestError:
            self.network_errors += 1
            return

        # Should we look for taint codes sent with GET in the webpages?
        # Exploiting those may imply sending more GET requests

        # Search in the page source for every taint code used by mod_xss
        for taint in self.tried_xss:
            input_request = self.tried_xss[taint][0]

            # Such situations should not occur as it would be stupid to block POST (or GET) requests for mod_xss
            # and not mod_permanentxss, but it is possible so let's filter that.
            if not self.do_get and input_request.method == "GET":
                continue

            if not self.do_post and input_request.method == "POST":
                continue

            if taint.lower() in data.lower():
                # Code found in the webpage !
                # Did mod_xss saw this as a reflected XSS ?
                if taint in self.successful_xss:
                    # Yes, it means XSS payloads were injected, not just tainted code.
                    payload, flags = self.successful_xss[taint]

                    if check_payload(self.DATA_DIR, self.PAYLOADS_FILE,
                                     self.external_endpoint,
                                     self.proto_endpoint, response, flags,
                                     taint):
                        # If we can find the payload again, this is in fact a stored XSS
                        get_params = input_request.get_params
                        post_params = input_request.post_params
                        file_params = input_request.file_params
                        referer = input_request.referer

                        # The following trick may seems dirty but it allows to treat GET and POST requests
                        # the same way.
                        for params_list in [
                                get_params, post_params, file_params
                        ]:
                            for i, __ in enumerate(params_list):
                                parameter, value = params_list[i]
                                parameter = quote(parameter)
                                if value != taint:
                                    continue

                                if params_list is file_params:
                                    params_list[i][1][0] = payload
                                else:
                                    params_list[i][1] = payload

                                # we found the xss payload again -> stored xss vuln
                                evil_request = Request(
                                    input_request.path,
                                    method=input_request.method,
                                    get_params=get_params,
                                    post_params=post_params,
                                    file_params=file_params,
                                    referer=referer)

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

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

                                await self.add_vuln_high(
                                    request_id=request.path_id,
                                    category=NAME,
                                    request=evil_request,
                                    parameter=parameter,
                                    info=description,
                                    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,
                                        request.path, parameter)

                                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("---")
                                # FIX: search for the next code in the webpage

                # Ok the content is stored, but will we be able to inject javascript?
                else:
                    parameter = self.tried_xss[taint][1]
                    payloads = generate_payloads(response.content, taint,
                                                 self.PAYLOADS_FILE,
                                                 self.external_endpoint)
                    flags = self.tried_xss[taint][2]

                    # TODO: check that and make it better
                    if flags.method == PayloadType.get:
                        method = "G"
                    elif flags.method == PayloadType.file:
                        method = "F"
                    else:
                        method = "P"

                    await self.attempt_exploit(method, payloads, input_request,
                                               parameter, taint, request)
示例#27
0
    async def process_line(self, line):
        match = match_or = match_and = False
        fail = fail_or = False

        osv_id = line[1]
        path = line[3]
        method = line[4]
        vuln_desc = line[10]
        post_data = line[11]

        path = path.replace("@CGIDIRS", "/cgi-bin/")
        path = path.replace("@ADMIN", "/admin/")
        path = path.replace("@NUKE", "/modules/")
        path = path.replace("@PHPMYADMIN", "/phpMyAdmin/")
        path = path.replace("@POSTNUKE", "/postnuke/")
        path = re.sub(r"JUNK\((\d+)\)",
                      lambda x: self.junk_string[:int(x.group(1))], path)

        if path[0] == "@":
            return

        if not path.startswith("/"):
            path = "/" + path

        try:
            url = f"{self.parts.scheme}://{self.parts.netloc}{path}"
        except UnicodeDecodeError:
            return

        if method == "GET":
            evil_request = Request(url)
        elif method == "POST":
            evil_request = Request(url, post_params=post_data, method=method)
        else:
            evil_request = Request(url, post_params=post_data, method=method)

        if method == "GET":
            log_verbose(f"[¨] {evil_request.url}")
        else:
            log_verbose(f"[¨] {evil_request.http_repr()}")

        try:
            response = await self.crawler.async_send(evil_request)
            page = response.content
            code = response.status
        except (RequestError, ConnectionResetError):
            self.network_errors += 1
            return
        except Exception as exception:
            logging.warning(
                f"{exception} occurred with URL {evil_request.url}")
            return

        raw = " ".join([x + ": " + y for x, y in response.headers.items()])
        raw += page

        # See https://github.com/sullo/nikto/blob/master/program/plugins/nikto_tests.plugin for reference
        expected_status_codes = []
        # First condition (match)
        if len(line[5]) == 3 and line[5].isdigit():
            expected_status_code = int(line[5])
            expected_status_codes.append(expected_status_code)
            if code == expected_status_code:
                match = True
        else:
            if line[5] in raw:
                match = True

        # Second condition (or)
        if line[6] != "":
            if len(line[6]) == 3 and line[6].isdigit():
                expected_status_code = int(line[6])
                expected_status_codes.append(expected_status_code)
                if code == expected_status_code:
                    match_or = True
            else:
                if line[6] in raw:
                    match_or = True

        # Third condition (and)
        if line[7] != "":
            if len(line[7]) == 3 and line[7].isdigit():
                if code == int(line[7]):
                    match_and = True
            else:
                if line[7] in raw:
                    match_and = True
        else:
            match_and = True

        # Fourth condition (fail)
        if line[8] != "":
            if len(line[8]) == 3 and line[8].isdigit():
                if code == int(line[8]):
                    fail = True
            else:
                if line[8] in raw:
                    fail = True

        # Fifth condition (or)
        if line[9] != "":
            if len(line[9]) == 3 and line[9].isdigit():
                if code == int(line[9]):
                    fail_or = True
            else:
                if line[9] in raw:
                    fail_or = True

        if ((match or match_or) and match_and) and not (fail or fail_or):
            if expected_status_codes:
                if await self.is_false_positive(evil_request,
                                                expected_status_codes):
                    return

            log_red("---")
            log_red(vuln_desc)
            log_red(url)

            refs = []
            if osv_id != "0":
                refs.append("https://vulners.com/osvdb/OSVDB:" + osv_id)

            # CERT
            cert_advisory = re.search("(CA-[0-9]{4}-[0-9]{2})", vuln_desc)
            if cert_advisory is not None:
                refs.append("http://www.cert.org/advisories/" +
                            cert_advisory.group(0) + ".html")

            # SecurityFocus
            securityfocus_bid = re.search("BID-([0-9]{4})", vuln_desc)
            if securityfocus_bid is not None:
                refs.append("http://www.securityfocus.com/bid/" +
                            securityfocus_bid.group(1))

            # Mitre.org
            mitre_cve = re.search("((CVE|CAN)-[0-9]{4}-[0-9]{4,})", vuln_desc)
            if mitre_cve is not None:
                refs.append("http://cve.mitre.org/cgi-bin/cvename.cgi?name=" +
                            mitre_cve.group(0))

            # CERT Incidents
            cert_incident = re.search("(IN-[0-9]{4}-[0-9]{2})", vuln_desc)
            if cert_incident is not None:
                refs.append("http://www.cert.org/incident_notes/" +
                            cert_incident.group(0) + ".html")

            # Microsoft Technet
            ms_bulletin = re.search("(MS[0-9]{2}-[0-9]{3})", vuln_desc)
            if ms_bulletin is not None:
                refs.append(
                    "http://www.microsoft.com/technet/security/bulletin/" +
                    ms_bulletin.group(0) + ".asp")

            info = vuln_desc
            if refs:
                log_red(_("References:"))
                log_red("  " + "\n  ".join(refs))

                info += "\n" + _("References:") + "\n"
                info += "\n".join(refs)

            log_red("---")

            await self.add_vuln_high(category=NAME,
                                     request=evil_request,
                                     info=info,
                                     wstg=WSTG_CODE)
示例#28
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("---")
示例#29
0
    async def boolean_based_attack(self, request: Request, parameters_to_skip: set):
        try:
            good_response = await self.crawler.async_send(request)
            good_status = good_response.status
            good_redirect = good_response.redirection_url
            # good_title = response.title
            good_hash = good_response.text_only_md5
        except ReadTimeout:
            self.network_errors += 1
            return

        methods = ""
        if self.do_get:
            methods += "G"
        if self.do_post:
            methods += "PF"

        mutator = Mutator(
            methods=methods,
            payloads=generate_boolean_payloads(),
            qs_inject=self.must_attack_query_string,
            skip=self.options.get("skipped_parameters", set()) | parameters_to_skip
        )

        page = request.path

        current_parameter = None
        skip_till_next_parameter = False
        current_session = None
        test_results = []
        last_mutated_request = None

        for mutated_request, parameter, __, flags in mutator.mutate(request):
            # Make sure we always pass through the following block to see changes of payloads formats
            if current_session != flags.platform:
                # We start a new set of payloads, let's analyse results for previous ones
                if test_results and all(test_results):
                    # We got a winner
                    skip_till_next_parameter = True
                    vuln_info = _("SQL Injection")

                    if current_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, current_parameter)

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

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

                # Don't forget to reset session and results
                current_session = flags.platform
                test_results = []

            if current_parameter != parameter:
                # Start attacking a new parameter, forget every state we kept
                current_parameter = parameter
                skip_till_next_parameter = False
            elif skip_till_next_parameter:
                # If parameter is vulnerable, just skip till next parameter
                continue

            if test_results and not all(test_results):
                # No need to go further: one of the tests was wrong
                continue

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

            try:
                response = await self.crawler.async_send(mutated_request)
            except RequestError:
                self.network_errors += 1
                # We need all cases to make sure SQLi is there
                test_results.append(False)
                continue

            comparison = (
                response.status == good_status and
                response.redirection_url == good_redirect and
                response.text_only_md5 == good_hash
            )

            test_results.append(comparison == (flags.section == "True"))
            last_mutated_request = mutated_request
示例#30
0
文件: mod_xxe.py 项目: jinxka/wapiti
    async def finish(self):
        endpoint_url = f"{self.internal_endpoint}get_xxe.php?session_id={self._session_id}"
        logging.info(
            _("[*] Asking endpoint URL {} for results, please wait...").format(
                endpoint_url))
        await sleep(2)
        # A la fin des attaques on questionne le endpoint pour savoir s'il a été contacté
        endpoint_request = Request(endpoint_url)
        try:
            response = await self.crawler.async_send(endpoint_request)
        except RequestError:
            self.network_errors += 1
            logging.error(
                _("[!] Unable to request endpoint URL '{}'").format(
                    self.internal_endpoint))
            return

        data = response.json
        if not isinstance(data, dict):
            return

        for request_id in data:
            original_request = await self.persister.get_path_by_id(request_id)
            if original_request is None:
                continue
                # raise ValueError("Could not find the original request with ID {}".format(request_id))

            page = original_request.path
            for hex_param in data[request_id]:
                parameter = unhexlify(hex_param).decode("utf-8")

                for infos in data[request_id][hex_param]:
                    request_url = infos["url"]
                    # Date in ISO format
                    request_date = infos["date"]
                    request_ip = infos["ip"]
                    request_size = infos["size"]
                    payload_name = infos["payload"]

                    if parameter == "QUERY_STRING":
                        vuln_message = Messages.MSG_QS_INJECT.format(
                            self.MSG_VULN, page)
                    elif parameter == "raw body":
                        vuln_message = _(
                            "Out-Of-Band {0} by sending raw XML in request body"
                        ).format(self.MSG_VULN)
                    else:
                        vuln_message = _(
                            "Out-Of-Band {0} via injection in the parameter {1}"
                        ).format(self.MSG_VULN, parameter)

                    more_infos = _(
                        "The target sent {0} bytes of data to the endpoint at {1} with IP {2}.\n"
                        "Received data can be seen at {3}.").format(
                            request_size, request_date, request_ip,
                            request_url)

                    vuln_message += "\n" + more_infos

                    # placeholder if shit happens
                    payload = (
                        "<xml>"
                        "See https://phonexicum.github.io/infosec/xxe.html#attack-vectors"
                        "</xml>")

                    for payload, _flags in self.payloads:
                        if f"{payload_name}.dtd" in payload:
                            payload = payload.replace(
                                "[PATH_ID]", str(original_request.path_id))
                            payload = payload.replace("[PARAM_AS_HEX]",
                                                      "72617720626f6479")
                            break

                    if parameter == "raw body":
                        mutated_request = Request(original_request.path,
                                                  method="POST",
                                                  enctype="text/xml",
                                                  post_params=payload)
                    elif parameter == "QUERY_STRING":
                        mutated_request = Request(
                            f"{original_request.path}?{quote(payload)}",
                            method="GET")
                    elif parameter in original_request.get_keys or parameter in original_request.post_keys:
                        mutator = Mutator(
                            methods="G"
                            if original_request.method == "GET" else "P",
                            payloads=[(payload, Flags())],
                            qs_inject=self.must_attack_query_string,
                            parameters=[parameter],
                            skip=self.options.get("skipped_parameters"))

                        mutated_request, __, __, __ = next(
                            mutator.mutate(original_request))
                    else:
                        mutator = FileMutator(
                            payloads=[(payload, Flags())],
                            parameters=[parameter],
                            skip=self.options.get("skipped_parameters"))
                        mutated_request, __, __, __ = next(
                            mutator.mutate(original_request))

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

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