"""
This plugin provides a command to search Exploit-DB for security exploits and vulnerabilities.
Uses the searchsploit-style approach with the files.csv database.
"""
import logging
import requests
import csv
import io
import simplematrixbotlib as botlib
from datetime import datetime
# Exploit-DB CSV database URL
EXPLOITDB_CSV_URL = "https://gitlab.com/exploit-database/exploitdb/-/raw/main/files_exploits.csv"
def format_exploit(exploit, index, total):
"""
Format an exploit entry for display.
Args:
exploit (dict): The exploit data.
index (int): Current result index.
total (int): Total number of results.
Returns:
str: Formatted HTML string.
"""
edb_id = exploit.get('id', 'N/A')
title = exploit.get('description', 'No title')
date = exploit.get('date', 'Unknown')
author = exploit.get('author', 'Unknown')
exploit_type = exploit.get('type', 'Unknown')
platform = exploit.get('platform', 'Unknown')
# Build the URL
url = f"https://www.exploit-db.com/exploits/{edb_id}"
output = f"""💣 Exploit {index}/{total}
Title: {title}
EDB-ID: {edb_id}
Type: {exploit_type} | Platform: {platform}
Author: {author} | Date: {date}
URL: {url}"""
return output
async def search_exploitdb_csv(query, max_results=5):
"""
Search Exploit-DB CSV database for exploits matching the query.
Args:
query (str): Search term.
max_results (int): Maximum number of results to return.
Returns:
list: List of exploit dictionaries, or None on error.
"""
try:
logging.info(f"Downloading Exploit-DB CSV database...")
headers = {
'User-Agent': 'FunguyBot/1.0',
}
# Download the CSV file
response = requests.get(EXPLOITDB_CSV_URL, headers=headers, timeout=30)
response.raise_for_status()
# Parse CSV
csv_data = response.text
csv_file = io.StringIO(csv_data)
reader = csv.DictReader(csv_file)
# Search through CSV
results = []
query_lower = query.lower()
logging.info(f"Searching CSV for: {query}")
for row in reader:
# Search in description (title) and other fields
description = row.get('description', '').lower()
file_path = row.get('file', '').lower()
if query_lower in description or query_lower in file_path:
exploit = {
'id': row.get('id', 'N/A'),
'description': row.get('description', 'No title'),
'date': row.get('date_published', row.get('date', 'Unknown')),
'author': row.get('author', 'Unknown'),
'type': row.get('type', 'Unknown'),
'platform': row.get('platform', 'Unknown')
}
results.append(exploit)
if len(results) >= max_results:
break
return results
except requests.exceptions.Timeout:
logging.error("Timeout downloading Exploit-DB database")
return None
except requests.exceptions.RequestException as e:
logging.error(f"Error downloading Exploit-DB database: {e}")
return None
except Exception as e:
logging.error(f"Unexpected error searching Exploit-DB: {e}", exc_info=True)
return None
async def search_exploitdb_google(query, max_results=5):
"""
Alternative: Search Exploit-DB using site-specific search.
Returns formatted search URLs instead of parsing.
Args:
query (str): Search term.
max_results (int): Maximum number of results to return.
Returns:
str: Formatted search information.
"""
# Create search URLs
exploitdb_search_url = f"https://www.exploit-db.com/search?q={query}"
google_search_url = f"https://www.google.com/search?q=site:exploit-db.com+{query}"
output = f"""💣 Exploit-DB Search for: {query}
Direct Search:
{exploitdb_search_url}
Google Site Search:
{google_search_url}
💡 Tip: You can also use searchsploit
command-line tool for offline searches.
⚠️ Use responsibly and only on systems you have permission to test."""
return output
async def handle_command(room, message, bot, prefix, config):
"""
Function to handle the !exploitdb 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("exploitdb"):
logging.info("Received !exploitdb command")
args = match.args()
if len(args) < 1:
await bot.api.send_text_message(
room.room_id,
"Usage: !exploitdb [max_results]\n"
"Examples:\n"
" !exploitdb wordpress\n"
" !exploitdb apache 3\n"
" !exploitdb windows privilege escalation\n"
"Searches Exploit-DB for security vulnerabilities and exploits."
)
logging.info("Sent usage message for !exploitdb")
return
# Check if last argument is a number (max results)
max_results = 5
search_terms = args
if args[-1].isdigit():
max_results = int(args[-1])
if max_results < 1:
max_results = 1
elif max_results > 10:
max_results = 10
search_terms = args[:-1]
query = ' '.join(search_terms)
try:
# Send "searching" message
await bot.api.send_text_message(
room.room_id,
f"🔍 Searching Exploit-DB for: {query}... (this may take a moment)"
)
# Try CSV search first
exploits = await search_exploitdb_csv(query, max_results)
if exploits is None:
# Fallback to providing search links
logging.warning("CSV search failed, providing search links instead")
output = await search_exploitdb_google(query, max_results)
await bot.api.send_markdown_message(room.room_id, output)
return
if not exploits:
# Also provide search links when no results
output = f"No exploits found in local search for: {query}
"
output += await search_exploitdb_google(query, max_results)
await bot.api.send_markdown_message(room.room_id, output)
logging.info(f"No exploits found for: {query}")
return
total = len(exploits)
logging.info(f"Found {total} exploit(s) for: {query}")
# Format all results
output = f"💣 Exploit-DB Search Results for: {query}
"
for idx, exploit in enumerate(exploits, 1):
output += format_exploit(exploit, idx, total)
output += "
"
output += f"⚠️ Use responsibly and only on systems you have permission to test."
# Wrap in collapsible details if more than 2 results
if total > 2:
summary = f"💣 Exploit-DB: {query} ({total} results)"
output = f"{summary}
{output} "
await bot.api.send_markdown_message(room.room_id, output)
logging.info(f"Sent {total} exploit(s) for: {query}")
except Exception as e:
await bot.api.send_text_message(
room.room_id,
f"An error occurred while searching Exploit-DB: {str(e)}"
)
logging.error(f"Error in exploitdb plugin: {e}", exc_info=True)