From 3731f18aacffadc1f9243284d1952296418589ca Mon Sep 17 00:00:00 2001 From: Alexander Ebhart Date: Fri, 22 Aug 2025 09:30:52 +0200 Subject: [PATCH 1/2] feat: discover URLs from spec & limit scans to URL prefix --- .gitignore | 2 ++ autoswagger.py | 37 ++++++++++++++++++++++++++++++------- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 26b4015..dcc9031 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ logs/* testing*/ venv/* venv +.venv/* +.venv env/* env *.log diff --git a/autoswagger.py b/autoswagger.py index eb51359..279a363 100644 --- a/autoswagger.py +++ b/autoswagger.py @@ -779,7 +779,7 @@ def test_endpoint(base_url, base_path, path_template, method, parameters, reques def test_endpoints(base_url, base_path, swagger_spec, verbose=False, include_risk=False, include_all=False, product_mode=False, - rate=30, tried_basepath_fallback=False, brute=False): + rate=30, tried_basepath_fallback=False, brute=False, limit="", discover=False): """ Iterates over all paths and methods in the provided swagger_spec. Submits tasks to test_endpoint if the method is allowed (GET or others if -risk). @@ -795,6 +795,22 @@ def test_endpoints(base_url, base_path, swagger_spec, verbose=False, all_results = [] max_workers = min(100, os.cpu_count() * 5) + # Base path contains a domain, use it as well! + if (base_path.startswith("http://") or base_path.startswith("https://")): + parsed_base_path = urlparse(base_path) + if discover: + rslts = test_endpoints(base_path, parsed_base_path.path, swagger_spec, verbose=verbose, include_risk=include_risk, include_all=include_all, product_mode=product_mode, rate=rate, tried_basepath_fallback=tried_basepath_fallback, brute=brute, limit=limit, discover=False) + all_results.extend(rslts) + + base_path = parsed_base_path.path + + if limit and not base_url.startswith(limit): + if verbose: + parsed_base_url = urlparse(base_url) + base_url_no_path = f"{parsed_base_url.scheme}://{parsed_base_url.netloc}" + log(f"Skipping base URL '{base_url_no_path}' as it does not match the limit prefix.", level="INFO") + return results + with ThreadPoolExecutor(max_workers=max_workers) as executor: future_to_endpoint = {} for path, methods in swagger_spec['paths'].items(): @@ -873,7 +889,7 @@ def test_endpoints(base_url, base_path, swagger_spec, verbose=False, fallback = test_endpoints( base_url, '/', swagger_spec, verbose, include_risk, include_all, product_mode=product_mode, - rate=rate, tried_basepath_fallback=True, brute=brute + rate=rate, tried_basepath_fallback=True, brute=brute, limit=limit, discover=discover ) return fallback @@ -1125,7 +1141,7 @@ def process_input(urls): processed.append(url) return processed -def main(urls, verbose, include_risk, include_all, product_mode, stats_flag, rate, brute, json_output): +def main(urls, verbose, include_risk, include_all, product_mode, stats_flag, rate, brute, json_output, limit, discover): """ Main function controlling flow: 1. Tracks start time @@ -1185,7 +1201,8 @@ def process_url(base_url): rslts = test_endpoints( base_url, base_path, swagger_spec, verbose, include_risk, include_all, - product_mode=product_mode, rate=rate, brute=brute + product_mode=product_mode, rate=rate, brute=brute, + limit=limit, discover=discover ) del swagger_spec with results_lock: @@ -1222,7 +1239,8 @@ def process_url(base_url): rslts = test_endpoints( base_url, base_path, swagger_spec, verbose, include_risk, include_all, - product_mode=product_mode, rate=rate, brute=brute + product_mode=product_mode, rate=rate, brute=brute, + limit=limit, discover=discover ) del swagger_spec with results_lock: @@ -1259,7 +1277,8 @@ def process_url(base_url): rslts2 = test_endpoints( base_url, base_path, sws, verbose, include_risk, include_all, - product_mode=product_mode, rate=rate, brute=brute + product_mode=product_mode, rate=rate, brute=brute, + limit=limit, disocver=discover ) del sws with results_lock: @@ -1464,6 +1483,8 @@ def process_url(base_url): parser.add_argument("-rate", type=int, default=30, help="Set the rate limit in requests per second (default: 30). Use 0 to disable rate limiting.") parser.add_argument("-b", "--brute", action="store_true", help="Enable exhaustive testing of parameter values.") parser.add_argument("-json", action="store_true", help="Output results in JSON format in default mode.") + parser.add_argument("-limit", type=str, default="", help="Limit scan to url prefix.") + parser.add_argument("-d", "--discover", action="store_true", help="Discover additional domains from the spec.") args = parser.parse_args() @@ -1485,6 +1506,8 @@ def process_url(base_url): rate = args.rate brute = args.brute json_output = args.json + limit = args.limit + discover = args.discover # Set up file logging if verbose is enabled if verbose: @@ -1498,4 +1521,4 @@ def process_url(base_url): logger.addHandler(file_handler) logger.propagate = False - main(urls, verbose, include_risk, include_all, product_mode, stats_flag, rate, brute, json_output) + main(urls, verbose, include_risk, include_all, product_mode, stats_flag, rate, brute, json_output, limit, discover) From c2c8227c29df6ccb30929ba227d4f7d3307022a8 Mon Sep 17 00:00:00 2001 From: Alexander Ebhart Date: Fri, 22 Aug 2025 09:50:06 +0200 Subject: [PATCH 2/2] docs: add discover and limit flags to README --- README.md | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 97a79df..825f314 100644 --- a/README.md +++ b/README.md @@ -12,15 +12,20 @@ --- ## Table of Contents -1. [Introduction](#introduction) -2. [Key Features](#key-features) -3. [Installation & Usage](#installation--usage) -4. [Discovery Phases](#discovery-phases) -5. [Endpoint Testing](#endpoint-testing) -6. [PII Detection](#pii-detection) -7. [Output Examples](#output) -8. [Stats & Reporting](#stats--reporting) -9. [Acknowledgments](#acknowledgments) +- [Autoswagger by Intruder](#autoswagger-by-intruder) + - [Table of Contents](#table-of-contents) + - [Introduction](#introduction) + - [Key Features](#key-features) + - [Installation \& Usage](#installation--usage) + - [Flags](#flags) + - [Help](#help) + - [Discovery Phases](#discovery-phases) + - [Endpoint Testing](#endpoint-testing) + - [PII Detection](#pii-detection) + - [Output](#output) + - [Interpreting Results](#interpreting-results) + - [Stats \& Reporting](#stats--reporting) + - [Acknowledgments](#acknowledgments) --- @@ -80,17 +85,19 @@ Autoswagger automates the process of finding **OpenAPI/Swagger** specifications, ## Flags -| Flag | Description | -|----------------------|-------------------------------------------------------------------------------------------------------------| -| `urls` | List of base URLs or direct spec URLs. | -| `-v, --verbose` | Enables verbose logging. Creates a log file under `~/.autoswagger/logs`. | -| `-risk` | Includes non-GET methods (POST, PUT, PATCH, DELETE) in testing. | -| `-all` | Includes 200 and 404 endpoints in output (excludes 401/403). | -| `-product` | Outputs only endpoints with PII or large responses, in JSON format. | -| `-stats` | Displays scan statistics (e.g. requests, RPS, hosts with PII). | -| `-rate ` | Throttles requests to N requests per second. Default is 30. Use 0 to disable rate limiting. | -| `-b, --brute` | Enables brute-forcing of parameter values (multiple test combos). | -| `-json` | Outputs results in JSON format instead of a Rich table in default mode. | +| Flag | Description | +| ---------------- | ------------------------------------------------------------------------------------------- | +| `urls` | List of base URLs or direct spec URLs. | +| `-v, --verbose` | Enables verbose logging. Creates a log file under `~/.autoswagger/logs`. | +| `-risk` | Includes non-GET methods (POST, PUT, PATCH, DELETE) in testing. | +| `-all` | Includes 200 and 404 endpoints in output (excludes 401/403). | +| `-product` | Outputs only endpoints with PII or large responses, in JSON format. | +| `-stats` | Displays scan statistics (e.g. requests, RPS, hosts with PII). | +| `-rate ` | Throttles requests to N requests per second. Default is 30. Use 0 to disable rate limiting. | +| `-b, --brute` | Enables brute-forcing of parameter values (multiple test combos). | +| `-json` | Outputs results in JSON format instead of a Rich table in default mode. | +| `-d, --discover` | Add URLs from the specs `server` object to the scan. | +| `-l, --limit` | Limit the scan to the given URL prefix. | ## Help