-
Notifications
You must be signed in to change notification settings - Fork 33
/
Copy pathidentify-header-maxforwards-abuse.py
92 lines (79 loc) · 4.57 KB
/
identify-header-maxforwards-abuse.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#!/usr/bin/env python
import requests
import argparse
import re
from termcolor import colored
from tabulate import tabulate
"""
Script to detect the different proxies in place by abusing the "Max-Forwards" HTTP request header.
Based on the following sources:
https://twitter.com/irsdl/status/1337299267652825088
https://www.agarri.fr/blog/archives/2011/11/12/traceroute-like_http_scanner/index.html
https://github.com/righettod/toolbox-pentest-web/issues/47
Documentation about this header:
http://webconcepts.info/concepts/http-header/Max-Forwards
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Max-Forwards
"The 'Max-Forwards' header field provides a mechanism with the TRACE and OPTIONS request methods
to limit the number of times that the request is forwarded by proxies."
Dependencies:
pip3 install requests termcolor tabulate
"""
# Config
# Disable TLS warning when validation is disabled when requests is used
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
# Constants
PROXIES = {}
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"
REFERENCE_HEADER_NAMES = ["server", "content-type", "via", "x-forwarded-for"]
REFERENCE_HTML_TAG_NAMES = {"title": r'<title.*?>(.*?)<\/title>'}
METHODS = ["GET", "TRACE", "OPTIONS"]
class ResponseData:
def __init__(self, method, response, max_forwards_value):
self.method = method
self.return_code = response.status_code
self.reference_headers_values = {}
self.reference_html_values = {}
self.max_forwards_value = max_forwards_value
content = response.text
for header in response.headers:
if header.lower() in REFERENCE_HEADER_NAMES:
self.reference_headers_values[header] = response.headers[header]
for tag_name, tag_name_regex in REFERENCE_HTML_TAG_NAMES.items():
tag_name_occurences = re.findall(tag_name_regex, content, re.IGNORECASE)
if len(tag_name_occurences) > 0:
self.reference_html_values[tag_name] = " ".join(tag_name_occurences)
def main(target_url, forwards_count):
print(colored(f"[+] Execution context:", "yellow"))
print(f"Full URL : {target_url}")
print(f"Forwards count : {forwards_count}")
print(f"Proxy : {PROXIES}")
print(colored("[+] Send crafted requests trying the different forward counts...", "yellow"))
results = []
req_headers = {"Max-Forwards": 0, "User-Agent": USER_AGENT}
for method in METHODS:
for t in range(forwards_count):
req_max_forwards_value = str((t+1)) # Range is 0 indexed
req_headers["Max-Forwards"] = req_max_forwards_value
print(f"\rTesting '{method}' with 'Max-Forwards' to {req_max_forwards_value}", end="", flush=True)
try:
response = requests.request(method, target_url, headers=req_headers, verify=False, proxies=PROXIES, allow_redirects=False, timeout=10)
results.append(ResponseData(method, response, req_max_forwards_value))
except:
pass
print(f"\rDone{' ' * 60}")
print(colored("[+] Results, if a methods does not appear then requests using it were rejected:", "yellow"))
data = []
data.append(["Method", "Max-Forwards", "HTTP Code", "Reference response headers values", "References response body HTML values"])
for result in results:
data.append([result.method, result.max_forwards_value, result.return_code, result.reference_headers_values, result.reference_html_values])
print(tabulate(data, headers="firstrow", tablefmt="github", numalign="center", stralign="left"))
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Script to detect the different proxies in place by abusing the 'Max-Forwards' HTTP request header.")
required_params = parser.add_argument_group("required named arguments")
required_params.add_argument("-t", action="store", dest="target_url", help="Target URL (ex: 'https://righettod.eu').", required=True)
parser.add_argument("-f", action="store", dest="forwards_count", type=int, help="Total count of forwards to test (ex: 5).", required=False, default="5")
parser.add_argument("-x", action="store", dest="proxy", help="Proxy to use for all probe requests (ex: 'http://127.0.0.1:8080', default to no proxy).", required=False, default=None)
args = parser.parse_args()
if args.proxy is not None:
PROXIES = {"http": args.proxy, "https": args.proxy}
main(args.target_url, args.forwards_count)