Ejemplo n.º 1
0
    def threader(self):
        # not sure why this is wrapped in parentheses
        while True:
            url_thread, waf_vector = self.threading_queue.get()
            try:
                if self.verbose:
                    debug("trying: '{}'".format(url_thread))

                response_retval = get_page(
                    url_thread,
                    user_agent=self.agent,
                    proxy=self.proxy,
                    provided_headers=self.provided_headers,
                    throttle=self.throttle,
                    timeout=self.timeout,
                    request_method=self.request_type,
                    post_data=self.post_data)
                self.response_retval_list.append(response_retval)

                _, response_status_code, _, _ = response_retval
                if self.verbose:
                    info('response status code: {}'.format(
                        response_status_code))

            except Exception as e:
                if "ECONNRESET" in str(e):
                    warning(
                        "possible network level firewall detected (hardware), received an aborted connection"
                    )
                    self.response_retval_list.append(None)
                else:
                    error(
                        "failed to obtain target meta-data with payload {}, error: '{}'"
                        .format(waf_vector.strip(), str(e)))
                    self.response_retval_list.append(None)

                    # 暂时关闭
                    # if self.save_fingerprint:
                    #     create_fingerprint(
                    #         self.url,
                    #         self.response_retval_list[0][2],
                    #         self.response_retval_list[0][1],
                    #         self.response_retval_list[0][3],
                    #         req_data=self.response_retval_list[0][0],
                    #         speak=True
                    #     )

            self.threading_queue.task_done()
Ejemplo n.º 2
0
    def get_response(self):
        for i, waf_vector in enumerate(self.payloads):
            # 如果给出的 url 没有 * 可 payload 点, 就直接追加到 url 的后面
            if not self.placement:
                primary_url = self.url + "{}".format(waf_vector)
            else:
                # 那就是在一个 url 中只会有一个 *, 只有一个可 payload 的点, 会把 * 替换成 payload
                # 这里需要加强一下, 如果有 多个 *
                url = self.url.split("*")
                primary_url = "{}{}{}".format(url[0], waf_vector,
                                              url[len(url) - 1])
            # secondary_url 是爆破 admin 路径的 但是只选择了一个, 就没什么卵用
            secondary_url = strip_url(self.url)
            secondary_url = "{}//{}".format(secondary_url[0], secondary_url[1])
            secondary_url = "{}/{}".format(secondary_url,
                                           random.choice(RAND_HOMEPAGES))

            # 如果指定了 --verbose, 就打印出传入的 payload 参数
            if self.verbose:
                payload("using payload: {}".format(waf_vector.strip()))

            try:
                if self.verbose:
                    debug("trying: '{}'".format(primary_url))
                response_retval = get_page(
                    # get_page return 形如 ->
                    # ('GET https://example.org', '200 OK', 'soup对象', "{'User_Agent': 'f**k'}")
                    primary_url,
                    user_agent=self.agent,
                    proxy=self.proxy,
                    provided_headers=self.provided_headers,
                    throttle=self.throttle,
                    timeout=self.timeout,
                    request_method=self.request_type,
                    post_data=self.post_data)
                self.response_retval_list.append(response_retval)

                _, response_status_code, _, _ = response_retval
                if self.verbose:
                    info("response status code: {}".format(
                        response_status_code))

                if self.verbose:
                    debug("trying: {}".format(secondary_url))

                # 请求给出 url 的主目录, 用了admin爆破字典, 因为是从 RAND_HOMEPAGES 中随机抽取了一个, 所以不一定会成功返回 200
                # response_retval 是一个列表, 列表的元素是元组
                response_retval = get_page(
                    # return -> ('GET https://example.org', '200 OK', 'soup对象', "{'User_Agent': 'f**k'}")
                    secondary_url,
                    user_agent=self.agent,
                    proxy=self.proxy,
                    provided_headers=self.provided_headers,
                    # throttle -> Provide a sleep time per request (*default=0), 发送各种请求的时间间隔
                    throttle=self.throttle,
                    timeout=self.timeout,
                    request_method=self.request_type,
                    post_data=self.post_data)
                self.response_retval_list.append(response_retval)

                _, response_status_code, _, _ = response_retval
                if self.verbose:
                    info('response status code: {}'.format(
                        response_status_code))

            except Exception as e:
                # 事实上应该返回各种请求码, 例如拒绝, 找不到页面, 而不应该出错
                if "ECONNRESET" in str(e):
                    warning(
                        "possible network level firewall detected (hardware), received an aborted connection"
                    )
                    self.response_retval_list.append(None)
                else:
                    error(
                        "failed to obtain target meta-data with payload {}, error: '{}'"
                        .format(waf_vector.strip(), str(e)))
                    self.response_retval_list.append(None)

            # 暂时关闭
            # 如果指定了 --fingerprint, 为什么只保存一个?
            # 这里只会保存第一个 payload 请求和爆破 index 页面请求
            # if self.save_fingerprint:
            #     create_fingerprint(
            #         self.url,
            #         # get_page return 形如 ->
            #         # ('GET https://example.org', '200 OK', 'soup对象', "{'User_Agent': 'f**k'}")
            #         # Soup object
            #         response_retval[0][2],
            #         # response code
            #         response_retval[0][1],
            #         # User Agent
            #         response_retval[0][3],
            #         # 形如 -> GET https://www.example.com
            #         req_data=response_retval[0][0],
            #         speak=True
            #     )

        # get_page return 形如多个 -> ('GET https://example.org', '200 OK', 'soup对象', "{'User_Agent': 'f**k'}")
        # 这样的集合
        # 有几个 payload 就请求了几次
        return self.response_retval_list
Ejemplo n.º 3
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)
Ejemplo n.º 4
0
def configure_request_headers(user_agent=None,
                              proxy=None,
                              random_agent=False,
                              tor=None,
                              tor_port=9050):
    """
    configure the HTTP request headers with a user defined
    proxy, Tor, or a random User-Agent from the user-agent
    file

    :param user_agent: User Agent from custom request
    :param proxy: Whether use proxy, default None
    :param random_agent: Whether use random User Agent, strong recommend
    :param tor: Whether use tor proxy, default None
    :param tor_port: setting custom tor port
    :return: proxy, user_agent
    """

    supported_proxies = ("socks5", "socks4", "http", "https")

    invalid_msg = "invalid switches detected, switch {} cannot be used in conjunction with switch {}"
    proxy_msg = "running behind proxy '{}'"

    # --proxy 和 --tor 不能同时被设置
    if proxy is not None and tor:
        error(invalid_msg.format("--tor", "--proxy"))
        exit(1)

    # --ra 和 --pa 不能在一起
    if user_agent is not None and random_agent:
        error(invalid_msg.format("--ra", "--pa"))
        exit(1)

    # tor 默认是 socket5://127.0.0.1:9050 端口
    if tor:
        proxy = "socks5://127.0.0.1:{}".format(tor_port)

    if user_agent is None:
        user_agent = get_random_agent()

    if random_agent:
        # 从 whatwaf/content/data/user_agent.txt 中随便挑一个 user_agent 请求头
        user_agent = get_random_agent()

    # proxy 不为空
    if proxy is not None:
        if any(item in proxy for item in supported_proxies):
            info(proxy_msg.format(proxy))
        else:
            error(
                "you did not provide a supported proxy protocol, "
                "supported protocols are '{}'. check your proxy and try again".
                format(", ".join([p for p in supported_proxies])))
            exit(1)
    else:
        # proxy 为 Null
        warning(
            "it is highly advised to use a proxy when using WhatWaf. do so by passing the proxy flag "
            "(eg `--proxy http://127.0.0.1:9050`) or by passing the Tor flag (eg `--tor`)"
        )

    #  如果 user_agent 请求头不为空
    if user_agent is not None:
        info("using User-Agent '{}'".format(user_agent))

    return proxy, user_agent
Ejemplo n.º 5
0
def write_to_file(filename, path, data, **kwargs):
    """
    write the data to a file

    :param filename:
    :param path:
    :param data:
    :param kwargs:
    :return:
    """

    write_yaml = kwargs.get("write_yaml", False)
    write_json = kwargs.get("write_json", False)
    write_csv = kwargs.get("write_csv", False)
    save_copy = kwargs.get("save_copy_to", None)

    full_path = "{}/{}".format(path, filename)

    if not os.path.exists(path):
        os.makedirs(path)
    if write_json and not write_yaml and not write_csv:
        with open(full_path, "a+") as _json:
            _json_data = json.loads(data)
            json.dump(_json_data, _json, sort_keys=True, indent=4)
    elif write_yaml and not write_json and not write_csv:
        try:
            # there is an extra dependency that needs to be installed for you to save to YAML
            # we'll check if you have it or not
            import yaml

            with open(full_path, "a+") as _yaml:
                _yaml_data = yaml.load(data)
                yaml.dump(_yaml_data, _yaml, default_flow_style=False)
        except ImportError:
            # if you don't we'll just skip the saving and warn you
            warning(
                "you do not have the needed dependency to save YAML data, to install the dependency run "
                "`pip install pyyaml`, skipping file writing")
            return None
    elif write_csv and not write_json and not write_yaml:
        import csv

        _json_data = json.loads(data)
        try:
            csv_data = [
                ["url", "is_protected", "protection", "working_tampers"],
                [
                    _json_data["url"], _json_data["is protected"],
                    _json_data["identified firewall"] if
                    _json_data["identified firewall"] is not None else "None",
                    _json_data["apparent working tampers"]
                    if _json_data["apparent working tampers"] is not None else
                    "None"
                ]
            ]
        except KeyError:
            pass
        with open(full_path, "a+") as _csv:
            writer = csv.writer(_csv)
            writer.writerows(csv_data)
    if save_copy is not None:
        import shutil
        try:
            shutil.copy(full_path, save_copy)
            info("copy of file saved to {}".format(save_copy))
        except Exception:
            error("failed to save copy of file, do you have permissions?")

    return full_path