Last active
November 1, 2024 20:00
-
-
Save ndavison/298d11b3a77b97c908d63a345d3c624d to your computer and use it in GitHub Desktop.
Attempts to find hop-by-hop header abuse potential against the provided URL.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# github.com/ndavison | |
import requests | |
import random | |
import string | |
from argparse import ArgumentParser | |
parser = ArgumentParser(description="Attempts to find hop-by-hop header abuse potential against the provided URL.") | |
parser.add_argument("-u", "--url", help="URL to target (without query string)") | |
parser.add_argument("-x", "--headers", default="X-Forwarded-For", help="A comma separated list of headers to add as hop-by-hop") | |
parser.add_argument("-c", "--cache-test", action="store_true", help="Test for cache poisoning") | |
parser.add_argument("-d", "--disable-size-check", action="store_true", help="Don't compare response size when detecting changes between normal and hop-by-hop requests") | |
parser.add_argument("-v", "--verbose", action="store_true", help="More output") | |
args = parser.parse_args() | |
if not args.url: | |
print('Must supply a URL to target') | |
exit(1) | |
letters = string.ascii_lowercase | |
headers = { | |
'Connection': 'keep-alive, %s' % args.headers | |
} | |
params1 = { | |
'cb': ''.join(random.choice(letters) for i in range(10)) | |
} | |
params2 = { | |
'cb': ''.join(random.choice(letters) for i in range(10)) | |
} | |
# try a normal request and one with the hop-by-hop headers (and a cache buster to avoid accidental cache poisoning) | |
try: | |
if args.verbose: | |
print("Trying %s?%s" % (args.url, params1['cb'])) | |
res1 = requests.get(args.url, params=params1, allow_redirects=False) | |
if args.verbose: | |
print('Trying %s?%s with hop-by-hop headers "%s"' % (args.url, params2['cb'], args.headers)) | |
res2 = requests.get(args.url, headers=headers, params=params2, allow_redirects=False) | |
except requests.exceptions.ConnectionError as e: | |
print(e) | |
exit(1) | |
# did adding the HbH headers cause a different response? | |
if res1.status_code != res2.status_code or (not args.disable_size_check and len(res1.content) != len(res2.content)): | |
if res1.status_code != res2.status_code: | |
print('%s returns a %s, but returned a %s with the hop-by-hop headers of "%s"' % (args.url, res1.status_code, res2.status_code, args.headers)) | |
if not args.disable_size_check and len(res1.content) != len(res2.content): | |
print('%s was %s in response size, but was %s with the hop-by-hop headers of "%s"' % (args.url, len(res1.content), len(res2.content), args.headers)) | |
# if enabled, run the cache poison test by quering the HbH request's cache buster without the HbH headers and comparing status codes | |
if args.cache_test: | |
try: | |
res3 = requests.get(args.url, params=params2, allow_redirects=False) | |
except requests.exceptions.ConnectionError as e: | |
print(e) | |
exit(1) | |
if res3.status_code == res2.status_code: | |
print('%s?cb=%s poisoned?' % (args.url, params2['cb'])) | |
else: | |
print('No poisoning detected') | |
else: | |
if args.verbose: | |
print('No change detected requesting "%s" with the hop-by-hop headers "%s"' % (args.url, args.headers)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment