142 lines
4.8 KiB
Python
142 lines
4.8 KiB
Python
"""
|
||
This plugin provides subdomain enumeration functionality using the CertSpotter API.
|
||
It queries SSL certificates issued for domains to discover associated subdomains.
|
||
"""
|
||
|
||
import logging
|
||
import aiohttp
|
||
import simplematrixbotlib as botlib
|
||
import json
|
||
import asyncio
|
||
|
||
async def query_certspotter(domain):
|
||
"""
|
||
Query CertSpotter API for subdomain enumeration.
|
||
|
||
Args:
|
||
domain (str): The domain to enumerate subdomains for
|
||
|
||
Returns:
|
||
list: List of discovered subdomains
|
||
"""
|
||
url = f"https://api.certspotter.com/v1/issuances?domain={domain}&include_subdomains=true&expand=dns_names"
|
||
subdomains = set()
|
||
|
||
try:
|
||
async with aiohttp.ClientSession() as session:
|
||
async with session.get(url) as response:
|
||
if response.status == 200:
|
||
data = await response.json()
|
||
for cert in data:
|
||
if 'dns_names' in cert:
|
||
for dns_name in cert['dns_names']:
|
||
if domain in dns_name:
|
||
subdomains.add(dns_name)
|
||
else:
|
||
logging.error(f"CertSpotter API returned status {response.status}")
|
||
return []
|
||
except Exception as e:
|
||
logging.error(f"Error querying CertSpotter API: {e}")
|
||
return []
|
||
|
||
return list(subdomains)
|
||
|
||
async def format_subdomain_results(domain, subdomains):
|
||
"""
|
||
Format subdomain results into a readable message.
|
||
|
||
Args:
|
||
domain (str): The queried domain
|
||
subdomains (list): List of discovered subdomains
|
||
|
||
Returns:
|
||
str: Formatted message
|
||
"""
|
||
if not subdomains:
|
||
return f"🔍 No subdomains found for {domain} using CertSpotter API."
|
||
|
||
# Sort and remove duplicates
|
||
subdomains = sorted(list(set(subdomains)))
|
||
count = len(subdomains)
|
||
|
||
# Create the subdomain list with proper HTML list formatting
|
||
subdomain_list = "<ul>"
|
||
for sub in subdomains:
|
||
subdomain_list += f"<li>{sub}</li>"
|
||
subdomain_list += "</ul>"
|
||
|
||
# Create collapsible content
|
||
content = f"<strong>🔍 Subdomain Enumeration Results for {domain}</strong><br><br>"
|
||
content += f"<strong>Total subdomains found:</strong> {count}<br><br>"
|
||
content += f"<strong>Discovered subdomains:</strong><br>{subdomain_list}"
|
||
|
||
# Wrap in details tag for Matrix compatibility
|
||
message = f"<details><summary><strong>🔍 Subdomain Enumeration: {domain} ({count} found)</strong></summary>{content}</details>"
|
||
|
||
return message
|
||
|
||
async def handle_command(room, message, bot, prefix, config):
|
||
"""
|
||
Function to handle the !subdomains command.
|
||
|
||
Args:
|
||
room (Room): The Matrix room where the command was invoked.
|
||
message (RoomMessage): The message object containing the command.
|
||
bot (Bot): The bot object.
|
||
prefix (str): The command prefix.
|
||
config (dict): Configuration parameters.
|
||
|
||
Returns:
|
||
None
|
||
"""
|
||
match = botlib.MessageMatch(room, message, bot, prefix)
|
||
|
||
if match.is_not_from_this_bot() and match.prefix() and match.command("subdomains"):
|
||
args = match.args()
|
||
|
||
if len(args) < 1:
|
||
await bot.api.send_text_message(
|
||
room.room_id,
|
||
"Usage: !subdomains <domain>\nExample: !subdomains example.com"
|
||
)
|
||
return
|
||
|
||
domain = args[0].strip()
|
||
logging.info(f"Received !subdomains command for: {domain}")
|
||
|
||
try:
|
||
# Notify user that we're starting the lookup
|
||
await bot.api.send_text_message(
|
||
room.room_id,
|
||
f"🔍 Enumerating subdomains for {domain} using CertSpotter API..."
|
||
)
|
||
|
||
# Query CertSpotter API
|
||
subdomains = await query_certspotter(domain)
|
||
|
||
# Format and send results
|
||
result_message = await format_subdomain_results(domain, subdomains)
|
||
await bot.api.send_markdown_message(room.room_id, result_message)
|
||
logging.info(f"Successfully sent subdomain enumeration results for {domain}")
|
||
|
||
except Exception as e:
|
||
await bot.api.send_text_message(
|
||
room.room_id,
|
||
f"An error occurred during subdomain enumeration for {domain}. Please try again later."
|
||
)
|
||
logging.error(f"Error in subdomains plugin for {domain}: {e}", exc_info=True)
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Plugin Metadata
|
||
# ---------------------------------------------------------------------------
|
||
|
||
__version__ = "1.0.0"
|
||
__author__ = "Funguy Bot"
|
||
__description__ = "Subdomain enumeration via CertSpotter"
|
||
__help__ = """
|
||
<details>
|
||
<summary><strong>!subdomains</strong> – Enumerate subdomains</summary>
|
||
<p><code>!subdomains <domain></code> – Finds subdomains using SSL certificate transparency logs (CertSpotter API).</p>
|
||
</details>
|
||
"""
|