Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ setuptools>=75.6.0
SQLAlchemy>=2.0.32
tldextract>=5.1.2
weasyprint>=65.0
playwright
6 changes: 5 additions & 1 deletion subdominator/modules/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ def cli():
parser.add_argument("-V", "--verbose", action="store_true")
parser.add_argument("-sup", "--show-updates", action="store_true")
parser.add_argument("-fw", "--filter-wildcards", action="store_true")

# New Feature: Sourcemap Leakage Flag
parser.add_argument("-sm", "--sourcemap", action="store_true")

parser.add_argument("-json", "--json", action="store_true")
parser.add_argument("-s", "--silent", action="store_true")
parser.add_argument("-ir", "--include-resources", type=str,default=None)
Expand All @@ -42,4 +46,4 @@ def cli():
Exit(1)
except Exception as e:
logger(f"Unahandled Exception occured in the CLI module due to: {e}", "warn")
Exit(1)
Exit(1)
25 changes: 24 additions & 1 deletion subdominator/modules/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
from .version.version import version
from .logger.logger import logger
from .utils.utils import filters,reader,split_to_list, check_directory_permission,check_file_permission, Exit
# Import the new scanner module
from .scanner.sourcemap import check_sourcemap_leakage
from .subscraper.abuseipdb.abuseipdb import abuseipdb
from .subscraper.alienvault.alientvault import alienvault
from .subscraper.anubis.anubis import anubis
Expand Down Expand Up @@ -270,7 +272,28 @@ async def _domain_handler_(domain):
file(output, domain, args)
elif args.output_directory:
dir(output, domain, args)


# START NEW FEATURE: Sourcemap Leakage Check
if args.sourcemap:
if not args.silent:
logger(f"Checking for Sourcemap Leakage on {len(final)} subdomains...", "info", args.no_color)

# Use Semaphore to limit concurrent web requests during scanning
sem = asyncio.Semaphore(10)

async def limited_check(sub):
async with sem:
return await check_sourcemap_leakage(sub, timeout=args.timeout)

scan_tasks = [limited_check(sub) for sub in final]
scan_results = await asyncio.gather(*scan_tasks)

for res in scan_results:
if res and res.get("vulnerable"):
msg = f"VULNERABLE: Sourcemap Leakage at {res['subdomain']} ({res['count']} maps found)"
logger(msg, "info", args.no_color) # You can customize this logger level
# END NEW FEATURE

async with AsyncSessionLocal() as db:
await add_or_update_domain(db, domain, final)

Expand Down
6 changes: 4 additions & 2 deletions subdominator/modules/help/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ def help(path, dbpath):
{bold}{white}[{reset}{bold}{blue}OPTIMIZATION{reset}{bold}{white}]{reset}:

{bold}{white}-t, --timeout : Set timeout value for API requests (default: 30s).
-fw, --filter-wildcards : Filter out wildcard subdomains.{reset}
-fw, --filter-wildcards : Filter out wildcard subdomains.
-sm, --sourcemap : Check for sourcemap leakage on discovered subdomains.{reset}


{bold}{white}[{reset}{bold}{blue}CONFIGURATION{reset}{bold}{white}]{reset}:

Expand Down Expand Up @@ -64,4 +66,4 @@ def help(path, dbpath):
-ls, --list-source : List available subdomain enumeration sources.
-V, --verbose : Enable verbose output.{reset}
""")
Exit()
Exit()
1 change: 1 addition & 0 deletions subdominator/modules/scanner/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

57 changes: 57 additions & 0 deletions subdominator/modules/scanner/sourcemap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import asyncio
import warnings
from urllib.parse import urljoin
from bs4 import XMLParsedAsHTMLWarning
from playwright.async_api import async_playwright
from subdominator.modules.logger.logger import logger

warnings.filterwarnings("ignore", category=XMLParsedAsHTMLWarning)

async def check_sourcemap_leakage(subdomain, timeout=15):
"""
Uses Playwright to load a site and listen for all JS requests.
Checks for .map files for every JS resource discovered.
"""
found_maps = []
url = f"http://{subdomain}"

try:
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
context = await browser.new_context(ignore_https_errors=True)
page = await context.new_page()

js_urls = set()

page.on("request", lambda request: js_urls.add(request.url)
if request.resource_type == "script" else None)

try:
await page.goto(url, timeout=timeout * 1000, wait_until="networkidle")
except Exception:
await page.goto(f"https://{subdomain}", timeout=timeout * 1000, wait_until="networkidle")

await browser.close()

import httpx
async with httpx.AsyncClient(verify=False, timeout=5.0) as client:
for js_url in js_urls:
map_url = f"{js_url}.map"
try:
res = await client.get(map_url)
if res.status_code == 200 and "application/json" in res.headers.get("Content-Type", ""):
found_maps.append(map_url)
except Exception:
continue

if len(found_maps) > 3:
return {
"subdomain": subdomain,
"vulnerable": True,
"count": len(found_maps),
"files": found_maps
}
except Exception:
pass

return None