| 1 | + | import os |
| 2 | + | import threading |
| 3 | + | import ipaddress |
| 4 | + | from waybackpy import WaybackMachineAvailabilityAPI, exceptions |
| 5 | + | from endpoint import Endpoint |
| 6 | + | from logger import log |
| 7 | + | |
| 8 | + | |
| 9 | + | class Bypass: |
| 10 | + | _protocolVersions = ['HTTP/1.0', 'HTTP/2'] |
| 11 | + | _headersPath = ['X-Original-URL', 'X-Rewrite-URL', 'X-Override-URL'] |
| 12 | + | _hosts = ['', 'localhost', 'google.com', 'fake.org'] |
| 13 | + | _verbs = ['POST', 'PUT', 'PATCH', 'DELETE', |
| 14 | + | 'HEAD', 'OPTIONS', 'TRACE', |
| 15 | + | 'CONNECT', 'FAKE', 'GET'] |
| 16 | + | _agents = ['Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36', |
| 17 | + | 'Mozilla/5.0 (iPhone14,6; U; CPU iPhone OS 15_4 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/19E241 Safari/602.1', |
| 18 | + | 'Mozilla/5.0 (Linux; Android 12; SM-X906C Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36', |
| 19 | + | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246', |
| 20 | + | 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9', |
| 21 | + | 'Mozilla/5.0 (X11; CrOS x86_64 8172.45.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.64 Safari/537.36', |
| 22 | + | 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1', |
| 23 | + | 'Mozilla/5.0 (PlayStation; PlayStation 5/2.26) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.15', |
| 24 | + | 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)', |
| 25 | + | 'Fake User Agent'] |
| 26 | + | _headers = ['X-Originating-IP', 'X-Forwarded-For', 'X-Forwarded', 'Forwarded-For', |
| 27 | + | 'X-Forwarded-Host', 'X-Remote-IP', 'X-Remote-Addr', 'X-ProxyUser-Ip', |
| 28 | + | 'X-Original-URL', 'Client-IP', 'X-Client-IP', 'X-Host', |
| 29 | + | 'True-Client-IP', 'Cluster-Client-IP', 'X-ProxyUser-Ip', 'X-Custom-IP-Authorization'] |
| 30 | + | _commonLANs = ['10.64.8.0/23', '192.168.0.0/24'] |
| 31 | + | |
| 32 | + | def __init__(self, endpoint: Endpoint) -> None: |
| 33 | + | self._endpoint = endpoint |
| 34 | + | |
| 35 | + | @property |
| 36 | + | def endpoint(self) -> Endpoint: |
| 37 | + | return self._endpoint |
| 38 | + | |
| 39 | + | @endpoint.setter |
| 40 | + | def method(self, endpoint: Endpoint) -> None: |
| 41 | + | self._endpoint = endpoint |
| 42 | + | |
| 43 | + | def _testHTTPOrHTTPS(self) -> None: |
| 44 | + | log.info('Start HTTP or HTTPS Testing') |
| 45 | + | if self._endpoint.getSchema() == "https": |
| 46 | + | newUrl = self._endpoint.url.replace("https", "http") |
| 47 | + | else: |
| 48 | + | newUrl = self._endpoint.url.replace("http", "https") |
| 49 | + | log.info(f'GET request to New URL {newUrl}') |
| 50 | + | self._endpoint.makeRequest(url=newUrl, timeout=1) |
| 51 | + | log.info('End HTTP or HTTPS Testing') |
| 52 | + | |
| 53 | + | def _protocolFuzzing(self) -> None: |
| 54 | + | log.info('Start Protocol Fuzzing') |
| 55 | + | for protocolVersion in self._protocolVersions: |
| 56 | + | self._endpoint.makeRequest(protocolVersion=protocolVersion) |
| 57 | + | log.info('End Protocol Fuzzing') |
| 58 | + | |
| 59 | + | def _testIPAndCNAME(self) -> None: |
| 60 | + | log.info('Start IP or CNAME Testing') |
| 61 | + | try: |
| 62 | + | ip = f'{self._endpoint.getSchema()}://{self._endpoint.getIP()}' |
| 63 | + | log.info(f'Requesting via IP {ip}') |
| 64 | + | self._endpoint.makeRequest(url=ip) |
| 65 | + | except Exception as e: |
| 66 | + | log.warning( |
| 67 | + | f'[-] Exception {e} generated by IP {ip}') |
| 68 | + | try: |
| 69 | + | cname = f'{self._endpoint.getSchema()}://{self._endpoint.getDomain()}' |
| 70 | + | log.info(f'Requesting via CNAME {cname}') |
| 71 | + | self._endpoint.makeRequest(url=cname) |
| 72 | + | except Exception as e: |
| 73 | + | log.warning( |
| 74 | + | f'[-] Exception {e} generated by CNAME {cname}') |
| 75 | + | log.info('End IP or CNAME Testing') |
| 76 | + | |
| 77 | + | def _headersFuzzingPath(self) -> None: |
| 78 | + | path = self._endpoint.getPath() |
| 79 | + | log.info(f'Start Headers Fuzzing with Path {self._endpoint.getPath()}') |
| 80 | + | for header in self._headersPath: |
| 81 | + | self._endpoint.makeRequest(headers={header: path}) |
| 82 | + | log.info(f'End Headers Fuzzing with Path {path}') |
| 83 | + | |
| 84 | + | def _hostHeaderFuzzing(self) -> None: |
| 85 | + | log.info('Start Host Header Fuzzing') |
| 86 | + | for host in self._hosts: |
| 87 | + | self._endpoint.makeRequest(headers={'Host': host}) |
| 88 | + | log.info('End Host Header Fuzzing') |
| 89 | + | |
| 90 | + | def _verbFuzzing(self) -> None: |
| 91 | + | log.info('Start Verb Fuzzing') |
| 92 | + | for verb in self._verbs: |
| 93 | + | self._endpoint.makeRequest(method=verb) |
| 94 | + | log.info('Start Verb Fuzzing with X-HTTP-Method-Override Header') |
| 95 | + | for verb in self._verbs: |
| 96 | + | self._endpoint.makeRequest( |
| 97 | + | headers={'X-HTTP-Method-Override': verb}) |
| 98 | + | log.info('End Verb Fuzzing') |
| 99 | + | |
| 100 | + | def _userAgentFuzzing(self) -> None: |
| 101 | + | log.info('Start User Agent Fuzzing') |
| 102 | + | for agent in self._agents: |
| 103 | + | self._endpoint.makeRequest( |
| 104 | + | headers={'User-Agent': agent}) |
| 105 | + | log.info('End User Agent Fuzzing') |
| 106 | + | |
| 107 | + | def _headersIPFuzzing(self, ip: str = '127.0.0.1') -> None: |
| 108 | + | log.info(f'Start Headers Fuzzing with IP {ip}') |
| 109 | + | for header in self._headers: |
| 110 | + | self._endpoint.makeRequest(headers={header: ip}) |
| 111 | + | log.info(f'End HTTP Headers Fuzzing with IP {ip}') |
| 112 | + | |
| 113 | + | def _pathFuzzing(self) -> None: |
| 114 | + | log.info('Start Path Fuzzing') |
| 115 | + | pathVariants = [f'{self._endpoint.getMainUrl()}/%2e/{self._endpoint.getPath()}', |
| 116 | + | f'{self._endpoint.getMainUrl()}/%252e/{self._endpoint.getPath()}', |
| 117 | + | f'{self._endpoint.getMainUrl()}/{self._endpoint.getPath().upper()}', |
| 118 | + | f'{self._endpoint.url}/', |
| 119 | + | f'{self._endpoint.url}/.', |
| 120 | + | f'{self._endpoint.url}//', |
| 121 | + | f'{self._endpoint.getMainUrl()}//{self._endpoint.getPath()}', |
| 122 | + | f'{self._endpoint.getMainUrl()}//{self._endpoint.getPath()}//', |
| 123 | + | f'{self._endpoint.getMainUrl()}/./{self._endpoint.getPath()}/..', |
| 124 | + | f'{self._endpoint.getMainUrl()}/;/{self._endpoint.getPath()}', |
| 125 | + | f'{self._endpoint.getMainUrl()}/,;/{self._endpoint.getPath()}', |
| 126 | + | f'{self._endpoint.getMainUrl()}//;//{self._endpoint.getPath()}', |
| 127 | + | f'{self._endpoint.url}.json', |
| 128 | + | f'{self._endpoint.url}..;/', |
| 129 | + | f'{self._endpoint.getMainUrl()}/../{self._endpoint.getPath()}', |
| 130 | + | f'{self._endpoint.getMainUrl()}/..;/{self._endpoint.getPath()}', |
| 131 | + | f'{self._endpoint.url}%20/', |
| 132 | + | f'{self._endpoint.getMainUrl()}/%20{self._endpoint.getPath()}%20/', |
| 133 | + | f'{self._endpoint.url};/', |
| 134 | + | f'{self._endpoint.url}/~', |
| 135 | + | f'{self._endpoint.getMainUrl()}/./{self._endpoint.getPath()}/./', |
| 136 | + | f'{self._endpoint.url}?param', |
| 137 | + | f'{self._endpoint.url}#'] |
| 138 | + | for pathVariant in pathVariants: |
| 139 | + | self._endpoint.makeRequest(url=pathVariant) |
| 140 | + | log.info('End Path Fuzzing') |
| 141 | + | |
| 142 | + | def _verbsHeadersIPFuzzing(self, ip: str = '127.0.0.1') -> None: |
| 143 | + | log.info('Start Verbs and Headers IP Fuzzing') |
| 144 | + | for verb in self._verbs: |
| 145 | + | for header in self._headers: |
| 146 | + | self._endpoint.makeRequest( |
| 147 | + | method=verb, headers={header: ip}) |
| 148 | + | log.info('End Verbs and Headers IP Fuzzing') |
| 149 | + | |
| 150 | + | def _getWaybackMachine(self) -> None: |
| 151 | + | log.info('Start Wayback Machine Testing') |
| 152 | + | try: |
| 153 | + | wayback = WaybackMachineAvailabilityAPI( |
| 154 | + | url=self._endpoint.getDomain()) |
| 155 | + | log.info( |
| 156 | + | f'[+] Found something on Wayback Machine, oldest archive {wayback.newest()}') |
| 157 | + | except exceptions.ArchiveNotInAvailabilityAPIResponse: |
| 158 | + | log.warning('[-] Nothing on Wayback Machine!') |
| 159 | + | except: |
| 160 | + | log.error('[-] General Connection Error for Wayback Machine!') |
| 161 | + | log.info('End Wayback Machine Testing') |
| 162 | + | |
| 163 | + | def _localIPFuzzing(self) -> None: |
| 164 | + | log.info('Start Local IP Fuzzing') |
| 165 | + | for commonLAN in self._commonLANs: |
| 166 | + | threads = [] |
| 167 | + | network = ipaddress.ip_network(commonLAN) |
| 168 | + | hosts = list(network.hosts()) |
| 169 | + | for host in hosts: |
| 170 | + | t = threading.Thread(target=self._headersIPFuzzing, |
| 171 | + | args=(str(host),)) |
| 172 | + | threads.append(t) |
| 173 | + | t.start() |
| 174 | + | for thread in threads: |
| 175 | + | thread.join() |
| 176 | + | log.info('End Local IP Fuzzing') |
| 177 | + | |
| 178 | + | def _pathUnicodeFuzzing(self) -> None: |
| 179 | + | log.info('Start Path Unicode Fuzzing') |
| 180 | + | unicodes = [] |
| 181 | + | with open(f'{os.path.dirname(__file__)}/unicode.txt', 'r') as file: |
| 182 | + | unicodes = file.readlines() |
| 183 | + | threads = [] |
| 184 | + | for unicode in unicodes: |
| 185 | + | url = f'{self._endpoint.getMainUrl()}/{unicode}/{self._endpoint.getPath()}' |
| 186 | + | t = threading.Thread(target=self._endpoint.makeRequest, |
| 187 | + | args=(url,)) |
| 188 | + | threads.append(t) |
| 189 | + | t.start() |
| 190 | + | for thread in threads: |
| 191 | + | thread.join() |
| 192 | + | log.info('End Path Unicode Fuzzing') |
| 193 | + | |
| 194 | + | def _getStressTest(self) -> None: |
| 195 | + | log.info('Start Get Stress Test') |
| 196 | + | threads = [] |
| 197 | + | for index in range(300): |
| 198 | + | t = threading.Thread(target=self._endpoint.makeRequest, |
| 199 | + | args=(index,)) |
| 200 | + | threads.append(t) |
| 201 | + | t.start() |
| 202 | + | for thread in threads: |
| 203 | + | thread.join() |
| 204 | + | log.info('End Get Stress Test') |
| 205 | + | |
| 206 | + | def bypass(self) -> None: |
| 207 | + | self._testHTTPOrHTTPS() |
| 208 | + | self._protocolFuzzing() |
| 209 | + | self._testIPAndCNAME() |
| 210 | + | self._headersFuzzingPath() |
| 211 | + | self._hostHeaderFuzzing() |
| 212 | + | self._verbFuzzing() |
| 213 | + | self._userAgentFuzzing() |
| 214 | + | self._headersIPFuzzing() |
| 215 | + | self._pathFuzzing() |
| 216 | + | self._verbsHeadersIPFuzzing() |
| 217 | + | self._getWaybackMachine() |
| 218 | + | self._getStressTest() |
| 219 | + | self._pathUnicodeFuzzing() |
| 220 | + | self._localIPFuzzing() |
| 221 | + | |
| 222 | + | def __str__(self) -> str: |
| 223 | + | return f'Bypass endpoint:{self._endpoint}' |
| 224 | + | |