"""
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 = "
"
for sub in subdomains:
subdomain_list += f"- {sub}
"
subdomain_list += "
"
# Create collapsible content
content = f"🔍 Subdomain Enumeration Results for {domain}
"
content += f"Total subdomains found: {count}
"
content += f"Discovered subdomains:
{subdomain_list}"
# Wrap in details tag for Matrix compatibility
message = f"🔍 Subdomain Enumeration: {domain} ({count} found)
{content} "
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 \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__ = """
!subdomains – Enumerate subdomains
!subdomains <domain> – Finds subdomains using SSL certificate transparency logs (CertSpotter API).
"""