├── 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 |
--------------------------------------------------------------------------------