├── README.md └── fuzzyguard.py /README.md: -------------------------------------------------------------------------------- 1 | # FuzzyGuard 2 | 3 | FuzzyGuard is a script designed to simplify the process of fuzzing websites using FFUF.
4 | By automatically generating FFUF commands based on the target website, FuzzyGuard eliminates the need to manually add filters when dealing with an influx of 200 status codes.
5 | 6 | ``` 7 | > cat subdomains.txt | python3 fuzzyguard.py --silent 8 | 9 | ffuf -u https://example.com/FUZZ -w WORDLIST 10 | ffuf -u https://example.com/FUZZ -w WORDLIST -fl 550-560 -fw 1619 -fs 18000-19000 11 | ffuf -u https://example.com/FUZZ -w WORDLIST -fl 169 -fw 792 -fs 160000-170000 12 | ``` 13 | ## Usage 14 | The script only has 2 options : 15 | ``` 16 | , _____ 17 | _.-"` `'-. | __|_ _ ___ ___ _ _ 18 | '._ __{}_( | __| | |- _|- _| | | 19 | |'--.__\ |__| |___|___|___|_ | 20 | ( =_\ = _____ |___| 21 | | _ | | __|_ _ ___ ___ _| | 22 | )\___/ | | | | | .'| _| . | 23 | .--'`:._] |_____|___|__,|_| |___| 24 | / \ '-. Made By: @castilho101 25 | 26 | --silent Silent mode 27 | --ffuf-flags FFUF_FLAGS Extra FFUF flags, ex: --ffuf-extra "-mc all -fc 404" 28 | ``` 29 | -------------------------------------------------------------------------------- /fuzzyguard.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import math 3 | import requests 4 | import argparse 5 | import concurrent.futures 6 | 7 | from collections import Counter 8 | 9 | # Global Variables 10 | SILENT_MODE = 1 11 | FFUF_EXTRA_FLAG = "" 12 | RANDOM_ENDPOINT = "THIS_ENDPOINT_DOES_NOT_EXIST" 13 | 14 | # Class for Colors (obviously) 15 | class Color: 16 | def red(message): return f"\033[91m{message}\033[0m" 17 | def bold(message): return f"\033[1m{message}\033[0m" 18 | def green(message): return f"\033[92m{message}\033[0m" 19 | def yellow(message): return f"\033[93m{message}\033[0m" 20 | 21 | # Function for ASCII Art 22 | def logo(): 23 | 24 | print(Color.bold(""" 25 | , _____ 26 | _.-"` `'-. | __|_ _ ___ ___ _ _ 27 | '._ __{}_( | __| | |- _|- _| | | 28 | |'--.__\\ |__| |___|___|___|_ | 29 | ( =_\ = _____ |___| 30 | | _ | | __|_ _ ___ ___ _| | 31 | )\___/ | | | | | .'| _| . | 32 | .--'`:._] |_____|___|__,|_| |___| 33 | / \ '-. Made By: @castilho101 34 | """)) 35 | 36 | # Generate the ffuf command string for the target 37 | def generate_ffuf_commands(target, ffuf_filter_flags): 38 | 39 | prefix = "" 40 | ffuf_command = f"ffuf -u {target}/FUZZ -w WORDLIST {FFUF_EXTRA_FLAG} {ffuf_filter_flags[1:]}" 41 | 42 | if not SILENT_MODE: 43 | prefix = f"{Color.bold('[')}{Color.green('+')}{Color.bold(']')} " 44 | 45 | print(prefix + ffuf_command) 46 | 47 | # Calculate the range of values 48 | def get_ranges_from_value(value): 49 | 50 | # Determine the divisor based on the number of digits in the value 51 | divisor = 10 ** (len(str(value)) - 2) 52 | 53 | # Calculate the minimum and maximum range values 54 | min_range = math.floor(value / divisor) * divisor 55 | max_range = min_range + divisor 56 | 57 | return f"{min_range}-{max_range}" 58 | 59 | 60 | # Validate the results of the target analysis 61 | def validate_results(target, target_num_lines, target_num_words, target_size_bytes): 62 | 63 | # Variables 64 | ffuf_filter_flags = "" 65 | 66 | # Create a list of tuples with the flag identifier and the Counter object 67 | counters = [ 68 | ('-fl', Counter(target_num_lines)), 69 | ('-fw', Counter(target_num_words)), 70 | ('-fs', Counter(target_size_bytes)), 71 | ] 72 | 73 | # Iterate over the counters 74 | for flag, counter in counters: 75 | 76 | # Get the most common values 77 | most_common = counter.most_common() 78 | 79 | # If there is only one most common value, add the corresponding filter flag and value 80 | if len(most_common) == 1: 81 | flag_value = most_common[0][0] 82 | ffuf_filter_flags += f" {flag} {flag_value}" 83 | else: 84 | # Create a list of values from the most_common tuples 85 | values_list = [] 86 | for tuple_value in most_common: 87 | values_list.append(tuple_value[0]) 88 | 89 | # Get the range for the filter flag 90 | ranges = get_ranges_from_value(min(values_list)) 91 | ffuf_filter_flags += f" {flag} {ranges}" 92 | 93 | # Generate ffuf command for target 94 | generate_ffuf_commands(target, ffuf_filter_flags) 95 | 96 | # Main Function 97 | def analyze_targets(target): 98 | 99 | # variables 100 | target_num_lines = set() 101 | target_num_words = set() 102 | target_size_bytes = set() 103 | 104 | # Request 15 times to get values to analyze 105 | for i in range(15): 106 | 107 | # Make GET request 108 | target_endpoint = target + f"/{RANDOM_ENDPOINT}" 109 | response = requests.get(target_endpoint) 110 | 111 | # Check response 112 | if response.status_code != 200: 113 | generate_ffuf_commands(target, "") 114 | break 115 | 116 | content = response.text 117 | 118 | # Get values for analyzies 119 | num_words = len(content.split(" ")) 120 | num_lines = len(content.split('\n')) 121 | size_bytes = len(content.encode('utf-8')) 122 | 123 | target_num_lines.add(num_lines) 124 | target_num_words.add(num_words) 125 | target_size_bytes.add(size_bytes) 126 | 127 | # Validate Results 128 | if len(target_num_lines): 129 | validate_results(target, target_num_lines, target_num_words, target_size_bytes) 130 | 131 | # Main function 132 | def main(): 133 | 134 | global FFUF_EXTRA_FLAG, SILENT_MODE 135 | 136 | # Script Arguments 137 | parser = argparse.ArgumentParser(description='FuzzyGuard for Automatic FFUF Fuzzing!') 138 | parser.add_argument('--ffuf-flags', dest='ffuf_flags', type=str, help='Extra FFUF flags, ex: --ffuf-extra "-mc all -fc 404"') 139 | parser.add_argument('--silent', dest='silent', action="store_true", help='Silent mode') 140 | 141 | # Parse and check Arguments 142 | args = parser.parse_args() 143 | 144 | if not args.silent: 145 | logo() 146 | SILENT_MODE = 0 147 | 148 | if args.ffuf_flags: FFUF_EXTRA_FLAG = args.ffuf_flags 149 | 150 | # See if we have data to analyze 151 | if not sys.stdin.isatty(): 152 | 153 | # List for each of the targets 154 | user_input = sys.stdin.read().split("\n") 155 | targets = list(filter(None, user_input)) 156 | 157 | # Create a thread for each URL 158 | with concurrent.futures.ThreadPoolExecutor() as executor: 159 | executor.map(analyze_targets, targets) 160 | 161 | else: 162 | # Exit if there's no STDIN 163 | print(f"{Color.bold('[')}{Color.red('!')}{Color.bold('] Please send me some targets!')}") 164 | exit() 165 | 166 | # yeah 167 | if __name__ == '__main__': 168 | main() 169 | --------------------------------------------------------------------------------