Esempio n. 1
0
 def encode(self):
     if self.exec_type in self.acceptable_exec_types:
         payload = self.payload_starts[self.exec_type]
     else:
         payload = self.payload
     payload = payload.format(self.payload)
     is_inserted = insert_payload(payload, self.payload_type,
                                  self.exec_type, self.cursor)
     return payload, is_inserted
Esempio n. 2
0
 def encode(self):
     acceptable_exec_types = ("powershell", "php", "python", "perl", "ruby",
                              "bash", "batch")
     if self.exec_type in acceptable_exec_types:
         payload = self.payload_starts[self.exec_type]
     else:
         payload = self.payload
     payload = payload.format(self.payload)
     is_inserted = insert_payload(payload, self.payload_type,
                                  self.exec_type, self.cursor)
     return payload, is_inserted
Esempio n. 3
0
 def encode(self):
     data = self.xor_string()
     encoded_payload, key = data
     if self.exec_type in self.acceptable_exec_types:
         payload = self.payload_starts[self.exec_type]
     else:
         payload = ""
     if payload == "":
         raise UnacceptableExecType("{} is unable to be encrypted into xor".format(self.exec_type))
     payload = payload.format(encoded_payload, key)
     is_inserted = insert_payload(payload, self.payload_type, self.exec_type, self.cursor)
     return payload, is_inserted
Esempio n. 4
0
    def encode(self):
        encoded_payload = base64.b64encode(self.payload)
        acceptable_exec_types = ("powershell", "php", "python", "perl", "ruby", "bash")
        if self.exec_type.lower() in acceptable_exec_types:
            payload = self.payload_starts[self.exec_type]
        else:
            payload = ""
        if payload == "":
            raise UnacceptableExecType("{} is not able to be encoded into Base64".format(self.exec_type))

        retval = payload.format(encoded_payload)
        is_inserted = insert_payload(retval, self.payload_type, self.exec_type, self.cursor)
        return retval, is_inserted
Esempio n. 5
0
 def encode(self):
     hexlify = lambda x: "".join([hex(ord(c))[2:].zfill(2) for c in x])
     acceptable_exec_types = ("powershell", "php", "python", "perl", "ruby",
                              "bash")
     encoded_payload = hexlify(self.payload)
     if self.exec_type in acceptable_exec_types:
         payload = self.payload_starts[self.exec_type]
     else:
         payload = ""
     if payload == "":
         raise UnacceptableExecType("{} cannot be encoded into hex".format(
             self.exec_type))
     payload = payload.format(encoded_payload)
     is_inserted = insert_payload(payload, self.payload_type,
                                  self.exec_type, self.cursor)
     return payload, is_inserted
Esempio n. 6
0
 def encode(self):
     if self.exec_type in self.acceptable_exec_types:
         usable_payload = []
         for c in self.payload:
             if c == '"':
                 c = r'\"'
             usable_payload.append(c)
         self.payload = "".join(usable_payload)
         payload = self.payload_starts[self.exec_type]
     else:
         payload = ""
     if payload == "":
         raise UnacceptableExecType(
             "{} cannot be encoded into atbash".format(self.exec_type))
     encoded_payload = self.atbash()
     retval = payload.format(encoded_payload)
     is_inserted = insert_payload(retval, self.payload_type, self.exec_type,
                                  self.cursor)
     return retval, is_inserted
Esempio n. 7
0
    def encode(self):
        tmp = []
        for char in list(self.payload):
            if char == '"':
                char = r'\"'
            tmp.append(char)
        usable_payload = "".join(tmp)
        encoded_payload = usable_payload.encode("rot13")
        if self.exec_type.lower() in self.acceptable_exec_types:
            payload = self.payload_starts[self.exec_type]
        else:
            payload = ""
        if payload == "":
            raise UnacceptableExecType(
                "# {} is not able to be encoded into Rot13".format(
                    self.exec_type))

        retval = payload.format(encoded_payload)
        is_inserted = insert_payload(retval, self.payload_type, self.exec_type,
                                     self.cursor)
        return retval, is_inserted
Esempio n. 8
0
 def encode(self):
     if self.exec_type.lower() in self.acceptable_exec_types:
         if self.exec_type == "python":
             print(
                 "# be sure that the target has PyCrypto on their system!")
         payload = self.payload_starts[self.exec_type]
         iv = Random.new().read(AES.block_size)
         iv_int = int(binascii.hexlify(iv), 16)
         ctr = Counter.new(AES.block_size * 8, initial_value=iv_int)
         key = Random.new().read(self.key_bytes)
         aes = AES.new(key, AES.MODE_CTR, counter=ctr)
         encoded_payload = base64.b64encode(aes.encrypt(self.payload))
     else:
         payload = ""
     if payload == "":
         raise UnacceptableExecType(
             "{} cannot be encoded into AES-256".format(self.exec_type))
     retval = payload.format(encoded_payload, base64.b64encode(key),
                             base64.b64encode(iv))
     is_inserted = insert_payload(retval, self.payload_type, self.exec_type,
                                  self.cursor)
     return retval, is_inserted
Esempio n. 9
0
def main():
    opt = WhatWafParser().cmd_parser()

    if not len(sys.argv) > 1:
        error("you failed to provide an option, redirecting to help menu")
        time.sleep(2)
        cmd = "python whatwaf.py --help"
        subprocess.call(shlex.split(cmd))
        exit(0)

    # if you feel that you have to many folders or files in the whatwaf home folder
    # we'll give you an option to clean it free of charge
    if opt.cleanHomeFolder:
        import shutil

        try:
            warn(
                "cleaning the home folder: {home}, if you have installed with setup.sh, "
                "this will erase the executable script along with everything inside "
                "of the {home} directory (fingerprints, scripts, copies of whatwaf, etc) "
                "if you are sure you want to do this press ENTER now. If you changed "
                "your mind press CNTRL-C now".format(home=HOME))
            # you have three seconds to change your mind
            raw_input("")
            info("attempting to clean home folder")
            shutil.rmtree(HOME)
            info("home folder removed")
        except KeyboardInterrupt:
            fatal("cleaning aborted")
        except OSError:
            fatal("no home folder detected, already cleaned?")
        exit(0)

    cursor = initialize()

    if opt.exportEncodedToFile is not None:
        payloads = fetch_data(cursor)
        if len(payloads) != 0:
            exported_payloads_path = export_payloads(payloads,
                                                     opt.exportEncodedToFile)
            info("payloads exported to: {}".format(exported_payloads_path))
        else:
            warn(
                "there appears to be no payloads stored in the database, to create payloads use the following options:"
            )
            proc = subprocess.check_output(["python", "whatwaf.py", "--help"])
            parsed_help = parse_help_menu(str(proc), "encoding options:",
                                          "output options:")
            print(parsed_help)
        exit(1)

    if opt.viewAllCache:
        cached_payloads = fetch_data(cursor)
        cached_urls = fetch_data(cursor, is_payload=False)
        display_cached(cached_urls, cached_payloads)
        exit(0)

    if opt.viewCachedPayloads:
        payloads = fetch_data(cursor)
        if len(payloads) != 0:
            display_cached(None, payloads)
        else:
            warn(
                "there appears to be no payloads stored in the database, to create payloads use the following options:"
            )
            proc = subprocess.check_output(["python", "whatwaf.py", "--help"])
            parsed_help = parse_help_menu(proc, "encoding options:",
                                          "output options:")
            print(parsed_help)
        exit(0)

    if opt.viewUrlCache:
        cached_urls = fetch_data(cursor, is_payload=False)
        display_cached(cached_urls, None)
        exit(0)

    if opt.encodePayload is not None:
        spacer = "-" * 30
        payload = opt.encodePayload[0]
        load_path = opt.encodePayload[1:]
        for load in load_path:
            try:
                payload = encode(payload, load)
            except (AttributeError, ImportError):
                warn(
                    "invalid load path given: '{}', skipping it and continuing"
                    .format(load))
        success("encoded successfully:")
        print("{}\n{}\n{}".format(spacer, payload, spacer))
        insert_payload(payload, cursor)
        info("payload has been cached for future use")
        exit(0)

    if opt.encodePayloadList is not None:
        spacer = "-" * 30
        try:
            file_path, load_path = opt.encodePayloadList
            info(
                "encoding payloads from given file '{}' using given tamper '{}'"
                .format(file_path, load_path))
            with open(file_path) as payloads:
                encoded = [
                    encode(p.strip(), load_path) for p in payloads.readlines()
                ]
                if opt.saveEncodedPayloads is not None:
                    with open(opt.saveEncodedPayloads, "a+") as save:
                        for item in encoded:
                            save.write(item + "\n")
                    success("saved encoded payloads to file '{}' successfully".
                            format(opt.saveEncodedPayloads))
                else:
                    success("payloads encoded successfully:")
                    print(spacer)
                    for i, item in enumerate(encoded, start=1):
                        insert_payload(item, cursor)
                        print("#{} {}".format(i, item))
                    print(spacer)
            info("payloads have been cached for future use")
        except IOError:
            fatal(
                "provided file '{}' appears to not exist, check the path and try again"
                .format(file_path))
        except (AttributeError, ImportError):
            fatal("invalid load path given, check the load path and try again")
        exit(0)

    if opt.updateWhatWaf:
        info("update in progress")
        cmd = shlex.split("git pull origin master")
        subprocess.call(cmd)
        exit(0)

    if not opt.hideBanner:
        print(BANNER)

    if opt.listEncodingTechniques:
        info("gathering available tamper script load paths")
        tamper_list = get_encoding_list(TAMPERS_DIRECTORY,
                                        is_tampers=True,
                                        is_wafs=False)
        for tamper in sorted(tamper_list):
            print(tamper)
        exit(0)

    if opt.viewPossibleWafs:
        import importlib

        info("gathering a list of possible detectable wafs")
        wafs_list = get_encoding_list(PLUGINS_DIRECTORY,
                                      is_tampers=False,
                                      is_wafs=True)
        for i, waf in enumerate(sorted(wafs_list), start=1):
            try:
                imported = importlib.import_module(waf)
                print("{}".format(imported.__product__))
            except ImportError:
                pass
        exit(0)

    # gotta find a better way to check for updates so ima hotfix it
    #info("checking for updates")
    #check_version()

    format_opts = [opt.sendToYAML, opt.sendToCSV, opt.sendToJSON]
    if opt.formatOutput:
        amount_used = 0
        for item in format_opts:
            if item is True:
                amount_used += 1
        if amount_used > 1:
            warn(
                "multiple file formats have been detected, there is a high probability that this will cause "
                "issues while saving file information. please use only one format at a time"
            )
        elif amount_used == 0:
            warn(
                "output will not be saved to a file as no file format was provided. to save output to file "
                "pass one of the file format flags (IE `-J` for JSON format)",
                minor=True)
    elif any(format_opts) and not opt.formatOutput:
        warn(
            "you've chosen to send the output to a file, but have not formatted the output, no file will be saved "
            "do so by passing the format flag (IE `-F -J` for JSON format)")

    if opt.skipBypassChecks and opt.amountOfTampersToDisplay is not None:
        warn(
            "you've chosen to skip bypass checks and chosen an amount of tamper to display, tampers will be skipped",
            minor=True)

    # there is an extra dependency that you need in order
    # for requests to run behind socks proxies, we'll just
    # do a little check to make sure you have it installed
    if opt.runBehindTor or opt.runBehindProxy is not None and "socks" in opt.runBehindProxy:
        try:
            import socks
        except ImportError:
            # if you don't we will go ahead and exit the system with an error message
            error(
                "to run behind socks proxies (like Tor) you need to install pysocks `pip install pysocks`, "
                "otherwise use a different proxy protocol")
            sys.exit(1)

    proxy, agent = configure_request_headers(random_agent=opt.useRandomAgent,
                                             agent=opt.usePersonalAgent,
                                             proxy=opt.runBehindProxy,
                                             tor=opt.runBehindTor,
                                             tor_port=opt.configTorPort)

    if opt.checkTorConnection:
        import re

        info("checking Tor connection")
        check_url = "https://check.torproject.org/"
        check_regex = re.compile("This browser is configured to use Tor.",
                                 re.I)
        _, _, content, _ = get_page(check_url, proxy=proxy, agent=agent)
        if check_regex.search(str(content)) is not None:
            success("it appears that Tor is working properly")
        else:
            warn("it appears Tor is not configured properly")

    if opt.providedPayloads is not None:
        payload_list = [
            p.strip() if p[0] == " " else p
            for p in str(opt.providedPayloads).split(",")
        ]
        info("using provided payloads")
    elif opt.payloadList is not None:
        try:
            open(opt.payloadList).close()
        except Exception:
            fatal(
                "provided file '{}' does not exists, check the path and try again"
                .format(opt.payloadList))
            exit(1)
        payload_list = [
            p.strip("\n") for p in open(opt.payloadList).readlines()
        ]
        info("using provided payload file '{}'".format(opt.payloadList))
    else:
        payload_list = WAF_REQUEST_DETECTION_PAYLOADS
        info("using default payloads")

    if opt.saveFingerprints:
        warn(
            "fingerprinting is enabled, all fingerprints (WAF related or not) will be saved for further analysis "
            "if the fingerprint already exists it will be skipped",
            minor=True)

    if opt.trafficFile is not None:
        info("saving HTTP traffic to '{}'".format(opt.trafficFile))
    if opt.sleepTimeThrottle != 0:
        info("sleep throttle has been set to {}s".format(
            opt.sleepTimeThrottle))

    try:
        if opt.postRequest:
            request_type = "POST"
        else:
            request_type = "GET"

        request_count = 0

        if opt.runSingleWebsite:
            url_to_use = auto_assign(opt.runSingleWebsite, ssl=opt.forceSSL)
            if opt.checkCachedUrls:
                checked_results = check_url_against_cached(url_to_use, cursor)
                if checked_results is not None:
                    print(
                        RESULTS_TEMPLATE.format("-" * 20,
                                                str(checked_results[1]),
                                                str(checked_results[2]),
                                                str(checked_results[3]),
                                                str(checked_results[4]),
                                                "-" * 20))
                    exit(0)

            if opt.testTargetConnection:
                info(
                    "testing connection to target URL before starting attack {}"
                    .format(
                        "\033[1m\033[33m(Tor is initialized which may increase latency)"
                        if opt.runBehindTor else ""))
                results = test_target_connection(url_to_use,
                                                 proxy=proxy,
                                                 agent=agent,
                                                 headers=opt.extraHeaders)
                if results == "nogo":
                    fatal(
                        "connection to target URL failed multiple times, check connection and try again"
                    )
                    exit(1)
                elif results == "acceptable":
                    warn(
                        "there appears to be some latency on the connection, this may interfere with results",
                        minor=False)
                else:
                    success("connection succeeded, continuing")

            info("running single web application '{}'".format(url_to_use))
            requests = detection_main(
                url_to_use,
                payload_list,
                cursor,
                agent=agent,
                proxy=proxy,
                verbose=opt.runInVerbose,
                skip_bypass_check=opt.skipBypassChecks,
                verification_number=opt.verifyNumber,
                formatted=opt.formatOutput,
                tamper_int=opt.amountOfTampersToDisplay,
                use_json=opt.sendToJSON,
                use_yaml=opt.sendToYAML,
                use_csv=opt.sendToCSV,
                fingerprint_waf=opt.saveFingerprints,
                provided_headers=opt.extraHeaders,
                traffic_file=opt.trafficFile,
                throttle=opt.sleepTimeThrottle,
                req_timeout=opt.requestTimeout,
                post_data=opt.postRequestData,
                request_type=request_type,
                check_server=opt.determineWebServer,
                threaded=opt.threaded,
                force_file_creation=opt.forceFileCreation,
                save_copy_of_file=opt.outputDirectory)
        elif any(o is not None
                 for o in [opt.runMultipleWebsites, opt.burpRequestFile]):
            info("reading from '{}'".format(opt.runMultipleWebsites
                                            or opt.burpRequestFile))
            try:
                open(opt.runMultipleWebsites or opt.burpRequestFile)
            except IOError:
                fatal("file: '{}' did not open, does it exist?".format(
                    opt.runMultipleWebsites))
                exit(-1)
            if opt.runMultipleWebsites is not None:
                site_runners = []
                with open(opt.runMultipleWebsites) as urls:
                    for url in urls:
                        possible_url = auto_assign(url.strip(),
                                                   ssl=opt.forceSSL)
                        if opt.checkCachedUrls:
                            url_is_cached = check_url_against_cached(
                                possible_url, cursor)
                            if url_is_cached is not None:
                                print(
                                    RESULTS_TEMPLATE.format(
                                        "-" * 20, str(url_is_cached[1]),
                                        str(url_is_cached[2]),
                                        str(url_is_cached[3]),
                                        str(url_is_cached[4]), "-" * 20))
                            else:
                                site_runners.append(possible_url)
                        else:
                            site_runners.append(possible_url)
            elif opt.burpRequestFile is not None:
                site_runners = parse_burp_request(opt.burpRequestFile)
            else:
                site_runners = []

            if len(site_runners) == 0:
                fatal("no targets parsed from file, exiting")
                exit(1)
            else:
                info("parsed a total of {} target(s) from file".format(
                    len(site_runners)))

            for i, url in enumerate(site_runners, start=1):
                if opt.testTargetConnection:
                    info(
                        "testing connection to target URL before starting attack"
                    )
                    results = test_target_connection(url,
                                                     proxy=proxy,
                                                     agent=agent,
                                                     headers=opt.extraHeaders)
                    if results == "nogo":
                        fatal(
                            "connection to target URL failed multiple times, check connection and try again, skipping"
                        )
                        continue
                    elif results == "acceptable":
                        warn(
                            "there appears to be some latency on the connection, this may interfere with results",
                            minor=False)
                    else:
                        success("connection succeeded, continuing")

                info("currently running on site #{} ('{}')".format(i, url))
                detection_main(url,
                               payload_list,
                               cursor,
                               agent=agent,
                               proxy=proxy,
                               verbose=opt.runInVerbose,
                               skip_bypass_check=opt.skipBypassChecks,
                               verification_number=opt.verifyNumber,
                               formatted=opt.formatOutput,
                               tamper_int=opt.amountOfTampersToDisplay,
                               use_json=opt.sendToJSON,
                               use_yaml=opt.sendToYAML,
                               use_csv=opt.sendToCSV,
                               fingerprint_waf=opt.saveFingerprints,
                               provided_headers=opt.extraHeaders,
                               traffic_file=opt.trafficFile,
                               throttle=opt.sleepTimeThrottle,
                               req_timeout=opt.requestTimeout,
                               post_data=opt.postRequestData,
                               request_type=request_type,
                               check_server=opt.determineWebServer,
                               threaded=opt.threaded,
                               force_file_creation=opt.forceFileCreation,
                               save_copy_of_file=opt.outputDirectory)
                print("\n\b")
                time.sleep(0.5)

        elif opt.googlerFile is not None:
            urls = parse_googler_file(opt.googlerFile)
            if urls is not None:
                info("parsed a total of {} URLS from Googler JSON file".format(
                    len(urls)))
                for i, url in enumerate(urls, start=1):
                    do_url_run = True
                    if opt.checkCachedUrls:
                        url_is_cached = check_url_against_cached(url, cursor)
                        if url_is_cached is not None:
                            print(
                                RESULTS_TEMPLATE.format(
                                    "-" * 20, str(url_is_cached[1]),
                                    str(url_is_cached[2]),
                                    str(url_is_cached[3]),
                                    str(url_is_cached[4]), "-" * 20))
                            do_url_run = False

                    if do_url_run:
                        if opt.testTargetConnection:
                            info(
                                "testing connection to target URL before starting attack"
                            )
                            results = test_target_connection(
                                url,
                                proxy=proxy,
                                agent=agent,
                                headers=opt.extraHeaders)
                            if results == "nogo":
                                fatal(
                                    "connection to target URL failed multiple times, check connection and try again"
                                )
                                continue
                            elif results == "acceptable":
                                warn(
                                    "there appears to be some latency on the connection, this may interfere with "
                                    "results",
                                    minor=False)
                            else:
                                success("connection succeeded, continuing")

                        info("currently running on '{}' (site #{})".format(
                            url, i))
                        requests = detection_main(
                            url,
                            payload_list,
                            cursor,
                            agent=agent,
                            proxy=proxy,
                            verbose=opt.runInVerbose,
                            skip_bypass_check=opt.skipBypassChecks,
                            verification_number=opt.verifyNumber,
                            formatted=opt.formatOutput,
                            tamper_int=opt.amountOfTampersToDisplay,
                            use_json=opt.sendToJSON,
                            use_yaml=opt.sendToYAML,
                            use_csv=opt.sendToCSV,
                            fingerprint_waf=opt.saveFingerprints,
                            provided_headers=opt.extraHeaders,
                            traffic_file=opt.trafficFile,
                            throttle=opt.sleepTimeThrottle,
                            req_timeout=opt.requestTimeout,
                            post_data=opt.postRequestData,
                            request_type=request_type,
                            check_server=opt.determineWebServer,
                            threaded=opt.threaded,
                            force_file_creation=opt.forceFileCreation,
                            save_copy_of_file=opt.outputDirectory)
                        request_count = request_count + requests if requests is not None else request_count
                        print("\n\b")
                        time.sleep(0.5)
            else:
                fatal("file failed to load, does it exist?")

    except KeyboardInterrupt:
        fatal("user aborted scanning")
    except InvalidURLProvided:
        fatal(
            "the provided URL is unable to be validated, check the URL and try again (you may need to unquote the "
            "HTML entities)")
    except Exception as e:
        import traceback

        sep = "-" * 30
        fatal(
            "WhatWaf has caught an unhandled exception with the error message: '{}'."
            .format(str(e)))
        exception_data = "Traceback (most recent call):\n{}{}".format(
            "".join(traceback.format_tb(sys.exc_info()[2])), str(e))
        error("\n{}\n{}\n{}".format(sep, exception_data, sep))
        request_issue_creation(exception_data)
Esempio n. 10
0
 def single_run_args(conf, cursor):
     """
     parses the configuration file and tells the program what needs to be done
     """
     # fetch the cached payloads out of the database
     cached_payloads = fetch_cached_payloads(cursor)
     if conf["graffiti"]["wipeData"]:
         print("wiping the database and the history files")
         secure_delete(DATABASE_PATH)
         history_files = get_history_files(HISTORY_FILES_PATH)
         for f in history_files:
             secure_delete(f)
         print("database and history files wiped")
         close()
     if conf["graffiti"]["useTerminal"]:
         print(BANNER)
         print(
             "no arguments have been passed, dropping into terminal type `help/?` to get help, "
             "all commands that sit inside of `/bin` are available in the terminal"
         )
         available_payloads = get_payload_paths()
         GraffitiTerminal(cached_payloads, available_payloads,
                          cursor).do_start(
                              conf["graffiti"]["saveCommandHistory"])
         exit()
     if conf["graffiti"]["listAvailablePayloads"]:
         available_payloads = get_payload_paths()
         print("total of {} payloads available\n".format(
             len(available_payloads)))
         for payload in available_payloads:
             print(payload)
         close()
     if conf["graffiti"]["viewCached"]:
         print("total of {} payloads present".format(len(cached_payloads)))
         for cache in cached_payloads:
             print("\nLanguage: {}\nPayload Type: {}\nPayload: {}".format(
                 cache[-1], cache[-2], str(repr(cache[1]))))
         close()
     if conf["graffiti"]["codecToUse"] != "":
         if conf["graffiti"]["userDefinedPayload"] != "":
             data = conf["graffiti"]["userDefinedPayload"]
             if len(data) < 2:
                 print(
                     "must provide at least the payload and the language type"
                 )
                 close(exit_code=1)
             else:
                 if len(data) == 4:
                     retval = prepare_single_payload(data[0],
                                                     data[2],
                                                     exec_type=data[1],
                                                     description=data[-1])
                 elif len(data) == 3:
                     retval = prepare_single_payload(data[0],
                                                     data[2],
                                                     exec_type=data[1],
                                                     description="N/A")
                 else:
                     retval = prepare_single_payload(data[0],
                                                     "N/A",
                                                     exec_type=data[1],
                                                     description="N/A")
                 checks = check_payload(data[0])
                 if len(checks) != 0:
                     print(
                         "seems you did not provide needed data to prepare your payload, you need to pass {}. "
                         "either pass the data using the host commands, add the data to the payload, "
                         "or remove the data from the payload all together".
                         format(",".join(checks)))
                     close(exit_code=1)
                 else:
                     if str(conf["graffiti"]["codecToUse"]) == "":
                         print("must specify a codec to use")
                         close(exit_code=1)
                     encoded = Encoder(
                         retval, cursor,
                         conf["graffiti"]["userDefinedLhost"],
                         conf["graffiti"]["userDefinedLport"],
                         conf["graffiti"]["userDefinedURL"],
                         conf["graffiti"]["codecToUse"]).encode()
                     print(encoded[0])
                     close()
         elif conf["graffiti"]["payloadPathToUse"] != "":
             useable_payload_paths = get_payload_paths()
             if conf["graffiti"][
                     "payloadPathToUse"] in useable_payload_paths:
                 full_path = FINISH_PATH_TEMPLATE.format(
                     CUR_DIR, conf["graffiti"]["payloadPathToUse"])
                 data_json = get_single_payload(full_path)
                 payload_type = data_json["data"]["information"]["type"]
                 if payload_type == "reverse":
                     if conf["graffiti"]["userDefinedLhost"] == "" or conf[
                             "graffiti"]["userDefinedLport"] == "":
                         print(
                             "no LHOST or LPORT given, specify and try again"
                         )
                         close()
                     else:
                         graph_data = (conf["graffiti"]["userDefinedLhost"],
                                       conf["graffiti"]["userDefinedLport"],
                                       None)
                 elif payload_type == "dropper":
                     if conf["graffiti"]["userDefinedURL"] == "":
                         print(
                             "no URL specified for the dropper, specify one and try again"
                         )
                         close()
                     else:
                         graph_data = (None, None,
                                       conf["graffiti"]["userDefinedURL"])
                 else:
                     graph_data = (None, None, None)
                 encoded_payload = Encoder(
                     data_json, cursor, graph_data[0], graph_data[1],
                     graph_data[2],
                     conf["graffiti"]["codecToUse"]).encode()
                 if encoded_payload is not None:
                     display_payload(encoded_payload[0],
                                     is_xor=True if
                                     conf["graffiti"]["codecToUse"].lower()
                                     == "xor" else False)
                 else:
                     print("dumping raw encoded payload")
                     display_payload(data_json["data"]["payload"])
                 close()
             else:
                 print(
                     "unknown payload path, do you want to make a payload?")
                 close()
     if conf["graffiti"]["createPayloadFile"] != "":
         acceptable_operating_systems = [
             "windows", "linux", "mac", "unix", "shared"
         ]
         if conf["graffiti"]["createPayloadFile"][
                 -1] in acceptable_operating_systems:
             target_system_type = conf["graffiti"]["createPayloadFile"][-1]
             description = conf["graffiti"]["createPayloadFile"][-2]
             payload_type = conf["graffiti"]["createPayloadFile"][-3]
             execution_type = conf["graffiti"]["createPayloadFile"][1]
             usable_payload = conf["graffiti"]["createPayloadFile"][0]
             data = (description, payload_type, execution_type,
                     usable_payload)
             json_data = tuple_to_json(data)
             path = write_to_file(json_data, target_system_type,
                                  execution_type, payload_type)
             database_info = tuple_to_json(data, sort_and_indent=False)
             insert_payload(database_info, payload_type, execution_type,
                            cursor)
             print("payload created and stored in {}".format(path))
             close()
         else:
             print(
                 "please choose an OS from the following and try again: {}".
                 format(",".join(acceptable_operating_systems)))
             close()
Esempio n. 11
0
def main():
    spacer = colored("-" * 30, 'white')

    print BANNER
    opts = CmdLineParser().cmd_parser()

    if not len(sys.argv[1:]):
        error("You failed to provide an option, redirecting to help menu")
        # 停顿2秒之后再显示 help banner
        time.sleep(2)
        print
        CmdLineParser().cmd_parser(get_help=True)
    else:
        # if you feel that you have to many folders or data in the whatwaf home folder
        # we'll give you an option to clean it free of charge
        if opts.cleanHomeFolder:
            # 对文件的权限或者 移动 拷贝什么的
            import shutil
            try:
                warning(
                    "cleaning the home folder: {home}, if you have installed with setup.sh, "
                    "this will erase the executable script along with everything inside "
                    "of the {home} directory (fingerprints, scripts, copies of whatwaf, etc) "
                    "if you are sure you want to do this press ENTER now. If you changed "
                    "your mind press CNTRL-C now".format(home=HOME))
                # you have three seconds to change your mind
                raw_input("")
                info("attempting to clean up home folder")
                # 这个 HOME 是程序根目录下的 .whatwaf 文件夹, 例如 /root/.whatwaf
                # 删了这个 .whatwaf 隐藏目录
                shutil.rmtree(HOME)
                info("home folder removed")
            except KeyboardInterrupt:
                fatal("cleaning aborted")
            except OSError:
                fatal("no home folder detected, already cleaned?")
            exit(0)

        # 初始化 sqlite3 数据库, 创建~/.whatwaf/whatwaf.sqlite
        # 如果没有 cached_payloads 或者 cached_urls 表,就创建,否则跳过, 然后函数 return 一个 cursor 指针操作数据库的
        cursor = initialize()

        # 如果指定了 --export FILE-TYPE 选项
        # 这里只导出 cached_payloads 表中的数据
        if opts.exportEncodedToFile is not None:
            # fetch_data(cursor, table_name=None)
            # return 一个列表, 包含一行一行的数据, 然后列表里面镶嵌着 每一列的元组
            payloads = fetch_data(cursor, table_name='cached_payloads')
            if len(payloads) != 0:
                # export_payloads() 把 payload 列的数据写入文件,然后返回这个文件的 filename
                exported_payloads_path = export_payloads(
                    payloads, opts.exportEncodedToFile)

                success(
                    "payloads exported to: {}".format(exported_payloads_path))
            else:
                # 数据库里面没有数据
                warning(
                    "there appears to be no payloads stored in the database, to create payloads use the "
                    "following options:")
                proc = subprocess.check_output(
                    ["python", "whatwaf.py", "--help"])
                parsed_help = CmdLineParser.parse_help_menu(
                    str(proc), "encoding options:", "output options:")
                print(parsed_help)
                exit(1)

        # 如果指定了 -vC --view-cache, 这个选项展示 cached_payload 和 cached_url 两张表的内容
        if opts.viewAllCache:
            cached_payloads = fetch_data(cursor, table_name='cached_payloads')
            cached_urls = fetch_data(cursor, table_name='cached_urls')

            # 其实就是将 cached_payload 和 cached_url 两个表的数据 全部展示, 把他们展示的漂亮点而已
            display_cached(cached_urls, cached_payloads)
            exit(0)

        # 指定了 -pC --payload-cache, 这个选项仅仅只展示 cached_payload 表的内容
        if opts.viewCachedPayloads:
            payloads = fetch_data(cursor, table_name='cached_payloads')
            if len(payloads) != 0:
                display_cached(None, payloads)
            else:
                warning(
                    "there appears to be no payloads stored in the database, to create payloads use the"
                    " following options:")
                proc = subprocess.check_output(
                    ["python", "whatwaf.py", "--help"])
                parsed_help = CmdLineParser.parse_help_menu(
                    proc, "encoding options:", "output options:")
                print(parsed_help)
            exit(0)

        # 指定了 -uC --view-url-cache, 这个选项仅仅只展示 cached_url 表的内容
        if opts.viewUrlCache:
            cached_urls = fetch_data(cursor, table_name='cached_urls')
            display_cached(cached_urls, None)
            exit(0)

        # 指定了 -e --encode
        # -e PAYLOAD [TAMPER-SCRIPT-LOAD-PATH ...], --encode PAYLOAD [TAMPER-SCRIPT-LOAD-PATH ...]
        # 这个地方 没有说 要如何指定 payload 和 payload 的路径, 先丢着
        if opts.encodePayload is not None:
            payload = opts.encodePayload[0]
            # opt.encodePayload[1:] -> payload 的加载路径, 例如 tampers.lowlevelunicodecharencode
            load_path = opts.encodePayload[1:]
            # 有可能加载好几个 payload 路径
            payload_list = []
            for load in load_path:
                try:
                    # encode(payload, script) 参数, script 应该就是 payload 位置参数
                    # eccode() 函数返回的是 根据 payload 产生的 绕过 字符串
                    payload = encode(payload, load)
                    payload_list.append(payload)
                except (AttributeError, ImportError):
                    warning(
                        "invalid load path given: '{}', skipping it and continuing"
                        .format(load))

            success("encoded successfully:")
            print spacer
            result = False
            for i, payload in enumerate(payload_list):
                # 上面得到 encoded successfully 之后,就把 payload 写入 database
                result = insert_payload(payload, cursor)
                print "{}{}".format(colored("#" + str(i) + " ", 'white'),
                                    payload)
            print spacer

            if result:
                info("payload has been cached for future use")
                exit(0)
            else:
                fatal("payload throwing error, see below")
                print colored(result, 'red')
                exit(1)

        # 指定 -el --encode-list 指定 payload 文件, payload 要用一行一行的隔开
        # -el PATH TAMPER-SCRIPT-LOAD-PATH, --encode-list PATH TAMPER-SCRIPT-LOAD-PATH
        if opts.encodePayloadList is not None:
            try:
                file_path, load_path = opts.encodePayloadList
                info(
                    "encoding payloads from given file '{}' using given tamper '{}'"
                    .format(colored(file_path, 'white'),
                            colored(load_path, 'white')))

                with open(file_path) as payloads:
                    # encode(payload, tamper_path)
                    encoded = [
                        encode(p.strip(), load_path)
                        for p in payloads.readlines()
                    ]

                    # 如果指定了 --save FILENAME
                    if opts.saveEncodedPayloads is not None:
                        with open(opts.saveEncodedPayloads, "a+") as save:
                            for item in encoded:
                                save.write(item + "\n")
                        success(
                            "saved encoded payloads to file '{}' successfully".
                            format(opts.saveEncodedPayloads))
                    else:
                        success("payloads encoded successfully:")
                        print(spacer)
                        for i, item in enumerate(encoded, start=1):
                            # 写入数据库
                            insert_payload(item, cursor)
                            print("{} {}".format(
                                colored("#" + str(i), 'white'), item))
                        print(spacer)
                info("payloads have been cached for future use")
            except IOError:
                fatal(
                    "provided file '{}' appears to not exist, check the path and try again"
                    .format(file_path))
            except (AttributeError, ImportError):
                fatal(
                    "invalid load path given, check the load path and try again"
                )
            exit(0)

        # 暂时先屏蔽
        # 指定了 --update
        # if opts.updateWhatWaf:
        #     info("update in progress")
        #     cmd = shlex.split("git pull origin master")
        #     subprocess.call(cmd)
        #     exit(0)

        # 指定了 --tampers
        # 这个 options 的命令是 列出所有的 tamper 可用列表
        if opts.listEncodingTechniques:
            info("gathering available tamper script load paths")
            # 返回的是所有的 tamper 的名字的集合 -> set()
            # is_tampers=True 就是返回 tampers 目录下的所有 tamper 名字集合
            # is_wafs=True 就是返回 plugins 目录下的所有 plugin 名字的集合

            print spacer
            tamper_list = get_encoding_list(TAMPERS_DIRECTORY,
                                            is_tampers=True,
                                            is_wafs=False)
            for tamper in sorted(tamper_list):
                print(tamper)
            print spacer
            exit(0)

        # 指定了 --wafs
        # 列出所有的 plugins 目录下的所有的 列表
        if opts.viewPossibleWafs:
            import importlib

            info("gathering a list of possible detectable wafs")

            print spacer
            wafs_list = get_encoding_list(PLUGINS_DIRECTORY,
                                          is_tampers=False,
                                          is_wafs=True)
            for i, waf in enumerate(wafs_list, start=1):
                try:
                    imported = importlib.import_module(waf)
                    print("{}".format(imported.__product__))
                except ImportError:
                    pass
            print spacer
            exit(0)

        # 在运行大型扫面之前先检查 更新, 先暂时关闭
        # gotta find a better way to check for updates so im a hotfix it
        # info("checking for updates")
        # check_version()

        # -Y --yaml sendToYAML
        # -C --cvs sendToCSV
        # -J --json sendToJSON
        format_opts = [opts.sendToYAML, opts.sendToCSV, opts.sendToJSON]
        # 指定了 -F --format
        if opts.formatOutput:
            amount_used = 0
            for item in format_opts:
                if item is True:
                    amount_used += 1
            if amount_used > 1:
                warning(
                    "multiple file formats have been detected, there is a high probability that this will cause "
                    "issues while saving file information. please use only one format at a time"
                )
            elif amount_used == 0:
                warning(
                    "output will not be saved to a file as no file format was provided. to save output to file "
                    "pass one of the file format flags (eg `-J` for JSON format)"
                )
        elif any(format_opts) and not opts.formatOutput:
            warning(
                "you've chosen to send the output to a file, but have not formatted the output, no file will be saved "
                "do so by passing the format flag (eg `-F -J` for JSON format)"
            )

        # 指定了 --skip skipBypassChecks 和 --tamper-int amountOfTampersToDisplay
        if opts.skipBypassChecks and opts.amountOfTampersToDisplay is not None:
            warning(
                "you've chosen to skip bypass checks and chosen an amount of tamper to display, tampers will be skipped"
            )

        # there is an extra dependency that you need in order
        # for requests to run behind socks proxies, we'll just
        # do a little check to make sure you have it installed
        # --tor     opt.runBehindTor
        # --proxy   opt.runBehindProxy
        # --tor --proxy 不为空 and socks in opt.runBehindProxy
        # 如果指定了 --tor --proxy --proxy 必须为 sock 就导入 socks 模块
        if opts.runBehindTor or opts.runBehindProxy is not None and "socks" in opts.runBehindProxy:
            try:
                import socks
            except ImportError:
                # if you don't we will go ahead and exit the system with an error message
                error(
                    "to run behind socks proxies (like Tor) you need to install pysocks `pip install pysocks`, "
                    "otherwise use a different proxy protocol")
                sys.exit(1)

        # configure_request_headers(user_agent=None, proxy=None, random_agent=False, tor=None, tor_port=9050)
        # return proxy, user_agent
        # configure_request_headers 判断如果没有指定 --proxy 会出现警告信息, 但是不会出错
        # 配置请求头和User Agent, 检查是否符合规范的, 符合的会返回 proxy 和 user_agent
        proxy, user_agent = configure_request_headers(
            # opts.usePersonalAgent -pa
            user_agent=opts.usePersonalAgent,
            # opts.runBehindProxy --proxy
            proxy=opts.runBehindProxy,
            # opts.useRandomAgent -ra 从 whatwaf/content/data/user_agents.txt 中随机挑选一个作为 USER_AGENT 客户请求头
            random_agent=opts.useRandomAgent,
            # opt.runBehindTor --tor, tor 默认是 socket5://127.0.0.1:9050,
            # 所以如果要指定使用 tor 代理, 直接 --tor 用默认的即可
            tor=opts.runBehindTor,
            # opt.configTorPort -tP --tor-port default=9050
            tor_port=opts.configTorPort)

        # 这个要先丢着
        # 如果指定了 --tor 就根据 https://check.torproject.org response返回的信息确定是否启用了 Tor
        # if opts.runBehindTor:
        #     import re
        #
        #     info("checking Tor connection")
        #     check_url = "https://check.torproject.org/"
        #     check_regex = re.compile("This browser is configured to use Tor.", re.I)
        #
        #     # 这里没判断,如果没有指定 --proxy 应该是要报错的
        #     # get_page() 是使用request 模块请求, BeautifulSoup 解析的
        #     _, _, content, _ = get_page(check_url, proxy=proxy, user_agent=user_agent)
        #
        #     if check_regex.search(str(content)) is not None:
        #         success("it appears that Tor is working properly")
        #     else:
        #         warning("it appears Tor is not configured properly")

        # 指定 -p --payload
        # 直说要使用 payload, 却没有说明如何使用 payload 用哪些payload
        if opts.providedPayloads is not None:
            # 如果指定了多个 payload 就丢入 payload_list 列表
            # 支持输入多个 payload 用逗号隔开存入 payload_list
            payload_list = [
                p.strip() if p[0] == " " else p
                for p in str(opts.providedPayloads).split(",")
            ]
            info("using provided payloads")
        elif opts.payloadList is not None:
            # 如果指定了 payload list 文件
            # --pl PAYLOAD-LIST-PATH
            try:
                open(opts.payloadList).close()
            except:
                fatal(
                    "provided file '{}' does not exists, check the path and try again"
                    .format(opts.payloadList))
                exit(1)
            payload_list = [
                p.strip("\n") for p in open(opts.payloadList).readlines()
            ]
            info("using provided payload file '{}'".format(opts.payloadList))
        else:
            # 如果都没有指定, 就使用默认的 whatwaf/content/data/default_payloads.lst 文件
            payload_list = WAF_REQUEST_DETECTION_PAYLOADS
            info("using default payloads")

        # 如果指定了 verbose
        if opts.runInVerbose:
            for payload in payload_list:
                info("using payload: {}".format(payload))

        # 指定了 --fingerprint 会保存指纹
        if opts.saveFingerprints:
            warning(
                "fingerprinting is enabled, all fingerprints (WAF related or not) will be saved for further analysis "
                "if the fingerprint already exists it will be skipped")

        # 指定了 --traffic FILENAME
        if opts.trafficFile is not None:
            info("saving HTTP traffic to '{}'".format(opts.trafficFile))
        # 指定了 --throttle INT, default 0
        if opts.sleepTimeThrottle != 0:
            info("sleep throttle has been set to {}s".format(
                opts.sleepTimeThrottle))

        try:
            if opts.postRequest:
                request_type = "POST"
            else:
                request_type = "GET"

            request_count = 0

            # -u --url
            if opts.runSingleWebsite:
                # opt.forceSSL --force-ssl
                # normalization_url(url, ssl=False) -> 是实现自动添加 http 或者 https 头的
                url_to_use = normalization_url(opts.runSingleWebsite,
                                               ssl=opts.forceSSL)

                # 在指定了 -u 的前提下, 指定 -c --url-cache default=False, 默认检查
                # if opts.checkCachedUrls:

                # check_url_against_cached(url, cursor) 如果 cached_urls 表里面有 给的 url 参数的话,
                # 就返回这个 url 在数据库中的数据行, 如果没有就是返回 Null
                checked_results = check_url_cached(url_to_use, cursor)

                # 如果数据库里面有
                if checked_results is not None:
                    print(
                        RESULTS_TEMPLATE.format(
                            spacer,
                            # uri
                            str(checked_results[1]),
                            # Identified Protections
                            str(checked_results[2]),
                            # Identified Tampers
                            str(checked_results[3]),
                            # Identified Web Server
                            str(checked_results[4]),
                            spacer))
                    exit(0)

                # 在指定了 -u 的前提下, 指定 -T --test default = True, 默认 去测试, 不用特意指定
                # if opts.testTargetConnection:
                info("testing connection to target URL before starting attack")
                # opt.extraHeaders -H --headers 注意这个headers要字典类型
                # 给参数的时候要形如这样的:
                # --headers {"Content-Length": "23", "User-Agent": "python-requests/2.10.0"}
                results = test_target_connection(
                    url=url_to_use,
                    post_data=opts.postRequestData,
                    proxy=proxy,
                    user_agent=user_agent,
                    headers=opts.extraHeaders)
                if results == "nogo":
                    fatal(
                        "connection to target URL failed multiple times, check connection and try again"
                    )
                    exit(1)
                elif results == "acceptable":
                    warning(
                        "there appears to be some latency on the connection, this may interfere with results"
                    )
                else:
                    success("connection succeeded, continuing")

                info("running single web application '{}'".format(url_to_use))

                # 指定了 -u 然后发送请求, detection_main 是主请求函数
                # detection_main(url, payload_list, cursor, **kwargs)
                # detection_main 返回 response_count 总请求的数量
                amount_of_requests = detection_main(
                    url_to_use,
                    payload_list,
                    cursor,
                    request_type=request_type,
                    post_data=opts.postRequestData,
                    user_agent=user_agent,
                    # --headers 后面要跟字典参数
                    provided_headers=opts.extraHeaders,
                    proxy=proxy,
                    verbose=opts.runInVerbose,
                    skip_bypass_check=opts.skipBypassChecks,
                    # verifyNumber --verify-num INT
                    verification_number=opts.verifyNumber,
                    # --fingerprint
                    # fingerprint_waf=opts.saveFingerprints,
                    formatted=opts.formatOutput,
                    # --tamper-int INT
                    tamper_int=opts.amountOfTampersToDisplay,
                    use_json=opts.sendToJSON,
                    use_yaml=opts.sendToYAML,
                    use_csv=opts.sendToCSV,
                    # --traffic FILENAME
                    traffic_file=opts.trafficFile,
                    throttle=opts.sleepTimeThrottle,
                    request_timeout=opts.requestTimeout,
                    # -W --determine-webserver default=False
                    # 这个应该默认开启
                    # determine_server=opts.determineWebServer,
                    # 线程数
                    threaded=opts.threaded,
                    #  --force-file default=False
                    force_file_creation=opts.forceFileCreation,
                    # -o --output
                    save_file_copy_path=opts.outputDirectory)

                request_count = amount_of_requests if amount_of_requests is not None else request_count

            elif any(
                    o is not None
                    for o in [opts.runMultipleWebsites, opts.burpRequestFile]):
                # 如果不指定 -u 而是指定了 -l --list 或者 --burp FILE-PATH
                info("reading from '{}'".format(opts.runMultipleWebsites
                                                or opts.burpRequestFile))

                try:
                    open(opts.runMultipleWebsites or opts.burpRequestFile)
                except IOError:
                    fatal("file: '{}' did not open, does it exist?".format(
                        opts.runMultipleWebsites))
                    exit(-1)

                if opts.runMultipleWebsites is not None:
                    # 需要检测的 url 列表
                    site_runners = []

                    with open(opts.runMultipleWebsites) as urls:
                        for url in urls:
                            possible_url = normalization_url(url.strip(),
                                                             ssl=opts.forceSSL)

                            if opts.checkCachedUrls:
                                url_is_cached = check_url_cached(
                                    possible_url, cursor)

                                if url_is_cached is not None:
                                    # 数据库里面有
                                    print(
                                        RESULTS_TEMPLATE.format(
                                            "-" * 20, str(url_is_cached[1]),
                                            str(url_is_cached[2]),
                                            str(url_is_cached[3]),
                                            str(url_is_cached[4]), "-" * 20))

                                else:
                                    site_runners.append(possible_url)
                            else:
                                site_runners.append(possible_url)
                elif opts.burpRequestFile is not None:
                    # parse_burp_request return: retval -> 包含 url 的列表
                    site_runners = parse_burp_request(opts.burpRequestFile)
                else:
                    site_runners = []

                if len(site_runners) == 0:
                    fatal("no targets parsed from file, exiting")
                    exit(1)
                else:
                    info("parsed a total of {} target(s) from file".format(
                        len(site_runners)))

                for i, url in enumerate(site_runners, start=1):
                    if opts.testTargetConnection:
                        info(
                            "testing connection to target URL before starting attack"
                        )
                        results = test_target_connection(
                            url,
                            proxy=proxy,
                            user_agent=user_agent,
                            headers=opts.extraHeaders)
                        if results == "nogo":
                            fatal(
                                "connection to target URL failed multiple times, check connection and try again"
                            )
                            exit(1)
                        elif results == "acceptable":
                            warning(
                                "there appears to be some latency on the connection, this may interfere with results"
                            )
                        else:
                            success("connection succeeded, continuing")

                    info("currently running on site #{} ('{}')".format(i, url))
                    requests = detection_main(
                        url,
                        payload_list,
                        cursor,
                        user_agent=user_agent,
                        proxy=proxy,
                        verbose=opts.runInVerbose,
                        skip_bypass_check=opts.skipBypassChecks,
                        verification_number=opts.verifyNumber,
                        formatted=opts.formatOutput,
                        tamper_int=opts.amountOfTampersToDisplay,
                        use_json=opts.sendToJSON,
                        use_yaml=opts.sendToYAML,
                        use_csv=opts.sendToCSV,
                        # fingerprint_waf=opts.saveFingerprints,
                        provided_headers=opts.extraHeaders,
                        traffic_file=opts.trafficFile,
                        throttle=opts.sleepTimeThrottle,
                        request_timeout=opts.requestTimeout,
                        post_data=opts.postRequestData,
                        request_type=request_type,
                        # check_server=opts.determineWebServer,
                        threaded=opts.threaded,
                        force_file_creation=opts.forceFileCreation,
                        save_file_copy_path=opts.outputDirectory)
                    request_count = request_count + requests if requests is not None else request_count
                    print("\n\b")
                    time.sleep(0.5)

            if request_count != 0:
                info("total requests sent: {}".format(request_count))
            else:
                warning(
                    "request counter failed to count correctly, deactivating")

        except KeyboardInterrupt:
            fatal("user aborted scanning")
        except InvalidURLProvided:
            fatal(
                "the provided URL is unable to be validated, check the URL and try again (you may need to unquote the "
                "HTML entities)")
        except Exception as e:
            # traceback 是跟踪堆栈的
            import traceback

            sep = colored("-" * 30, 'white')

            fatal(
                "WhatWaf has caught an unhandled exception with the error message: '{}'."
                .format(str(e)))

            exception_data = "Traceback (most recent call):\n{}{}".format(
                "".join(traceback.format_tb(sys.exc_info()[2])), str(e))

            error("\n{}\n{}\n{}".format(sep, exception_data, sep))

            request_issue_creation(exception_data)