diff --git a/README.md b/README.md index 2f35ed7..600e441 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,8 @@ The results are then contained inside a queue object and used for further scans. Iterates over a wordlist and probes (in a brute manner) different endpoints by appending the words to the target hostname.
A result is considered successful if the request status code is one of the following: `200`, `301`, `302`. If a forbidden status code is returned (`403`) and `403bypass` scan is enabled, further probing takes place where different kind of methods are attempted in order to bypass the forbidden status. Those attempts are also considered as success only if they manage to retrieve one of the aformentioned successful status code.
- +
+The output in the progress log (and the results file) contains the status code and the page content size. * In order to use a custom wordlist, "--set-contentscan-wl" argument should be passed, followed by the path * The default wordlist used here is dirbuster's `directory-list-2.3-medium.txt` list, which is also located under `/usr/share/wordlists/dirbuster` @@ -48,7 +49,7 @@ A result is considered successful if the request status code is one of the follo Probes a url using different methods in order to bypass a `403` forbidden status code.
This scan is a subscan and should only be invoked by `Content Scan`. * If listing a custom scan list rather than using the `-sA` option, this scan should be listed as well, otherwise it would be disabled -* Most of the methods in this scanner were converted from another known GitHub repo, credit goes to https://github.com/iamj0ker/bypass-403 +* Most of the methods in this scanner were converted from 2 other known GitHub repos, credit goes to https://github.com/iamj0ker/bypass-403 and https://github.com/yunemse48/403bypasser ### NMAP Scan (`nmap`) diff --git a/scanners/base_scanner.py b/scanners/base_scanner.py index 83d1590..3480899 100644 --- a/scanners/base_scanner.py +++ b/scanners/base_scanner.py @@ -303,14 +303,14 @@ def _setup_session(self): self._session = requests.Session() - def _make_request(self, method: str, url: str, headers=None, **kwargs): + def _make_request(self, method: str, url: str, headers=None, timeout=None, **kwargs): if not self._session_refresh_count % self._session_refresh_interval: self._setup_session() if not headers: headers = dict() headers.update(self._default_headers) - res = self._session.request(method=method, url=url, headers=headers, timeout=self.request_timeout, + res = self._session.request(method=method, url=url, headers=headers, timeout=timeout or self.request_timeout, verify=False, **kwargs) if res.status_code == ScannerDefaultParams.LimitRateSCode: diff --git a/scanners/bypass_403.py b/scanners/bypass_403.py index a3a04bf..9a2aef8 100644 --- a/scanners/bypass_403.py +++ b/scanners/bypass_403.py @@ -6,7 +6,7 @@ from .base_scanner import Scanner from .utils import * -from typing import Any, Dict, List +from typing import Any, Dict, List, Tuple # -------------------------------------------------------------------------------------------------------------------- @@ -16,6 +16,7 @@ # Ⓒ Ⓒ # Ⓒ This module was written in Python in order to integrate better with WebRecon, most methods were taken from Ⓒ # Ⓒ from this repo -> https://github.com/iamj0ker/bypass-403 Ⓒ +# Ⓒ and this repo -> https://github.com/yunemse48/403bypasser Ⓒ # Ⓒ Ⓒ # # -------------------------------------------------------------------------------------------------------------------- @@ -28,6 +29,17 @@ class Bypass403(Scanner): _SUPPORTS_CACHE = False _FOUND = 0 + _HOST_HEADERS = ["X-Custom-IP-Authorization", "X-Forwarded-For", + "X-Forward-For", "X-Remote-IP", "X-Originating-IP", + "X-Remote-Addr", "X-Client-IP", "X-Real-IP", + "X-Host"] + + _LHOST_NICKNAMES = ["localhost", "localhost:80", "localhost:443", + "127.0.0.1", "127.0.0.1:80", "127.0.0.1:443", + "2130706433", "0x7F000001", "0177.0000.0000.0001", + "0", "127.1", "10.0.0.0", "10.0.0.1", "172.16.0.0", + "172.16.0.1", "192.168.1.0", "192.168.1.1"] + def __init__(self, target_keyword, *args, **kwargs): self.target_keyword = target_keyword.strip("/") @@ -36,120 +48,75 @@ def __init__(self, target_keyword, *args, **kwargs): def try_bypass(self) -> dict: results = collections.defaultdict(list) + original_path = f"{self.target_url}/{self.target_keyword}" + self._log_progress(f"in progress -> {self.target_keyword}") # methods - req_path = f"{self.target_url}/{self.target_keyword}" - results[self.send_request("GET", req_path)].append(f"GET {req_path}") - - req_path = f"{self.target_url}/{self.target_keyword}" - headers = {"Content-Length": "0"} - results[self.send_request("POST", req_path, headers=headers)].append(f"POST {req_path} -H 'Content-Length: 0'") - - req_path = f"{self.target_url}/{self.target_keyword}" - headers = {"Content-Length": "0"} - results[self.send_request("PUT", req_path, headers=headers)].append(f"PUT {req_path} -H 'Content-Length: 0'") - - req_path = f"{self.target_url}/{self.target_keyword}" - results[self.send_request("TRACE", req_path)].append(f"TRACE {req_path}") - - req_path = f"{self.target_url}/{self.target_keyword}" - results[self.send_request("DELETE", req_path)].append(f"DELETE {req_path}") + for method in ["GET", "POST", "PUT", "TRACE", "DELETE"]: + scode, size = self.send_request(method, original_path) + results[scode].append(f"size {size}\t\t{method} {original_path}") # encoding / path traversal - req_path = f"{self.target_url}/%2e/{self.target_keyword}" - results[self.send_request("GET", req_path)].append(f"GET {req_path}") - - req_path = f"{self.target_url}/{self.target_keyword}/." - results[self.send_request("GET", req_path)].append(f"GET {req_path}") - - req_path = f"{self.target_url}//{self.target_keyword}//" - results[self.send_request("GET", req_path)].append(f"GET {req_path}") - - req_path = f"{self.target_url}/./{self.target_keyword}/./" - results[self.send_request("GET", req_path)].append(f"GET {req_path}") - - req_path = f"{self.target_url}/{self.target_keyword}..;/" - results[self.send_request("GET", req_path)].append(f"GET {req_path}") - - req_path = f"{self.target_url}/{self.target_keyword};/" - results[self.send_request("GET", req_path)].append(f"GET {req_path}") - - req_path = f"{self.target_url}/{self.target_keyword}%20" - results[self.send_request("GET", req_path)].append(f"GET {req_path}") - - req_path = f"{self.target_url}/{self.target_keyword}%09" - results[self.send_request("GET", req_path)].append(f"GET {req_path}") - - req_path = f"{self.target_url}/{self.target_keyword}?" - results[self.send_request("GET", req_path)].append(f"GET {req_path}") - - req_path = f"{self.target_url}/{self.target_keyword}#" - results[self.send_request("GET", req_path)].append(f"GET {req_path}") - - req_path = f"{self.target_url}/{self.target_keyword}/*" - results[self.send_request("GET", req_path)].append(f"GET {req_path}") + for req_path in [f"{self.target_url}/%2e/{self.target_keyword}", f"{self.target_url}/{self.target_keyword}/.", + f"{self.target_url}//{self.target_keyword}//", f"{self.target_url}/./{self.target_keyword}/./", + f"{self.target_url}/{self.target_keyword}..;/", f"{self.target_url}/{self.target_keyword};/", + f"{self.target_url}/{self.target_keyword}%20", f"{self.target_url}/{self.target_keyword}%09", + f"{self.target_url}/{self.target_keyword}?", f"{self.target_url}/{self.target_keyword}#", + f"{self.target_url}/{self.target_keyword}/*"]: + scode, size = self.send_request("GET", req_path) + results[scode].append(f"size {size}\t\tGET {req_path}") # file extensions - req_path = f"{self.target_url}/{self.target_keyword}.html" - results[self.send_request("GET", req_path)].append(f"GET {req_path}") - - req_path = f"{self.target_url}/{self.target_keyword}.php" - results[self.send_request("GET", req_path)].append(f"GET {req_path}") - - req_path = f"{self.target_url}/{self.target_keyword}.json" - results[self.send_request("GET", req_path)].append(f"GET {req_path}") + for file_ext in ["html", "php", "json"]: + req_path = f"{original_path}.{file_ext}" + scode, size = self.send_request("GET", req_path) + results[scode].append(f"size {size}\t\tGET {req_path}\t\tsize {size}") # headers - req_path = f"{self.target_url}/{self.target_keyword}" - headers = {"X-Original-URL": self.target_keyword} - results[self.send_request("GET", req_path, - headers=headers)].append(f"GET {req_path} -H 'X-Original-URL: {self.target_keyword}'") - - req_path = f"{self.target_url}/{self.target_keyword}" - headers = {"X-Custom-IP-Authorization": "127.0.0.1"} - results[self.send_request("GET", req_path, - headers=headers)].append(f"GET {req_path} -H 'X-Custom-IP-Authorization: 127.0.0.1'") - - req_path = f"{self.target_url}/{self.target_keyword}" - headers = {"X-Forwarded-For": "http://127.0.0.1"} - results[self.send_request("GET", req_path, - headers=headers)].append(f"GET {req_path} -H 'X-Forwarded-For: http://127.0.0.1'") - - req_path = f"{self.target_url}/{self.target_keyword}" - headers = {"X-Forwarded-For": "127.0.0.1:80"} - results[self.send_request("GET", req_path, - headers=headers)].append(f"GET {req_path} -H 'X-Forwarded-For: 127.0.0.1:80'") + for header in Bypass403._HOST_HEADERS: + for host_nickname in Bypass403._LHOST_NICKNAMES: + headers = {header: host_nickname} + scode, size = self.send_request("GET", original_path, headers=headers) + results[scode].append(f"size {size}\t\tGET {original_path} -H {header}: {host_nickname}") req_path = f"{self.target_url}" headers = {"X-rewrite-url": self.target_keyword} - results[self.send_request("GET", req_path, - headers=headers)].append(f"GET {req_path} -H 'X-rewrite-url: {self.target_keyword}'") + scode, size = self.send_request("GET", req_path, headers=headers) + results[scode].append(f"size {size}\t\tGET {req_path} -H 'X-rewrite-url: {self.target_keyword}'") + + req_path = f"{self.target_url}" + headers = {"X-Original-URL": self.target_keyword} + scode, size = self.send_request("GET", req_path, headers=headers) + results[scode].append(f"size {size}\t\tGET {req_path} -H 'X-Original-URL: {self.target_keyword}'") - req_path = f"{self.target_url}/{self.target_keyword}" - headers = {"X-Host": "127.0.0.1"} - results[self.send_request("GET", req_path, headers=headers)].append(f"GET {req_path} -H 'X-Host: 127.0.0.1'") + headers = {"Content-Length": "0"} + scode, size = self.send_request("POST", original_path, headers=headers) + results[scode].append(f"size {size}\t\tPOST {original_path} -H 'Content-Length: 0'") + + headers = {"Content-Length": "0"} + scode, size = self.send_request("PUT", original_path, headers=headers) + results[scode].append(f"size {size}\t\tPUT {original_path} -H 'Content-Length: 0'") return results - def send_request(self, method, path, headers=None) -> int: - response = 0 - time.sleep(self.request_cooldown) + def send_request(self, method, path, headers=None) -> Tuple[int, int]: # returns status_code, size + time.sleep(0.25 * self.request_cooldown) try: response = self._make_request(method=method, url=path, headers=headers, - allow_redirects=True).status_code + allow_redirects=True, timeout=0.5 * self.request_timeout) except (requests.exceptions.ConnectionError, requests.exceptions.ConnectTimeout, requests.exceptions.ReadTimeout, HTTPError): - pass + return 0, 0 except requests.exceptions.TooManyRedirects: self._log_exception(requests.exceptions.TooManyRedirects.__name__, abort=False) - return ScannerDefaultParams.TooManyRedirectsSCode - except Exception as exc: # error -> return 0 - pass - return response + return ScannerDefaultParams.TooManyRedirectsSCode, 0 + except Exception as exc: + return 0, 0 + return response.status_code, len(response.text) def _start_scanner(self, results_filename=None) -> Dict[int, List[str]]: success_results = dict() diff --git a/scanners/content_scanner.py b/scanners/content_scanner.py index 36edb53..0d4cb85 100644 --- a/scanners/content_scanner.py +++ b/scanners/content_scanner.py @@ -75,15 +75,16 @@ def single_bruter(self): if scode in ScannerDefaultParams.SuccessStatusCodes or \ scode == ScannerDefaultParams.ForbiddenSCode: - self.ret_results[scode].append(url) - self._log_progress(f"{path} -> [{scode}]") + res_size = len(response.text) + self.ret_results[scode].append(f"size {res_size}\t\t{url}") + self._log_progress(f"{path} -> [{scode}, {res_size}]") found_any = True except (requests.exceptions.ConnectionError, requests.exceptions.ConnectTimeout, requests.exceptions.ReadTimeout, HTTPError): continue except requests.exceptions.TooManyRedirects: - self.ret_results[ScannerDefaultParams.TooManyRedirectsSCode].append(url) + self.ret_results[ScannerDefaultParams.TooManyRedirectsSCode].append(f"size 0\t\t{url}") self._log_progress(f"{path} -> [{ScannerDefaultParams.TooManyRedirectsSCode}]") found_any = True except Exception as exc: diff --git a/scanners/utils/default_values.py b/scanners/utils/default_values.py index 22166f8..0d769b1 100644 --- a/scanners/utils/default_values.py +++ b/scanners/utils/default_values.py @@ -104,7 +104,7 @@ class ScannerDefaultParams(_ExtendedEnum): LimitRateSCode = 429 TooManyRedirectsSCode = 301 SuccessStatusCodes = [200, 301, 302] - ThreadCount = 8 + ThreadCount = 16 class WordlistDefaultPath(_ExtendedEnum):