From b571888e326607502990392289358145d405d060 Mon Sep 17 00:00:00 2001 From: aarushikool Date: Fri, 3 Apr 2026 14:08:34 +0530 Subject: [PATCH] Adding InSp3ctor tool for public buckets. --- configs/local.yml | 2 +- mantis/constants.py | 1 + mantis/custom_tools/inSp3ctor | 1 + mantis/db/db_models.py | 3 +- mantis/modules/prerecon/InSp3ctor.py | 90 ++++++++++++++++++++++++++++ requirements.txt | 2 + 6 files changed, 97 insertions(+), 2 deletions(-) create mode 160000 mantis/custom_tools/inSp3ctor create mode 100644 mantis/modules/prerecon/InSp3ctor.py diff --git a/configs/local.yml b/configs/local.yml index 83b85dc2..9b2f825e 100644 --- a/configs/local.yml +++ b/configs/local.yml @@ -70,7 +70,7 @@ workflow: tools: ['Subfinder'] order: 1 - moduleName: prerecon - tools: ['FindCDN', 'Naabu'] + tools: ['FindCDN', 'Naabu', 'InSp3ctor'] order: 2 - moduleName: activehostscan tools: ['HTTPX_Active', 'HTTPX'] diff --git a/mantis/constants.py b/mantis/constants.py index cff746b2..ff766de3 100644 --- a/mantis/constants.py +++ b/mantis/constants.py @@ -8,6 +8,7 @@ SCAN_MODULE = "scan" ASSET_TYPE_TLD = 'TLD' ASSET_TYPE_THIRD_PARTY = "third_party_integration" +ASSET_TYPE_CLOUD_BUCKET = "cloud_bucket" GET_METHOD = "GET" POST_METHOD = "POST" diff --git a/mantis/custom_tools/inSp3ctor b/mantis/custom_tools/inSp3ctor new file mode 160000 index 00000000..71688ed6 --- /dev/null +++ b/mantis/custom_tools/inSp3ctor @@ -0,0 +1 @@ +Subproject commit 71688ed6f95a72d6f66218754cb2f2f3aa2596ab diff --git a/mantis/db/db_models.py b/mantis/db/db_models.py index 08be1c26..f8c315b1 100644 --- a/mantis/db/db_models.py +++ b/mantis/db/db_models.py @@ -66,4 +66,5 @@ class Extended(BaseModel): availability_status: Optional[Literal['Exists', 'Not Exists']] = Field(None) created_timestamp: str = Field(None) updated_timestamp: Optional[str] = Field(None) - others: Optional[dict] = dict() \ No newline at end of file + tools_source: Optional[str] = Field(None) + others: Optional[dict] = dict() diff --git a/mantis/modules/prerecon/InSp3ctor.py b/mantis/modules/prerecon/InSp3ctor.py new file mode 100644 index 00000000..3f937e1c --- /dev/null +++ b/mantis/modules/prerecon/InSp3ctor.py @@ -0,0 +1,90 @@ +import re +import hashlib +import logging +from mantis.constants import ASSET_TYPE_CLOUD_BUCKET +from mantis.utils.crud_utils import CrudUtils +from mantis.tool_base_classes.toolScanner import ToolScanner +from mantis.models.args_model import ArgsModel + +# Regex to strip ANSI escape codes from colorama output +ANSI_ESCAPE = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') + +# True positive patterns from inSp3ctor tool's check_response(): +# Status 200: [*] Bucket/Object is public [http://...] +PUBLIC_BUCKET = re.compile(r'\[\*\]\s+\w+ is public \[(https?://[^\]]+)\]') +# Status 301: [>] Bucket/Object has a redirect [http://...] Redirected here - [...] +REDIRECT_BUCKET = re.compile(r'\[>\]\s+\w+ has a redirect \[(https?://[^\]]+)\]') + +# Cloud provider patterns to identify bucket type from URL +CLOUD_PROVIDER_PATTERNS = { + 'amazonS3': re.compile(r'amazonaws\.com'), + 'azure': re.compile(r'blob\.core\.windows\.net'), + 'digitalocean': re.compile(r'digitaloceanspaces\.com'), + 'googlecloud': re.compile(r'storage\.googleapis\.com'), +} + +''' +InSp3ctor is a targeted reconnaissance tool built for the discovery and enumeration of Amazon S3 storage buckets. +Output: CSV File +''' + + +def extract_bucket_url(line): + + match = ( + PUBLIC_BUCKET.search(line) or + REDIRECT_BUCKET.search(line) + ) + if match: + return match.group(1) + return None + + +def identify_cloud_provider(url): + """ + Identifies the cloud provider from the bucket URL. + """ + for provider, pattern in CLOUD_PROVIDER_PATTERNS.items(): + if pattern.search(url): + return provider + return 'unknown' + + +class InSp3ctor(ToolScanner): + + def __init__(self) -> None: + super().__init__() + + async def get_commands(self, args: ArgsModel): + self.org = args.org + self.base_command = f"python mantis/custom_tools/inSp3ctor/inSp3ctor.py -n {self.org} -c > {{output_file_path}}" + self.outfile_extension = ".txt" + self.assets = [self.org] + return super().base_get_commands(self.assets) + + def parse_report(self, outfile): + logging.debug('Parsing InSp3ctor Report for cloud recon - Begins') + output_dict_list = [] + InSp3ctor_output = open(outfile).readlines() + for line in InSp3ctor_output: + clean_line = ANSI_ESCAPE.sub('', line).strip() + + bucket_url = extract_bucket_url(clean_line) + if not bucket_url: + continue + + unique_hash = hashlib.md5( + str(bucket_url).encode() + str(ASSET_TYPE_CLOUD_BUCKET).encode() + str(self.org).encode() + ).hexdigest() + domain_dict = {} + domain_dict['_id'] = unique_hash + domain_dict['asset'] = identify_cloud_provider(bucket_url) + domain_dict['url'] = bucket_url + domain_dict['asset_type'] = ASSET_TYPE_CLOUD_BUCKET + domain_dict['org'] = self.org + domain_dict['tools_source'] = 'InSp3ctor' + output_dict_list.append(domain_dict) + return output_dict_list + + async def db_operations(self, tool_output_dict, asset=None): + await CrudUtils.insert_extended_assets(tool_output_dict) diff --git a/requirements.txt b/requirements.txt index 13c1f53f..e3913086 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,3 +17,5 @@ tqdm==4.66.5 lxml==5.3.0 cloudflare GitPython==3.1.43 +beautifulsoup4==4.14.3 +colorama==0.4.6 \ No newline at end of file