예제 #1
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)
예제 #2
0
    async def attack(self, request: Request):
        page = request.path
        self.excluded_path.add(page)

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

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

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

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

            if interesting_methods:
                log_orange("---")
                log_orange(
                    _("Interesting methods allowed on {}: {}").format(
                        page, ", ".join(interesting_methods)))
                await self.add_addition(
                    category=NAME,
                    request=option_request,
                    info=_("Interesting methods allowed on {}: {}").format(
                        page, ", ".join(interesting_methods)),
                    wstg=WSTG_CODE)
                log_orange("---")
예제 #3
0
    async def 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("---")
예제 #4
0
파일: mod_ssrf.py 프로젝트: jinxka/wapiti
    async def attack(self, request: Request):
        # Let's just send payloads, we don't care of the response as what we want to know is if the target
        # contacted the endpoint.
        for mutated_request, _parameter, _payload, _flags in self.mutator.mutate(request):
            log_verbose(f"[¨] {mutated_request}")

            try:
                await self.crawler.async_send(mutated_request)
            except RequestError:
                self.network_errors += 1
                continue
예제 #5
0
파일: mod_crlf.py 프로젝트: devl00p/wapiti
    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
    async def test_directory(self, path: str):
        log_verbose(f"[¨] Testing directory {path}")

        test_page = Request(path + "does_n0t_exist.htm")
        try:
            response = await self.crawler.async_send(test_page)
        except RequestError:
            self.network_errors += 1
            return

        if response.status not in [403, 404]:
            # we don't want to deal with this at the moment
            return

        tasks = set()
        pending_count = 0
        payload_iterator = iter(self.payloads)

        while True:
            if pending_count < self.options[
                    "tasks"] and not self._stop_event.is_set():
                try:
                    candidate, __ = next(payload_iterator)
                except StopIteration:
                    pass
                else:
                    url = path + candidate
                    if url not in self.known_dirs and url not in self.known_pages and url not in self.new_resources:
                        task = asyncio.create_task(self.check_path(url))
                        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:
                    await task
                except RequestError:
                    self.network_errors += 1
                tasks.remove(task)

            if self._stop_event.is_set():
                for task in pending_tasks:
                    task.cancel()
                    tasks.remove(task)
예제 #7
0
파일: mod_backup.py 프로젝트: jinxka/wapiti
    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
파일: 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
예제 #10
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)
예제 #11
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)
예제 #12
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
예제 #13
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("---")
예제 #14
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("---")
예제 #15
0
파일: mod_file.py 프로젝트: devl00p/wapiti
    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("---")
예제 #16
0
    async def async_analyze(self, request) -> Tuple[bool, List]:
        async with self._sem:
            self._processed_requests.append(request)  # thread safe

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

            dir_name = request.dir_name
            # Currently not exploited. Would be interesting though but then it should be implemented separately
            # Maybe in another task as we don't want to spend to much time in this function
            # async with self._shared_lock:
            #     # lock to prevent launching duplicates requests that would otherwise waste time
            #     if dir_name not in self._custom_404_codes:
            #         invalid_page = "zqxj{0}.html".format("".join([choice(ascii_letters) for __ in range(10)]))
            #         invalid_resource = web.Request(dir_name + invalid_page)
            #         try:
            #             page = await self._crawler.async_get(invalid_resource)
            #             self._custom_404_codes[dir_name] = page.status
            #         except httpx.RequestError:
            #             pass

            self._hostnames.add(request.hostname)

            resource_url = request.url

            try:
                page = await self._crawler.async_send(request)
            except (TypeError, UnicodeDecodeError) as exception:
                logging.debug(f"{exception} with url {resource_url}")  # debug
                return False, []
            # TODO: what to do of connection errors ? sleep a while before retrying ?
            except ConnectionError:
                logging.error(_("[!] Connection error with URL"), resource_url)
                return False, []
            except httpx.RequestError as error:
                logging.error(
                    _("[!] {} with url {}").format(error.__class__.__name__,
                                                   resource_url))
                return False, []

            if self._max_files_per_dir:
                async with self._shared_lock:
                    self._file_counts[dir_name] += 1

            if self._qs_limit and request.parameters_count:
                async with self._shared_lock:
                    self._pattern_counts[request.pattern] += 1

            if request.link_depth == self._max_depth:
                # We are at the edge of the depth so next links will have depth + 1 so to need to parse the page.
                return True, []

            # Above this line we need the content of the page. As we are in stream mode we must force reading the body.
            await page.read()

            # Sur les ressources statiques le content-length est généralement indiqué
            if self._max_page_size > 0:
                if page.raw_size > self._max_page_size:
                    await page.clean()
                    return False, []

            await asyncio.sleep(0)
            resources = self.extract_links(page, request)
            # TODO: there's more situations where we would not want to attack the resource... must check this
            if page.is_directory_redirection:
                return False, resources

            return True, resources
예제 #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
파일: 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("---")
예제 #19
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
예제 #20
0
파일: mod_xss.py 프로젝트: devl00p/wapiti
    async def attempt_exploit(self, method, payloads, original_request,
                              parameter, taint):
        timeouted = False
        page = original_request.path
        saw_internal_error = False

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

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

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

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

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

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

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

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

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

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

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

                    # stop trying payloads and jump to the next parameter
                    break

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

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

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