mirror of
https://github.com/Gowtham-Darkseid/AutoPentestX.git
synced 2026-07-05 16:37:47 +02:00
Initial commit: AutoPentestX - Complete automated penetration testing toolkit with 8 modules, comprehensive documentation, and professional PDF reporting
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
"""
|
||||
AutoPentestX - Automated Penetration Testing Toolkit
|
||||
Modules Package Initialization
|
||||
"""
|
||||
|
||||
__version__ = "1.0.0"
|
||||
__author__ = "AutoPentestX Team"
|
||||
@@ -0,0 +1,282 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
AutoPentestX - CVE Lookup Module
|
||||
Looks up CVE information for discovered services and versions
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import re
|
||||
from datetime import datetime
|
||||
import time
|
||||
|
||||
class CVELookup:
|
||||
def __init__(self):
|
||||
"""Initialize CVE lookup module"""
|
||||
self.cve_data = []
|
||||
self.api_url = "https://cve.circl.lu/api"
|
||||
self.nvd_api = "https://services.nvd.nist.gov/rest/json/cves/2.0"
|
||||
|
||||
def search_cve_by_product(self, product, version=""):
|
||||
"""Search for CVEs by product name and version"""
|
||||
print(f"[*] Searching CVEs for: {product} {version}")
|
||||
|
||||
cves = []
|
||||
|
||||
try:
|
||||
# Clean product name
|
||||
product_clean = product.lower().strip()
|
||||
product_clean = re.sub(r'[^\w\s-]', '', product_clean)
|
||||
|
||||
# Try CVE CIRCL API
|
||||
search_url = f"{self.api_url}/search/{product_clean}"
|
||||
|
||||
response = requests.get(search_url, timeout=10)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
|
||||
if isinstance(data, list):
|
||||
# Limit results to most recent/relevant
|
||||
for cve_entry in data[:10]: # Top 10 results
|
||||
cve_info = self.parse_cve_entry(cve_entry)
|
||||
|
||||
# Filter by version if specified
|
||||
if version and version.strip():
|
||||
if self.version_matches(cve_entry, version):
|
||||
cves.append(cve_info)
|
||||
else:
|
||||
cves.append(cve_info)
|
||||
|
||||
print(f"[✓] Found {len(cves)} relevant CVEs for {product}")
|
||||
|
||||
else:
|
||||
print(f"[!] CVE API returned status code: {response.status_code}")
|
||||
|
||||
# Rate limiting
|
||||
time.sleep(1)
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"[!] CVE lookup failed for {product}: {e}")
|
||||
except Exception as e:
|
||||
print(f"[!] Error during CVE lookup: {e}")
|
||||
|
||||
return cves
|
||||
|
||||
def parse_cve_entry(self, cve_entry):
|
||||
"""Parse CVE entry and extract relevant information"""
|
||||
try:
|
||||
cve_id = cve_entry.get('id', 'Unknown')
|
||||
summary = cve_entry.get('summary', 'No description available')
|
||||
|
||||
# Get CVSS score
|
||||
cvss_score = 0.0
|
||||
if 'cvss' in cve_entry:
|
||||
cvss_score = float(cve_entry.get('cvss', 0.0))
|
||||
elif 'impact' in cve_entry:
|
||||
if 'baseMetricV3' in cve_entry['impact']:
|
||||
cvss_score = float(cve_entry['impact']['baseMetricV3'].get('cvssV3', {}).get('baseScore', 0.0))
|
||||
elif 'baseMetricV2' in cve_entry['impact']:
|
||||
cvss_score = float(cve_entry['impact']['baseMetricV2'].get('cvssV2', {}).get('baseScore', 0.0))
|
||||
|
||||
# Determine risk level
|
||||
risk_level = self.calculate_risk_level(cvss_score)
|
||||
|
||||
# Get published date
|
||||
published = cve_entry.get('Published', cve_entry.get('published', 'Unknown'))
|
||||
|
||||
# Check if exploit is available
|
||||
exploitable = self.check_exploit_availability(cve_id, cve_entry)
|
||||
|
||||
return {
|
||||
'cve_id': cve_id,
|
||||
'description': summary[:500], # Limit description length
|
||||
'cvss_score': cvss_score,
|
||||
'risk_level': risk_level,
|
||||
'published_date': published,
|
||||
'exploitable': exploitable,
|
||||
'references': cve_entry.get('references', [])[:3] # Top 3 references
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
print(f"[!] Error parsing CVE entry: {e}")
|
||||
return {
|
||||
'cve_id': 'Unknown',
|
||||
'description': 'Error parsing CVE data',
|
||||
'cvss_score': 0.0,
|
||||
'risk_level': 'UNKNOWN',
|
||||
'published_date': 'Unknown',
|
||||
'exploitable': False,
|
||||
'references': []
|
||||
}
|
||||
|
||||
def calculate_risk_level(self, cvss_score):
|
||||
"""Calculate risk level based on CVSS score"""
|
||||
if cvss_score >= 9.0:
|
||||
return "CRITICAL"
|
||||
elif cvss_score >= 7.0:
|
||||
return "HIGH"
|
||||
elif cvss_score >= 4.0:
|
||||
return "MEDIUM"
|
||||
elif cvss_score > 0.0:
|
||||
return "LOW"
|
||||
else:
|
||||
return "UNKNOWN"
|
||||
|
||||
def check_exploit_availability(self, cve_id, cve_entry):
|
||||
"""Check if exploit is publicly available"""
|
||||
# Check in CVE data
|
||||
if 'exploit-db' in str(cve_entry).lower():
|
||||
return True
|
||||
|
||||
if 'metasploit' in str(cve_entry).lower():
|
||||
return True
|
||||
|
||||
# Check references
|
||||
if 'references' in cve_entry:
|
||||
for ref in cve_entry['references']:
|
||||
ref_str = str(ref).lower()
|
||||
if any(keyword in ref_str for keyword in ['exploit', 'poc', 'metasploit', 'exploit-db']):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def version_matches(self, cve_entry, version):
|
||||
"""Check if CVE applies to specific version"""
|
||||
try:
|
||||
# Simple version matching - can be enhanced
|
||||
cve_text = json.dumps(cve_entry).lower()
|
||||
version_clean = version.lower().strip()
|
||||
|
||||
# Extract version numbers
|
||||
version_nums = re.findall(r'\d+\.\d+', version_clean)
|
||||
|
||||
if version_nums:
|
||||
for ver in version_nums:
|
||||
if ver in cve_text:
|
||||
return True
|
||||
|
||||
# If version is mentioned anywhere in CVE
|
||||
if version_clean in cve_text:
|
||||
return True
|
||||
|
||||
# Default: include if we can't determine
|
||||
return True
|
||||
|
||||
except:
|
||||
return True
|
||||
|
||||
def lookup_services(self, services):
|
||||
"""Lookup CVEs for multiple services"""
|
||||
print("\n" + "="*60)
|
||||
print("AutoPentestX - CVE Lookup")
|
||||
print("="*60)
|
||||
print(f"Services to check: {len(services)}")
|
||||
print("="*60 + "\n")
|
||||
|
||||
all_cves = []
|
||||
|
||||
for service in services:
|
||||
port = service.get('port')
|
||||
service_name = service.get('service', 'unknown')
|
||||
version = service.get('version', '')
|
||||
|
||||
print(f"\n[*] Checking port {port}: {service_name} {version}")
|
||||
|
||||
# Extract product name from service
|
||||
product = self.extract_product_name(service_name, version)
|
||||
|
||||
if product and product != 'unknown':
|
||||
cves = self.search_cve_by_product(product, version)
|
||||
|
||||
for cve in cves:
|
||||
cve['port'] = port
|
||||
cve['service'] = service_name
|
||||
cve['version'] = version
|
||||
all_cves.append(cve)
|
||||
|
||||
print(f" [!] {cve['cve_id']} - CVSS: {cve['cvss_score']} ({cve['risk_level']})")
|
||||
|
||||
self.cve_data = all_cves
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("CVE LOOKUP SUMMARY")
|
||||
print("="*60)
|
||||
print(f"Total CVEs found: {len(all_cves)}")
|
||||
|
||||
# Count by risk level
|
||||
critical = len([c for c in all_cves if c['risk_level'] == 'CRITICAL'])
|
||||
high = len([c for c in all_cves if c['risk_level'] == 'HIGH'])
|
||||
medium = len([c for c in all_cves if c['risk_level'] == 'MEDIUM'])
|
||||
low = len([c for c in all_cves if c['risk_level'] == 'LOW'])
|
||||
|
||||
print(f" CRITICAL: {critical}")
|
||||
print(f" HIGH: {high}")
|
||||
print(f" MEDIUM: {medium}")
|
||||
print(f" LOW: {low}")
|
||||
print("="*60 + "\n")
|
||||
|
||||
return all_cves
|
||||
|
||||
def extract_product_name(self, service_name, version):
|
||||
"""Extract clean product name for CVE search"""
|
||||
# Common product name mappings
|
||||
product_map = {
|
||||
'http': 'apache',
|
||||
'ssh': 'openssh',
|
||||
'ftp': 'ftp',
|
||||
'smtp': 'postfix',
|
||||
'mysql': 'mysql',
|
||||
'postgresql': 'postgresql',
|
||||
'microsoft-ds': 'microsoft windows',
|
||||
'netbios-ssn': 'samba',
|
||||
'domain': 'bind',
|
||||
'ssl/http': 'apache',
|
||||
'https': 'apache'
|
||||
}
|
||||
|
||||
service_lower = service_name.lower()
|
||||
|
||||
# Check mapped products
|
||||
for key, product in product_map.items():
|
||||
if key in service_lower:
|
||||
return product
|
||||
|
||||
# Try to extract from version string
|
||||
version_lower = version.lower()
|
||||
|
||||
products = ['apache', 'nginx', 'openssh', 'vsftpd', 'proftpd',
|
||||
'mysql', 'postgresql', 'bind', 'postfix', 'dovecot']
|
||||
|
||||
for product in products:
|
||||
if product in version_lower:
|
||||
return product
|
||||
|
||||
# Return service name if nothing else matches
|
||||
return service_name if service_name != 'unknown' else None
|
||||
|
||||
def get_results(self):
|
||||
"""Return CVE lookup results"""
|
||||
return self.cve_data
|
||||
|
||||
def save_results(self, filename):
|
||||
"""Save CVE data to JSON file"""
|
||||
try:
|
||||
with open(filename, 'w') as f:
|
||||
json.dump(self.cve_data, f, indent=4)
|
||||
print(f"[✓] CVE data saved to {filename}")
|
||||
except Exception as e:
|
||||
print(f"[✗] Failed to save CVE data: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Test CVE lookup
|
||||
cve_lookup = CVELookup()
|
||||
|
||||
sample_services = [
|
||||
{'port': 22, 'service': 'ssh', 'version': 'OpenSSH 7.4'},
|
||||
{'port': 80, 'service': 'http', 'version': 'Apache 2.4.6'}
|
||||
]
|
||||
|
||||
results = cve_lookup.lookup_services(sample_services)
|
||||
print(f"\nTotal CVEs found: {len(results)}")
|
||||
@@ -0,0 +1,284 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
AutoPentestX - Database Module
|
||||
Handles SQLite database operations for storing scan results
|
||||
"""
|
||||
|
||||
import sqlite3
|
||||
import json
|
||||
from datetime import datetime
|
||||
import os
|
||||
|
||||
class Database:
|
||||
def __init__(self, db_path="database/autopentestx.db"):
|
||||
"""Initialize database connection"""
|
||||
self.db_path = db_path
|
||||
self.ensure_directory()
|
||||
self.conn = None
|
||||
self.cursor = None
|
||||
self.connect()
|
||||
self.create_tables()
|
||||
|
||||
def ensure_directory(self):
|
||||
"""Ensure database directory exists"""
|
||||
db_dir = os.path.dirname(self.db_path)
|
||||
if db_dir and not os.path.exists(db_dir):
|
||||
os.makedirs(db_dir)
|
||||
|
||||
def connect(self):
|
||||
"""Establish database connection"""
|
||||
try:
|
||||
self.conn = sqlite3.connect(self.db_path)
|
||||
self.cursor = self.conn.cursor()
|
||||
print(f"[✓] Database connected: {self.db_path}")
|
||||
except sqlite3.Error as e:
|
||||
print(f"[✗] Database connection error: {e}")
|
||||
raise
|
||||
|
||||
def create_tables(self):
|
||||
"""Create necessary database tables"""
|
||||
try:
|
||||
# Scans table
|
||||
self.cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS scans (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
target TEXT NOT NULL,
|
||||
scan_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
os_detection TEXT,
|
||||
scan_duration REAL,
|
||||
total_ports INTEGER,
|
||||
open_ports INTEGER,
|
||||
vulnerabilities_found INTEGER,
|
||||
risk_score TEXT,
|
||||
status TEXT DEFAULT 'completed'
|
||||
)
|
||||
''')
|
||||
|
||||
# Ports table
|
||||
self.cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS ports (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
scan_id INTEGER,
|
||||
port_number INTEGER,
|
||||
protocol TEXT,
|
||||
state TEXT,
|
||||
service_name TEXT,
|
||||
service_version TEXT,
|
||||
FOREIGN KEY (scan_id) REFERENCES scans(id)
|
||||
)
|
||||
''')
|
||||
|
||||
# Vulnerabilities table
|
||||
self.cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS vulnerabilities (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
scan_id INTEGER,
|
||||
port_number INTEGER,
|
||||
service_name TEXT,
|
||||
vuln_name TEXT,
|
||||
vuln_description TEXT,
|
||||
cve_id TEXT,
|
||||
cvss_score REAL,
|
||||
risk_level TEXT,
|
||||
exploitable BOOLEAN,
|
||||
FOREIGN KEY (scan_id) REFERENCES scans(id)
|
||||
)
|
||||
''')
|
||||
|
||||
# Exploits table
|
||||
self.cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS exploits (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
scan_id INTEGER,
|
||||
vuln_id INTEGER,
|
||||
exploit_name TEXT,
|
||||
exploit_status TEXT,
|
||||
exploit_result TEXT,
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (scan_id) REFERENCES scans(id),
|
||||
FOREIGN KEY (vuln_id) REFERENCES vulnerabilities(id)
|
||||
)
|
||||
''')
|
||||
|
||||
# Web vulnerabilities table
|
||||
self.cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS web_vulnerabilities (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
scan_id INTEGER,
|
||||
url TEXT,
|
||||
vuln_type TEXT,
|
||||
severity TEXT,
|
||||
description TEXT,
|
||||
FOREIGN KEY (scan_id) REFERENCES scans(id)
|
||||
)
|
||||
''')
|
||||
|
||||
self.conn.commit()
|
||||
print("[✓] Database tables created successfully")
|
||||
|
||||
except sqlite3.Error as e:
|
||||
print(f"[✗] Error creating tables: {e}")
|
||||
raise
|
||||
|
||||
def insert_scan(self, target, os_detection="Unknown"):
|
||||
"""Insert new scan record"""
|
||||
try:
|
||||
self.cursor.execute('''
|
||||
INSERT INTO scans (target, os_detection, status)
|
||||
VALUES (?, ?, 'in_progress')
|
||||
''', (target, os_detection))
|
||||
self.conn.commit()
|
||||
return self.cursor.lastrowid
|
||||
except sqlite3.Error as e:
|
||||
print(f"[✗] Error inserting scan: {e}")
|
||||
return None
|
||||
|
||||
def update_scan(self, scan_id, **kwargs):
|
||||
"""Update scan record with new information"""
|
||||
try:
|
||||
updates = []
|
||||
values = []
|
||||
for key, value in kwargs.items():
|
||||
updates.append(f"{key} = ?")
|
||||
values.append(value)
|
||||
|
||||
values.append(scan_id)
|
||||
query = f"UPDATE scans SET {', '.join(updates)} WHERE id = ?"
|
||||
self.cursor.execute(query, values)
|
||||
self.conn.commit()
|
||||
except sqlite3.Error as e:
|
||||
print(f"[✗] Error updating scan: {e}")
|
||||
|
||||
def insert_port(self, scan_id, port_data):
|
||||
"""Insert port information"""
|
||||
try:
|
||||
self.cursor.execute('''
|
||||
INSERT INTO ports (scan_id, port_number, protocol, state, service_name, service_version)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
''', (
|
||||
scan_id,
|
||||
port_data.get('port'),
|
||||
port_data.get('protocol', 'tcp'),
|
||||
port_data.get('state', 'open'),
|
||||
port_data.get('service', 'unknown'),
|
||||
port_data.get('version', 'unknown')
|
||||
))
|
||||
self.conn.commit()
|
||||
except sqlite3.Error as e:
|
||||
print(f"[✗] Error inserting port: {e}")
|
||||
|
||||
def insert_vulnerability(self, scan_id, vuln_data):
|
||||
"""Insert vulnerability information"""
|
||||
try:
|
||||
self.cursor.execute('''
|
||||
INSERT INTO vulnerabilities
|
||||
(scan_id, port_number, service_name, vuln_name, vuln_description,
|
||||
cve_id, cvss_score, risk_level, exploitable)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
''', (
|
||||
scan_id,
|
||||
vuln_data.get('port'),
|
||||
vuln_data.get('service'),
|
||||
vuln_data.get('name'),
|
||||
vuln_data.get('description'),
|
||||
vuln_data.get('cve_id'),
|
||||
vuln_data.get('cvss_score', 0.0),
|
||||
vuln_data.get('risk_level', 'UNKNOWN'),
|
||||
vuln_data.get('exploitable', False)
|
||||
))
|
||||
self.conn.commit()
|
||||
return self.cursor.lastrowid
|
||||
except sqlite3.Error as e:
|
||||
print(f"[✗] Error inserting vulnerability: {e}")
|
||||
return None
|
||||
|
||||
def insert_web_vulnerability(self, scan_id, web_vuln_data):
|
||||
"""Insert web vulnerability information"""
|
||||
try:
|
||||
self.cursor.execute('''
|
||||
INSERT INTO web_vulnerabilities
|
||||
(scan_id, url, vuln_type, severity, description)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
''', (
|
||||
scan_id,
|
||||
web_vuln_data.get('url'),
|
||||
web_vuln_data.get('type'),
|
||||
web_vuln_data.get('severity'),
|
||||
web_vuln_data.get('description')
|
||||
))
|
||||
self.conn.commit()
|
||||
except sqlite3.Error as e:
|
||||
print(f"[✗] Error inserting web vulnerability: {e}")
|
||||
|
||||
def insert_exploit(self, scan_id, vuln_id, exploit_data):
|
||||
"""Insert exploit attempt information"""
|
||||
try:
|
||||
self.cursor.execute('''
|
||||
INSERT INTO exploits (scan_id, vuln_id, exploit_name, exploit_status, exploit_result)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
''', (
|
||||
scan_id,
|
||||
vuln_id,
|
||||
exploit_data.get('name'),
|
||||
exploit_data.get('status'),
|
||||
exploit_data.get('result')
|
||||
))
|
||||
self.conn.commit()
|
||||
except sqlite3.Error as e:
|
||||
print(f"[✗] Error inserting exploit: {e}")
|
||||
|
||||
def get_scan_data(self, scan_id):
|
||||
"""Retrieve complete scan data"""
|
||||
try:
|
||||
# Get scan info
|
||||
self.cursor.execute('SELECT * FROM scans WHERE id = ?', (scan_id,))
|
||||
scan = self.cursor.fetchone()
|
||||
|
||||
# Get ports
|
||||
self.cursor.execute('SELECT * FROM ports WHERE scan_id = ?', (scan_id,))
|
||||
ports = self.cursor.fetchall()
|
||||
|
||||
# Get vulnerabilities
|
||||
self.cursor.execute('SELECT * FROM vulnerabilities WHERE scan_id = ?', (scan_id,))
|
||||
vulnerabilities = self.cursor.fetchall()
|
||||
|
||||
# Get web vulnerabilities
|
||||
self.cursor.execute('SELECT * FROM web_vulnerabilities WHERE scan_id = ?', (scan_id,))
|
||||
web_vulns = self.cursor.fetchall()
|
||||
|
||||
# Get exploits
|
||||
self.cursor.execute('SELECT * FROM exploits WHERE scan_id = ?', (scan_id,))
|
||||
exploits = self.cursor.fetchall()
|
||||
|
||||
return {
|
||||
'scan': scan,
|
||||
'ports': ports,
|
||||
'vulnerabilities': vulnerabilities,
|
||||
'web_vulnerabilities': web_vulns,
|
||||
'exploits': exploits
|
||||
}
|
||||
except sqlite3.Error as e:
|
||||
print(f"[✗] Error retrieving scan data: {e}")
|
||||
return None
|
||||
|
||||
def get_all_scans(self):
|
||||
"""Get list of all scans"""
|
||||
try:
|
||||
self.cursor.execute('SELECT * FROM scans ORDER BY scan_date DESC')
|
||||
return self.cursor.fetchall()
|
||||
except sqlite3.Error as e:
|
||||
print(f"[✗] Error retrieving scans: {e}")
|
||||
return []
|
||||
|
||||
def close(self):
|
||||
"""Close database connection"""
|
||||
if self.conn:
|
||||
self.conn.close()
|
||||
print("[✓] Database connection closed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Test database
|
||||
db = Database()
|
||||
print("Database module test completed successfully")
|
||||
db.close()
|
||||
@@ -0,0 +1,315 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
AutoPentestX - Exploit Engine
|
||||
Safe exploitation module with Metasploit integration
|
||||
Educational/Lab use only - Safe mode enabled by default
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import json
|
||||
import re
|
||||
from datetime import datetime
|
||||
import os
|
||||
|
||||
class ExploitEngine:
|
||||
def __init__(self, safe_mode=True):
|
||||
"""Initialize exploit engine"""
|
||||
self.safe_mode = safe_mode
|
||||
self.exploit_results = []
|
||||
self.metasploit_available = self.check_metasploit()
|
||||
|
||||
# Exploit database (common exploits mapped to vulnerabilities)
|
||||
self.exploit_db = {
|
||||
'vsftpd 2.3.4': {
|
||||
'name': 'vsftpd_234_backdoor',
|
||||
'module': 'exploit/unix/ftp/vsftpd_234_backdoor',
|
||||
'description': 'VSFTPD v2.3.4 Backdoor Command Execution',
|
||||
'safe': True
|
||||
},
|
||||
'ProFTPD 1.3.3c': {
|
||||
'name': 'proftpd_133c_backdoor',
|
||||
'module': 'exploit/unix/ftp/proftpd_133c_backdoor',
|
||||
'description': 'ProFTPD 1.3.3c Backdoor',
|
||||
'safe': True
|
||||
},
|
||||
'EternalBlue': {
|
||||
'name': 'ms17_010_eternalblue',
|
||||
'module': 'exploit/windows/smb/ms17_010_eternalblue',
|
||||
'description': 'MS17-010 EternalBlue SMB Remote Windows Kernel Pool Corruption',
|
||||
'safe': False # Potentially destructive
|
||||
},
|
||||
'Shellshock': {
|
||||
'name': 'apache_mod_cgi_bash_env_exec',
|
||||
'module': 'exploit/multi/http/apache_mod_cgi_bash_env_exec',
|
||||
'description': 'Apache mod_cgi Bash Environment Variable Code Injection (Shellshock)',
|
||||
'safe': True
|
||||
},
|
||||
'Drupalgeddon2': {
|
||||
'name': 'drupalgeddon2',
|
||||
'module': 'exploit/unix/webapp/drupal_drupalgeddon2',
|
||||
'description': 'Drupal Drupalgeddon 2 Remote Code Execution',
|
||||
'safe': True
|
||||
}
|
||||
}
|
||||
|
||||
def check_metasploit(self):
|
||||
"""Check if Metasploit is installed"""
|
||||
try:
|
||||
result = subprocess.run(['which', 'msfconsole'],
|
||||
capture_output=True, text=True)
|
||||
if result.returncode == 0:
|
||||
print("[✓] Metasploit Framework detected")
|
||||
return True
|
||||
else:
|
||||
print("[!] Metasploit not found - Exploitation features limited")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"[!] Error checking Metasploit: {e}")
|
||||
return False
|
||||
|
||||
def match_exploits(self, vulnerabilities, cves):
|
||||
"""Match vulnerabilities to available exploits"""
|
||||
print("\n" + "="*60)
|
||||
print("AutoPentestX - Exploit Matching")
|
||||
print("="*60)
|
||||
|
||||
matched_exploits = []
|
||||
|
||||
# Match based on service versions
|
||||
for vuln in vulnerabilities:
|
||||
service = vuln.get('service', '').lower()
|
||||
version = vuln.get('version', '').lower()
|
||||
|
||||
# Check exploit database
|
||||
for key, exploit in self.exploit_db.items():
|
||||
if key.lower() in f"{service} {version}":
|
||||
matched_exploits.append({
|
||||
'vulnerability': vuln,
|
||||
'exploit': exploit,
|
||||
'port': vuln.get('port'),
|
||||
'confidence': 'HIGH'
|
||||
})
|
||||
print(f"[✓] Exploit matched: {exploit['name']} for port {vuln.get('port')}")
|
||||
|
||||
# Match based on CVEs
|
||||
for cve in cves:
|
||||
cve_id = cve.get('cve_id', '')
|
||||
|
||||
# Known CVE to exploit mappings
|
||||
cve_exploits = {
|
||||
'CVE-2017-0144': 'EternalBlue', # MS17-010
|
||||
'CVE-2014-6271': 'Shellshock',
|
||||
'CVE-2018-7600': 'Drupalgeddon2'
|
||||
}
|
||||
|
||||
for cve_pattern, exploit_key in cve_exploits.items():
|
||||
if cve_pattern in cve_id and exploit_key in self.exploit_db:
|
||||
matched_exploits.append({
|
||||
'cve': cve,
|
||||
'exploit': self.exploit_db[exploit_key],
|
||||
'port': cve.get('port'),
|
||||
'confidence': 'MEDIUM'
|
||||
})
|
||||
print(f"[✓] Exploit matched: {self.exploit_db[exploit_key]['name']} "
|
||||
f"for CVE {cve_id}")
|
||||
|
||||
print(f"\n[*] Total exploits matched: {len(matched_exploits)}")
|
||||
return matched_exploits
|
||||
|
||||
def run_metasploit_exploit(self, target, port, exploit_module, payload='generic/shell_reverse_tcp'):
|
||||
"""Execute Metasploit exploit in safe mode"""
|
||||
if not self.metasploit_available:
|
||||
return {
|
||||
'status': 'SKIPPED',
|
||||
'reason': 'Metasploit not available'
|
||||
}
|
||||
|
||||
if not self.safe_mode:
|
||||
print("[!] WARNING: Safe mode disabled - This could cause system damage!")
|
||||
return {
|
||||
'status': 'BLOCKED',
|
||||
'reason': 'Exploitation disabled for safety'
|
||||
}
|
||||
|
||||
print(f"[*] Simulating exploit: {exploit_module}")
|
||||
print(f" Target: {target}:{port}")
|
||||
print(f" Payload: {payload}")
|
||||
|
||||
# In safe mode, we only CHECK if the exploit would work, not actually execute
|
||||
result = {
|
||||
'status': 'SIMULATED',
|
||||
'exploit_module': exploit_module,
|
||||
'target': target,
|
||||
'port': port,
|
||||
'payload': payload,
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'safe_mode': True,
|
||||
'result': 'Exploit would be executed in non-safe mode'
|
||||
}
|
||||
|
||||
# Create resource script for Metasploit (for reference)
|
||||
rc_script = self.create_rc_script(target, port, exploit_module, payload)
|
||||
result['rc_script'] = rc_script
|
||||
|
||||
return result
|
||||
|
||||
def create_rc_script(self, target, port, exploit_module, payload):
|
||||
"""Create Metasploit resource script"""
|
||||
rc_content = f"""# Metasploit Resource Script
|
||||
# Generated by AutoPentestX
|
||||
# Target: {target}:{port}
|
||||
# Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
||||
|
||||
use {exploit_module}
|
||||
set RHOSTS {target}
|
||||
set RPORT {port}
|
||||
set PAYLOAD {payload}
|
||||
set LHOST 0.0.0.0
|
||||
set LPORT 4444
|
||||
check
|
||||
# Exploit execution disabled in safe mode
|
||||
# Uncomment to execute: exploit
|
||||
"""
|
||||
|
||||
# Save RC script
|
||||
rc_filename = f"exploits/exploit_{target}_{port}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.rc"
|
||||
|
||||
try:
|
||||
os.makedirs('exploits', exist_ok=True)
|
||||
with open(rc_filename, 'w') as f:
|
||||
f.write(rc_content)
|
||||
print(f"[✓] Metasploit RC script saved: {rc_filename}")
|
||||
except Exception as e:
|
||||
print(f"[!] Failed to save RC script: {e}")
|
||||
|
||||
return rc_filename
|
||||
|
||||
def simulate_exploitation(self, matched_exploits, target):
|
||||
"""Simulate exploitation attempts (safe mode)"""
|
||||
print("\n" + "="*60)
|
||||
print("AutoPentestX - Exploitation Simulation")
|
||||
print("="*60)
|
||||
print(f"Safe Mode: {'ENABLED' if self.safe_mode else 'DISABLED'}")
|
||||
print(f"Target: {target}")
|
||||
print("="*60 + "\n")
|
||||
|
||||
if self.safe_mode:
|
||||
print("[*] Running in SAFE MODE - No actual exploitation will occur")
|
||||
print("[*] Generating exploit feasibility reports...\n")
|
||||
|
||||
exploitation_results = []
|
||||
|
||||
for match in matched_exploits:
|
||||
exploit = match.get('exploit')
|
||||
port = match.get('port')
|
||||
confidence = match.get('confidence')
|
||||
|
||||
if not exploit:
|
||||
continue
|
||||
|
||||
# Check if exploit is safe to run
|
||||
if not exploit.get('safe', False) and self.safe_mode:
|
||||
print(f"[!] Skipping potentially dangerous exploit: {exploit['name']}")
|
||||
result = {
|
||||
'port': port,
|
||||
'exploit_name': exploit['name'],
|
||||
'status': 'SKIPPED',
|
||||
'reason': 'Exploit marked as potentially destructive',
|
||||
'safe_mode': True
|
||||
}
|
||||
else:
|
||||
# Simulate exploit
|
||||
result = self.run_metasploit_exploit(
|
||||
target,
|
||||
port,
|
||||
exploit.get('module', ''),
|
||||
'generic/shell_reverse_tcp'
|
||||
)
|
||||
result['exploit_name'] = exploit['name']
|
||||
result['description'] = exploit['description']
|
||||
result['confidence'] = confidence
|
||||
|
||||
exploitation_results.append(result)
|
||||
|
||||
print(f"[*] Port {port}: {exploit['name']} - {result['status']}")
|
||||
|
||||
self.exploit_results = exploitation_results
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("EXPLOITATION SUMMARY")
|
||||
print("="*60)
|
||||
print(f"Exploits matched: {len(matched_exploits)}")
|
||||
print(f"Exploits simulated: {len(exploitation_results)}")
|
||||
print(f"Safe mode: {'ENABLED' if self.safe_mode else 'DISABLED'}")
|
||||
print("="*60 + "\n")
|
||||
|
||||
if self.safe_mode:
|
||||
print("[i] Note: All exploitation was simulated only.")
|
||||
print("[i] RC scripts generated for manual testing if needed.")
|
||||
|
||||
return exploitation_results
|
||||
|
||||
def generate_exploit_report(self, filename):
|
||||
"""Generate exploitation report"""
|
||||
try:
|
||||
report = {
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'safe_mode': self.safe_mode,
|
||||
'metasploit_available': self.metasploit_available,
|
||||
'exploitation_results': self.exploit_results,
|
||||
'summary': {
|
||||
'total_attempts': len(self.exploit_results),
|
||||
'simulated': len([r for r in self.exploit_results if r.get('status') == 'SIMULATED']),
|
||||
'skipped': len([r for r in self.exploit_results if r.get('status') == 'SKIPPED']),
|
||||
'successful': len([r for r in self.exploit_results if r.get('status') == 'SUCCESS'])
|
||||
}
|
||||
}
|
||||
|
||||
with open(filename, 'w') as f:
|
||||
json.dump(report, f, indent=4)
|
||||
|
||||
print(f"[✓] Exploitation report saved: {filename}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[✗] Failed to save exploitation report: {e}")
|
||||
|
||||
def get_results(self):
|
||||
"""Return exploitation results"""
|
||||
return self.exploit_results
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("="*60)
|
||||
print("AutoPentestX - Exploit Engine Test")
|
||||
print("="*60)
|
||||
print("\n[!] WARNING: This tool is for EDUCATIONAL/AUTHORIZED TESTING ONLY")
|
||||
print("[!] Unauthorized use is ILLEGAL and UNETHICAL\n")
|
||||
|
||||
# Test exploit engine
|
||||
exploit_engine = ExploitEngine(safe_mode=True)
|
||||
|
||||
# Sample vulnerabilities
|
||||
sample_vulns = [
|
||||
{
|
||||
'port': 21,
|
||||
'service': 'ftp',
|
||||
'version': 'vsftpd 2.3.4',
|
||||
'name': 'FTP Backdoor'
|
||||
}
|
||||
]
|
||||
|
||||
sample_cves = [
|
||||
{
|
||||
'port': 445,
|
||||
'cve_id': 'CVE-2017-0144',
|
||||
'service': 'microsoft-ds'
|
||||
}
|
||||
]
|
||||
|
||||
# Match exploits
|
||||
matched = exploit_engine.match_exploits(sample_vulns, sample_cves)
|
||||
|
||||
# Simulate exploitation
|
||||
if matched:
|
||||
results = exploit_engine.simulate_exploitation(matched, "192.168.1.100")
|
||||
exploit_engine.generate_exploit_report("exploits/test_exploit_report.json")
|
||||
@@ -0,0 +1,546 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
AutoPentestX - PDF Report Generator
|
||||
Generates professional penetration testing reports using ReportLab
|
||||
"""
|
||||
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.pagesizes import letter, A4
|
||||
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer, PageBreak, Image
|
||||
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
||||
from reportlab.lib.units import inch
|
||||
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
|
||||
from reportlab.pdfgen import canvas
|
||||
from datetime import datetime
|
||||
import os
|
||||
|
||||
class PDFReportGenerator:
|
||||
def __init__(self, target, scan_id):
|
||||
"""Initialize PDF report generator"""
|
||||
self.target = target
|
||||
self.scan_id = scan_id
|
||||
self.timestamp = datetime.now()
|
||||
self.filename = f"reports/AutoPentestX_Report_{target.replace('.', '_')}_{self.timestamp.strftime('%Y%m%d_%H%M%S')}.pdf"
|
||||
|
||||
# Ensure reports directory exists
|
||||
os.makedirs('reports', exist_ok=True)
|
||||
|
||||
# Initialize document
|
||||
self.doc = SimpleDocTemplate(self.filename, pagesize=letter)
|
||||
self.story = []
|
||||
self.styles = getSampleStyleSheet()
|
||||
|
||||
# Custom styles
|
||||
self.create_custom_styles()
|
||||
|
||||
def create_custom_styles(self):
|
||||
"""Create custom paragraph styles"""
|
||||
# Title style
|
||||
self.styles.add(ParagraphStyle(
|
||||
name='CustomTitle',
|
||||
parent=self.styles['Heading1'],
|
||||
fontSize=24,
|
||||
textColor=colors.HexColor('#1a1a1a'),
|
||||
spaceAfter=30,
|
||||
alignment=TA_CENTER,
|
||||
fontName='Helvetica-Bold'
|
||||
))
|
||||
|
||||
# Section heading style
|
||||
self.styles.add(ParagraphStyle(
|
||||
name='SectionHeading',
|
||||
parent=self.styles['Heading2'],
|
||||
fontSize=16,
|
||||
textColor=colors.HexColor('#2c3e50'),
|
||||
spaceAfter=12,
|
||||
spaceBefore=12,
|
||||
fontName='Helvetica-Bold'
|
||||
))
|
||||
|
||||
# Risk level styles
|
||||
self.styles.add(ParagraphStyle(
|
||||
name='CriticalRisk',
|
||||
parent=self.styles['Normal'],
|
||||
fontSize=12,
|
||||
textColor=colors.red,
|
||||
fontName='Helvetica-Bold'
|
||||
))
|
||||
|
||||
self.styles.add(ParagraphStyle(
|
||||
name='HighRisk',
|
||||
parent=self.styles['Normal'],
|
||||
fontSize=12,
|
||||
textColor=colors.orangered,
|
||||
fontName='Helvetica-Bold'
|
||||
))
|
||||
|
||||
self.styles.add(ParagraphStyle(
|
||||
name='MediumRisk',
|
||||
parent=self.styles['Normal'],
|
||||
fontSize=12,
|
||||
textColor=colors.orange,
|
||||
fontName='Helvetica-Bold'
|
||||
))
|
||||
|
||||
self.styles.add(ParagraphStyle(
|
||||
name='LowRisk',
|
||||
parent=self.styles['Normal'],
|
||||
fontSize=12,
|
||||
textColor=colors.blue,
|
||||
fontName='Helvetica-Bold'
|
||||
))
|
||||
|
||||
def add_cover_page(self, tester_name="AutoPentestX Team"):
|
||||
"""Add cover page to report"""
|
||||
# Title
|
||||
title = Paragraph("PENETRATION TESTING REPORT", self.styles['CustomTitle'])
|
||||
self.story.append(title)
|
||||
self.story.append(Spacer(1, 0.5*inch))
|
||||
|
||||
# Target information
|
||||
target_info = f"""
|
||||
<para align=center fontSize=14>
|
||||
<b>Target System:</b> {self.target}<br/>
|
||||
<b>Scan ID:</b> {self.scan_id}<br/>
|
||||
<b>Report Date:</b> {self.timestamp.strftime('%B %d, %Y')}<br/>
|
||||
<b>Report Time:</b> {self.timestamp.strftime('%H:%M:%S %Z')}<br/>
|
||||
</para>
|
||||
"""
|
||||
self.story.append(Paragraph(target_info, self.styles['Normal']))
|
||||
self.story.append(Spacer(1, 1*inch))
|
||||
|
||||
# Confidential notice
|
||||
confidential = """
|
||||
<para align=center fontSize=12 textColor=red>
|
||||
<b>CONFIDENTIAL</b><br/>
|
||||
This report contains sensitive security information.<br/>
|
||||
Handle with appropriate care and restrict distribution.
|
||||
</para>
|
||||
"""
|
||||
self.story.append(Paragraph(confidential, self.styles['Normal']))
|
||||
self.story.append(Spacer(1, 0.5*inch))
|
||||
|
||||
# Tester information
|
||||
tester_info = f"""
|
||||
<para align=center fontSize=12>
|
||||
<b>Prepared by:</b> {tester_name}<br/>
|
||||
<b>Tool:</b> AutoPentestX v1.0<br/>
|
||||
<b>Framework:</b> Automated Penetration Testing Toolkit
|
||||
</para>
|
||||
"""
|
||||
self.story.append(Paragraph(tester_info, self.styles['Normal']))
|
||||
|
||||
self.story.append(PageBreak())
|
||||
|
||||
def add_executive_summary(self, risk_summary):
|
||||
"""Add executive summary section"""
|
||||
self.story.append(Paragraph("EXECUTIVE SUMMARY", self.styles['SectionHeading']))
|
||||
|
||||
overall_risk = risk_summary.get('overall_risk_level', 'UNKNOWN')
|
||||
total_vulns = risk_summary.get('total_vulnerabilities', 0)
|
||||
|
||||
summary_text = f"""
|
||||
<para align=justify>
|
||||
This penetration testing report presents the findings of an automated security assessment
|
||||
conducted on the target system <b>{self.target}</b>. The assessment was performed on
|
||||
{self.timestamp.strftime('%B %d, %Y')} using the AutoPentestX automated penetration testing toolkit.
|
||||
<br/><br/>
|
||||
<b>Overall Risk Level: </b><font color="{self.get_risk_color(overall_risk)}">{overall_risk}</font><br/>
|
||||
<b>Total Vulnerabilities Identified: </b>{total_vulns}<br/>
|
||||
<b>Critical/High Risk Items: </b>{len(risk_summary.get('high_risk_items', []))}<br/>
|
||||
<b>Web Vulnerabilities: </b>{risk_summary.get('web_vulnerabilities', 0)}<br/>
|
||||
<b>SQL Injection Points: </b>{risk_summary.get('sql_vulnerabilities', 0)}<br/>
|
||||
</para>
|
||||
"""
|
||||
|
||||
self.story.append(Paragraph(summary_text, self.styles['Normal']))
|
||||
self.story.append(Spacer(1, 0.3*inch))
|
||||
|
||||
# Risk assessment summary
|
||||
if overall_risk in ['CRITICAL', 'HIGH']:
|
||||
warning = f"""
|
||||
<para align=justify textColor=red>
|
||||
<b>⚠ CRITICAL FINDING:</b> The target system exhibits {overall_risk} risk level.
|
||||
Immediate remediation action is required to address identified security vulnerabilities
|
||||
before the system can be considered secure for production use.
|
||||
</para>
|
||||
"""
|
||||
self.story.append(Paragraph(warning, self.styles['Normal']))
|
||||
|
||||
self.story.append(Spacer(1, 0.3*inch))
|
||||
|
||||
def add_scan_details(self, scan_data):
|
||||
"""Add scan details section"""
|
||||
self.story.append(Paragraph("SCAN DETAILS", self.styles['SectionHeading']))
|
||||
|
||||
os_detection = scan_data.get('os_detection', 'Unknown')
|
||||
scan_time = scan_data.get('scan_time', 0) or 0
|
||||
total_ports = len(scan_data.get('ports', []))
|
||||
|
||||
details_text = f"""
|
||||
<para>
|
||||
<b>Target:</b> {self.target}<br/>
|
||||
<b>Operating System:</b> {os_detection}<br/>
|
||||
<b>Scan Duration:</b> {scan_time:.2f} seconds<br/>
|
||||
<b>Total Open Ports:</b> {total_ports}<br/>
|
||||
<b>Scan Method:</b> Automated comprehensive scan using Nmap, Nikto, and SQLMap<br/>
|
||||
</para>
|
||||
"""
|
||||
|
||||
self.story.append(Paragraph(details_text, self.styles['Normal']))
|
||||
self.story.append(Spacer(1, 0.3*inch))
|
||||
|
||||
def add_open_ports_table(self, ports_data):
|
||||
"""Add table of open ports"""
|
||||
self.story.append(Paragraph("OPEN PORTS AND SERVICES", self.styles['SectionHeading']))
|
||||
|
||||
if not ports_data:
|
||||
self.story.append(Paragraph("No open ports detected.", self.styles['Normal']))
|
||||
return
|
||||
|
||||
# Create table data
|
||||
table_data = [['Port', 'Protocol', 'State', 'Service', 'Version']]
|
||||
|
||||
for port in ports_data[:20]: # Limit to 20 ports for readability
|
||||
table_data.append([
|
||||
str(port.get('port', '')),
|
||||
port.get('protocol', 'tcp'),
|
||||
port.get('state', 'open'),
|
||||
port.get('service', 'unknown'),
|
||||
port.get('version', 'unknown')[:30] # Truncate long versions
|
||||
])
|
||||
|
||||
# Create table
|
||||
table = Table(table_data, colWidths=[0.8*inch, 1*inch, 0.8*inch, 1.5*inch, 2.5*inch])
|
||||
table.setStyle(TableStyle([
|
||||
('BACKGROUND', (0, 0), (-1, 0), colors.grey),
|
||||
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
|
||||
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
|
||||
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0, 0), (-1, 0), 10),
|
||||
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
|
||||
('BACKGROUND', (0, 1), (-1, -1), colors.beige),
|
||||
('GRID', (0, 0), (-1, -1), 1, colors.black),
|
||||
('FONTSIZE', (0, 1), (-1, -1), 8),
|
||||
]))
|
||||
|
||||
self.story.append(table)
|
||||
self.story.append(Spacer(1, 0.3*inch))
|
||||
|
||||
def add_vulnerabilities_table(self, vulnerabilities, cves):
|
||||
"""Add vulnerabilities table"""
|
||||
self.story.append(PageBreak())
|
||||
self.story.append(Paragraph("VULNERABILITIES IDENTIFIED", self.styles['SectionHeading']))
|
||||
|
||||
all_vulns = []
|
||||
|
||||
# Add regular vulnerabilities
|
||||
for vuln in vulnerabilities:
|
||||
all_vulns.append({
|
||||
'port': vuln.get('port', 'N/A'),
|
||||
'name': vuln.get('name', 'Unknown'),
|
||||
'severity': vuln.get('severity', 'UNKNOWN'),
|
||||
'cve_id': vuln.get('cve_id', 'N/A'),
|
||||
'description': vuln.get('description', '')[:100]
|
||||
})
|
||||
|
||||
# Add CVEs
|
||||
for cve in cves[:15]: # Limit CVEs
|
||||
all_vulns.append({
|
||||
'port': cve.get('port', 'N/A'),
|
||||
'name': cve.get('cve_id', 'Unknown'),
|
||||
'severity': cve.get('risk_level', 'UNKNOWN'),
|
||||
'cve_id': cve.get('cve_id', 'N/A'),
|
||||
'description': cve.get('description', '')[:100]
|
||||
})
|
||||
|
||||
if not all_vulns:
|
||||
self.story.append(Paragraph("No vulnerabilities detected.", self.styles['Normal']))
|
||||
return
|
||||
|
||||
# Create table
|
||||
table_data = [['Port', 'Vulnerability', 'Severity', 'CVE ID']]
|
||||
|
||||
for vuln in all_vulns[:25]: # Limit to 25 for space
|
||||
table_data.append([
|
||||
str(vuln['port']),
|
||||
vuln['name'][:40],
|
||||
vuln['severity'],
|
||||
vuln['cve_id']
|
||||
])
|
||||
|
||||
table = Table(table_data, colWidths=[0.7*inch, 2.5*inch, 1*inch, 1.5*inch])
|
||||
table.setStyle(TableStyle([
|
||||
('BACKGROUND', (0, 0), (-1, 0), colors.grey),
|
||||
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
|
||||
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
|
||||
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0, 0), (-1, 0), 10),
|
||||
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
|
||||
('GRID', (0, 0), (-1, -1), 1, colors.black),
|
||||
('FONTSIZE', (0, 1), (-1, -1), 8),
|
||||
]))
|
||||
|
||||
self.story.append(table)
|
||||
self.story.append(Spacer(1, 0.3*inch))
|
||||
|
||||
def add_risk_assessment(self, risk_summary):
|
||||
"""Add risk assessment section"""
|
||||
self.story.append(PageBreak())
|
||||
self.story.append(Paragraph("RISK ASSESSMENT", self.styles['SectionHeading']))
|
||||
|
||||
overall_risk = risk_summary.get('overall_risk_level', 'UNKNOWN')
|
||||
total_risk_score = risk_summary.get('total_risk_score', 0)
|
||||
|
||||
risk_text = f"""
|
||||
<para align=justify>
|
||||
Based on the comprehensive analysis of identified vulnerabilities, their severity levels,
|
||||
exploitability, and potential impact, the overall risk assessment for the target system is:
|
||||
<br/><br/>
|
||||
<b>Overall Risk Level:</b> <font color="{self.get_risk_color(overall_risk)}">{overall_risk}</font><br/>
|
||||
<b>Total Risk Score:</b> {total_risk_score:.2f}<br/>
|
||||
<b>Average Risk per Port:</b> {risk_summary.get('average_risk_per_port', 0):.2f}<br/>
|
||||
</para>
|
||||
"""
|
||||
|
||||
self.story.append(Paragraph(risk_text, self.styles['Normal']))
|
||||
self.story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
# High risk items
|
||||
high_risk_items = risk_summary.get('high_risk_items', [])
|
||||
if high_risk_items:
|
||||
self.story.append(Paragraph("<b>High Risk Items:</b>", self.styles['Normal']))
|
||||
|
||||
for item in high_risk_items[:10]:
|
||||
if isinstance(item, dict):
|
||||
if 'port' in item:
|
||||
item_text = f"• Port {item.get('port')}: {item.get('service', 'Unknown')} - Risk Score: {item.get('risk_score', 0)}/10"
|
||||
else:
|
||||
item_text = f"• {item.get('category', 'Unknown')}: {item.get('count', 0)} issues found"
|
||||
|
||||
self.story.append(Paragraph(item_text, self.styles['Normal']))
|
||||
|
||||
self.story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
def add_exploitation_results(self, exploit_results):
|
||||
"""Add exploitation results section"""
|
||||
self.story.append(Paragraph("EXPLOITATION ASSESSMENT", self.styles['SectionHeading']))
|
||||
|
||||
if not exploit_results:
|
||||
self.story.append(Paragraph("No exploitation attempts were made.", self.styles['Normal']))
|
||||
return
|
||||
|
||||
exploit_text = f"""
|
||||
<para align=justify>
|
||||
The following exploitation scenarios were evaluated in SAFE MODE.
|
||||
No actual exploitation was performed to prevent system damage.
|
||||
<br/><br/>
|
||||
<b>Total Exploits Identified:</b> {len(exploit_results)}<br/>
|
||||
</para>
|
||||
"""
|
||||
|
||||
self.story.append(Paragraph(exploit_text, self.styles['Normal']))
|
||||
self.story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
# List exploits
|
||||
for exploit in exploit_results[:10]:
|
||||
exploit_info = f"""
|
||||
<para>
|
||||
<b>• Port {exploit.get('port')}:</b> {exploit.get('exploit_name', 'Unknown')}<br/>
|
||||
<i>Status:</i> {exploit.get('status', 'Unknown')}<br/>
|
||||
<i>Description:</i> {exploit.get('description', 'N/A')[:100]}<br/>
|
||||
</para>
|
||||
"""
|
||||
self.story.append(Paragraph(exploit_info, self.styles['Normal']))
|
||||
|
||||
self.story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
def add_recommendations(self, recommendations):
|
||||
"""Add security recommendations"""
|
||||
self.story.append(PageBreak())
|
||||
self.story.append(Paragraph("SECURITY RECOMMENDATIONS", self.styles['SectionHeading']))
|
||||
|
||||
if not recommendations:
|
||||
self.story.append(Paragraph("No specific recommendations available.", self.styles['Normal']))
|
||||
return
|
||||
|
||||
intro = """
|
||||
<para align=justify>
|
||||
Based on the identified vulnerabilities and risk assessment, the following remediation
|
||||
actions are recommended to improve the security posture of the target system:
|
||||
</para>
|
||||
"""
|
||||
self.story.append(Paragraph(intro, self.styles['Normal']))
|
||||
self.story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
# Group by priority
|
||||
priorities = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']
|
||||
|
||||
for priority in priorities:
|
||||
priority_recs = [r for r in recommendations if r.get('priority') == priority]
|
||||
|
||||
if priority_recs:
|
||||
self.story.append(Paragraph(f"<b>{priority} Priority:</b>", self.styles['Normal']))
|
||||
|
||||
for rec in priority_recs[:5]:
|
||||
rec_text = f"""
|
||||
<para>
|
||||
<b>• {rec.get('action', 'Unknown Action')}</b><br/>
|
||||
{rec.get('description', 'No description')}<br/>
|
||||
</para>
|
||||
"""
|
||||
self.story.append(Paragraph(rec_text, self.styles['Normal']))
|
||||
|
||||
self.story.append(Spacer(1, 0.15*inch))
|
||||
|
||||
def add_conclusion(self):
|
||||
"""Add conclusion section"""
|
||||
self.story.append(PageBreak())
|
||||
self.story.append(Paragraph("CONCLUSION", self.styles['SectionHeading']))
|
||||
|
||||
conclusion_text = """
|
||||
<para align=justify>
|
||||
This automated penetration testing assessment has identified various security vulnerabilities
|
||||
and potential risks in the target system. The findings should be carefully reviewed and
|
||||
prioritized based on the risk levels assigned.
|
||||
<br/><br/>
|
||||
It is strongly recommended to address all CRITICAL and HIGH severity findings immediately,
|
||||
followed by MEDIUM and LOW severity items according to available resources and priorities.
|
||||
<br/><br/>
|
||||
Regular security assessments should be conducted to maintain a strong security posture
|
||||
and protect against emerging threats.
|
||||
<br/><br/>
|
||||
<b>Important Notes:</b><br/>
|
||||
• This assessment was conducted using automated tools and may not identify all vulnerabilities<br/>
|
||||
• Manual verification and testing is recommended for critical systems<br/>
|
||||
• Results should be validated before taking remediation actions<br/>
|
||||
• This report is confidential and should be handled securely<br/>
|
||||
</para>
|
||||
"""
|
||||
|
||||
self.story.append(Paragraph(conclusion_text, self.styles['Normal']))
|
||||
|
||||
def add_disclaimer(self):
|
||||
"""Add legal disclaimer"""
|
||||
self.story.append(Spacer(1, 0.5*inch))
|
||||
|
||||
disclaimer_text = """
|
||||
<para align=justify fontSize=9 textColor=grey>
|
||||
<b>DISCLAIMER:</b> This penetration testing report is provided for educational and authorized
|
||||
security assessment purposes only. The tools and techniques used are intended for legitimate
|
||||
security testing in controlled environments with proper authorization. Unauthorized use of
|
||||
these tools against systems you do not own or have explicit permission to test is illegal
|
||||
and unethical. The developers and users of AutoPentestX assume no liability for misuse or
|
||||
damage caused by this tool.
|
||||
</para>
|
||||
"""
|
||||
|
||||
self.story.append(Paragraph(disclaimer_text, self.styles['Normal']))
|
||||
|
||||
def get_risk_color(self, risk_level):
|
||||
"""Get color code for risk level"""
|
||||
colors_map = {
|
||||
'CRITICAL': 'red',
|
||||
'HIGH': 'orangered',
|
||||
'MEDIUM': 'orange',
|
||||
'LOW': 'blue',
|
||||
'MINIMAL': 'green',
|
||||
'UNKNOWN': 'grey'
|
||||
}
|
||||
return colors_map.get(risk_level, 'black')
|
||||
|
||||
def generate_report(self, scan_data, vulnerabilities, cves, web_vulns, sql_vulns,
|
||||
risk_summary, exploit_results, tester_name="AutoPentestX Team"):
|
||||
"""Generate complete PDF report"""
|
||||
print("\n" + "="*60)
|
||||
print("AutoPentestX - PDF Report Generation")
|
||||
print("="*60)
|
||||
print(f"Target: {self.target}")
|
||||
print(f"Generating report: {self.filename}")
|
||||
print("="*60 + "\n")
|
||||
|
||||
try:
|
||||
# Build report sections
|
||||
print("[*] Adding cover page...")
|
||||
self.add_cover_page(tester_name)
|
||||
|
||||
print("[*] Adding executive summary...")
|
||||
self.add_executive_summary(risk_summary)
|
||||
|
||||
print("[*] Adding scan details...")
|
||||
self.add_scan_details(scan_data)
|
||||
|
||||
print("[*] Adding open ports table...")
|
||||
self.add_open_ports_table(scan_data.get('ports', []))
|
||||
|
||||
print("[*] Adding vulnerabilities...")
|
||||
self.add_vulnerabilities_table(vulnerabilities, cves)
|
||||
|
||||
print("[*] Adding risk assessment...")
|
||||
self.add_risk_assessment(risk_summary)
|
||||
|
||||
print("[*] Adding exploitation results...")
|
||||
self.add_exploitation_results(exploit_results)
|
||||
|
||||
print("[*] Adding recommendations...")
|
||||
self.add_recommendations(risk_summary.get('recommendations', []))
|
||||
|
||||
print("[*] Adding conclusion...")
|
||||
self.add_conclusion()
|
||||
|
||||
print("[*] Adding disclaimer...")
|
||||
self.add_disclaimer()
|
||||
|
||||
# Build PDF
|
||||
print("[*] Building PDF document...")
|
||||
self.doc.build(self.story)
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("PDF REPORT GENERATED SUCCESSFULLY")
|
||||
print("="*60)
|
||||
print(f"Report saved to: {self.filename}")
|
||||
print(f"File size: {os.path.getsize(self.filename) / 1024:.2f} KB")
|
||||
print("="*60 + "\n")
|
||||
|
||||
return self.filename
|
||||
|
||||
except Exception as e:
|
||||
print(f"[✗] Error generating PDF report: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Test PDF generator
|
||||
print("Testing PDF Report Generator...")
|
||||
|
||||
sample_scan = {
|
||||
'target': '192.168.1.100',
|
||||
'os_detection': 'Linux Ubuntu 20.04',
|
||||
'scan_time': 45.67,
|
||||
'ports': [
|
||||
{'port': 22, 'protocol': 'tcp', 'state': 'open', 'service': 'ssh', 'version': 'OpenSSH 8.2'},
|
||||
{'port': 80, 'protocol': 'tcp', 'state': 'open', 'service': 'http', 'version': 'Apache 2.4.41'}
|
||||
]
|
||||
}
|
||||
|
||||
sample_vulns = [
|
||||
{'port': 22, 'name': 'Outdated SSH', 'severity': 'MEDIUM', 'cve_id': 'N/A', 'description': 'SSH version is outdated'}
|
||||
]
|
||||
|
||||
sample_risk = {
|
||||
'overall_risk_level': 'MEDIUM',
|
||||
'total_risk_score': 15.5,
|
||||
'average_risk_per_port': 7.75,
|
||||
'high_risk_items': [],
|
||||
'total_vulnerabilities': 1,
|
||||
'web_vulnerabilities': 0,
|
||||
'sql_vulnerabilities': 0,
|
||||
'recommendations': [
|
||||
{'priority': 'HIGH', 'action': 'Update SSH', 'description': 'Update to latest version'}
|
||||
]
|
||||
}
|
||||
|
||||
generator = PDFReportGenerator('192.168.1.100', 1)
|
||||
generator.generate_report(sample_scan, sample_vulns, [], [], [], sample_risk, [])
|
||||
@@ -0,0 +1,300 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
AutoPentestX - Risk Assessment Engine
|
||||
Calculates overall risk scores based on vulnerabilities and CVSS scores
|
||||
"""
|
||||
|
||||
from collections import defaultdict
|
||||
import json
|
||||
|
||||
class RiskEngine:
|
||||
def __init__(self):
|
||||
"""Initialize risk engine"""
|
||||
self.risk_scores = {
|
||||
'CRITICAL': 10,
|
||||
'HIGH': 7,
|
||||
'MEDIUM': 4,
|
||||
'LOW': 2,
|
||||
'UNKNOWN': 1
|
||||
}
|
||||
|
||||
self.severity_weights = {
|
||||
'exploitable': 2.0,
|
||||
'public_exploit': 1.5,
|
||||
'network_accessible': 1.3,
|
||||
'authenticated': 0.7
|
||||
}
|
||||
|
||||
def calculate_cvss_risk(self, cvss_score):
|
||||
"""Convert CVSS score to risk level"""
|
||||
if cvss_score >= 9.0:
|
||||
return "CRITICAL"
|
||||
elif cvss_score >= 7.0:
|
||||
return "HIGH"
|
||||
elif cvss_score >= 4.0:
|
||||
return "MEDIUM"
|
||||
elif cvss_score > 0.0:
|
||||
return "LOW"
|
||||
else:
|
||||
return "UNKNOWN"
|
||||
|
||||
def calculate_port_risk(self, port_data, vulnerabilities, cves):
|
||||
"""Calculate risk for a specific port"""
|
||||
risk_score = 0
|
||||
risk_factors = []
|
||||
|
||||
port_num = port_data.get('port')
|
||||
service = port_data.get('service', '').lower()
|
||||
|
||||
# Base risk for port exposure
|
||||
if port_num < 1024: # Well-known ports
|
||||
risk_score += 1
|
||||
risk_factors.append("Well-known port exposed")
|
||||
|
||||
# Check for sensitive services
|
||||
sensitive_services = ['ftp', 'telnet', 'smtp', 'mysql', 'postgresql',
|
||||
'microsoft-ds', 'netbios-ssn', 'rdp']
|
||||
|
||||
if any(svc in service for svc in sensitive_services):
|
||||
risk_score += 2
|
||||
risk_factors.append(f"Sensitive service exposed: {service}")
|
||||
|
||||
# Check vulnerabilities for this port
|
||||
port_vulns = [v for v in vulnerabilities if v.get('port') == port_num]
|
||||
|
||||
for vuln in port_vulns:
|
||||
severity = vuln.get('severity', 'UNKNOWN')
|
||||
risk_score += self.risk_scores.get(severity, 1)
|
||||
risk_factors.append(f"{severity} vulnerability: {vuln.get('name', 'Unknown')}")
|
||||
|
||||
if vuln.get('exploitable', False):
|
||||
risk_score *= 1.5
|
||||
risk_factors.append("Exploitable vulnerability detected")
|
||||
|
||||
# Check CVEs for this port
|
||||
port_cves = [c for c in cves if c.get('port') == port_num]
|
||||
|
||||
for cve in port_cves:
|
||||
cvss = cve.get('cvss_score', 0)
|
||||
risk_score += cvss / 2 # Weight CVE score
|
||||
|
||||
if cve.get('exploitable', False):
|
||||
risk_score *= 1.3
|
||||
risk_factors.append(f"CVE with public exploit: {cve.get('cve_id')}")
|
||||
|
||||
# Normalize to 0-10 scale
|
||||
normalized_score = min(risk_score, 10.0)
|
||||
|
||||
return {
|
||||
'port': port_num,
|
||||
'service': service,
|
||||
'risk_score': round(normalized_score, 2),
|
||||
'risk_level': self.calculate_cvss_risk(normalized_score),
|
||||
'risk_factors': risk_factors
|
||||
}
|
||||
|
||||
def calculate_overall_risk(self, scan_data, vulnerabilities, cves, web_vulns, sql_vulns):
|
||||
"""Calculate overall system risk score"""
|
||||
print("\n" + "="*60)
|
||||
print("AutoPentestX - Risk Assessment")
|
||||
print("="*60)
|
||||
|
||||
total_risk = 0
|
||||
high_risk_items = []
|
||||
port_risks = []
|
||||
|
||||
# Analyze each port
|
||||
ports = scan_data.get('ports', [])
|
||||
|
||||
for port in ports:
|
||||
port_risk = self.calculate_port_risk(port, vulnerabilities, cves)
|
||||
port_risks.append(port_risk)
|
||||
total_risk += port_risk['risk_score']
|
||||
|
||||
if port_risk['risk_level'] in ['CRITICAL', 'HIGH']:
|
||||
high_risk_items.append(port_risk)
|
||||
print(f"[!] HIGH RISK: Port {port_risk['port']} - {port_risk['service']} "
|
||||
f"(Score: {port_risk['risk_score']}/10)")
|
||||
|
||||
# Add web vulnerabilities to risk
|
||||
web_risk = len(web_vulns) * 2 # Each web vuln adds 2 points
|
||||
total_risk += web_risk
|
||||
|
||||
if web_vulns:
|
||||
print(f"[!] Web vulnerabilities detected: {len(web_vulns)} issues")
|
||||
high_risk_items.append({
|
||||
'category': 'Web Security',
|
||||
'risk_score': web_risk,
|
||||
'count': len(web_vulns)
|
||||
})
|
||||
|
||||
# Add SQL injection vulnerabilities (highest risk)
|
||||
sql_risk = len(sql_vulns) * 5 # SQL injection is critical
|
||||
total_risk += sql_risk
|
||||
|
||||
if sql_vulns:
|
||||
print(f"[!] CRITICAL: SQL Injection vulnerabilities found: {len(sql_vulns)}")
|
||||
high_risk_items.append({
|
||||
'category': 'SQL Injection',
|
||||
'risk_score': sql_risk,
|
||||
'count': len(sql_vulns),
|
||||
'risk_level': 'CRITICAL'
|
||||
})
|
||||
|
||||
# Calculate average risk per port
|
||||
avg_risk = total_risk / len(ports) if ports else 0
|
||||
|
||||
# Determine overall risk level
|
||||
overall_risk_level = self.determine_overall_risk_level(
|
||||
total_risk, avg_risk, high_risk_items
|
||||
)
|
||||
|
||||
# Risk summary
|
||||
risk_summary = {
|
||||
'total_risk_score': round(total_risk, 2),
|
||||
'average_risk_per_port': round(avg_risk, 2),
|
||||
'overall_risk_level': overall_risk_level,
|
||||
'high_risk_items': high_risk_items,
|
||||
'port_risks': port_risks,
|
||||
'total_vulnerabilities': len(vulnerabilities) + len(cves),
|
||||
'web_vulnerabilities': len(web_vulns),
|
||||
'sql_vulnerabilities': len(sql_vulns),
|
||||
'recommendations': self.generate_recommendations(
|
||||
overall_risk_level, high_risk_items, vulnerabilities, cves
|
||||
)
|
||||
}
|
||||
|
||||
# Print summary
|
||||
print("\n" + "="*60)
|
||||
print("RISK ASSESSMENT SUMMARY")
|
||||
print("="*60)
|
||||
print(f"Overall Risk Level: {overall_risk_level}")
|
||||
print(f"Total Risk Score: {risk_summary['total_risk_score']}")
|
||||
print(f"Average Risk per Port: {risk_summary['average_risk_per_port']}")
|
||||
print(f"High Risk Items: {len(high_risk_items)}")
|
||||
print(f"Total Vulnerabilities: {risk_summary['total_vulnerabilities']}")
|
||||
print("="*60 + "\n")
|
||||
|
||||
return risk_summary
|
||||
|
||||
def determine_overall_risk_level(self, total_risk, avg_risk, high_risk_items):
|
||||
"""Determine overall system risk level"""
|
||||
# Count critical and high risk items
|
||||
critical_count = len([item for item in high_risk_items
|
||||
if item.get('risk_level') == 'CRITICAL'])
|
||||
high_count = len([item for item in high_risk_items
|
||||
if item.get('risk_level') == 'HIGH'])
|
||||
|
||||
# Decision logic
|
||||
if critical_count > 0 or total_risk > 50:
|
||||
return "CRITICAL"
|
||||
elif high_count >= 3 or total_risk > 30:
|
||||
return "HIGH"
|
||||
elif avg_risk > 4 or total_risk > 15:
|
||||
return "MEDIUM"
|
||||
elif total_risk > 5:
|
||||
return "LOW"
|
||||
else:
|
||||
return "MINIMAL"
|
||||
|
||||
def generate_recommendations(self, risk_level, high_risk_items, vulnerabilities, cves):
|
||||
"""Generate security recommendations based on findings"""
|
||||
recommendations = []
|
||||
|
||||
# General recommendations by risk level
|
||||
if risk_level in ['CRITICAL', 'HIGH']:
|
||||
recommendations.append({
|
||||
'priority': 'CRITICAL',
|
||||
'action': 'Immediate Action Required',
|
||||
'description': 'Critical vulnerabilities detected. Isolate system and patch immediately.'
|
||||
})
|
||||
|
||||
# Specific recommendations based on vulnerabilities
|
||||
vuln_types = defaultdict(int)
|
||||
for vuln in vulnerabilities:
|
||||
vuln_types[vuln.get('service', 'unknown')] += 1
|
||||
|
||||
for service, count in vuln_types.items():
|
||||
if count > 0:
|
||||
recommendations.append({
|
||||
'priority': 'HIGH',
|
||||
'action': f'Update {service} service',
|
||||
'description': f'Found {count} vulnerabilities in {service}. Update to latest version.'
|
||||
})
|
||||
|
||||
# CVE-based recommendations
|
||||
critical_cves = [cve for cve in cves if cve.get('risk_level') == 'CRITICAL']
|
||||
if critical_cves:
|
||||
recommendations.append({
|
||||
'priority': 'CRITICAL',
|
||||
'action': 'Patch Critical CVEs',
|
||||
'description': f'Apply security patches for {len(critical_cves)} critical CVEs.'
|
||||
})
|
||||
|
||||
# SQL Injection recommendations
|
||||
if any('sql' in str(item).lower() for item in high_risk_items):
|
||||
recommendations.append({
|
||||
'priority': 'CRITICAL',
|
||||
'action': 'Fix SQL Injection Vulnerabilities',
|
||||
'description': 'Implement parameterized queries and input validation immediately.'
|
||||
})
|
||||
|
||||
# General security recommendations
|
||||
recommendations.extend([
|
||||
{
|
||||
'priority': 'MEDIUM',
|
||||
'action': 'Enable Firewall',
|
||||
'description': 'Configure firewall to restrict access to necessary ports only.'
|
||||
},
|
||||
{
|
||||
'priority': 'MEDIUM',
|
||||
'action': 'Update All Services',
|
||||
'description': 'Ensure all services are running latest stable versions.'
|
||||
},
|
||||
{
|
||||
'priority': 'LOW',
|
||||
'action': 'Implement Monitoring',
|
||||
'description': 'Set up intrusion detection and continuous monitoring.'
|
||||
},
|
||||
{
|
||||
'priority': 'LOW',
|
||||
'action': 'Security Hardening',
|
||||
'description': 'Apply security hardening guidelines for the operating system.'
|
||||
}
|
||||
])
|
||||
|
||||
return recommendations
|
||||
|
||||
def save_risk_assessment(self, risk_summary, filename):
|
||||
"""Save risk assessment to JSON file"""
|
||||
try:
|
||||
with open(filename, 'w') as f:
|
||||
json.dump(risk_summary, f, indent=4)
|
||||
print(f"[✓] Risk assessment saved to {filename}")
|
||||
except Exception as e:
|
||||
print(f"[✗] Failed to save risk assessment: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Test risk engine
|
||||
risk_engine = RiskEngine()
|
||||
|
||||
sample_scan = {
|
||||
'ports': [
|
||||
{'port': 22, 'service': 'ssh'},
|
||||
{'port': 80, 'service': 'http'},
|
||||
{'port': 3306, 'service': 'mysql'}
|
||||
]
|
||||
}
|
||||
|
||||
sample_vulns = [
|
||||
{'port': 22, 'name': 'Outdated SSH', 'severity': 'MEDIUM', 'exploitable': False},
|
||||
{'port': 3306, 'name': 'MySQL Exposed', 'severity': 'HIGH', 'exploitable': True}
|
||||
]
|
||||
|
||||
sample_cves = [
|
||||
{'port': 22, 'cve_id': 'CVE-2021-1234', 'cvss_score': 7.5, 'exploitable': False}
|
||||
]
|
||||
|
||||
results = risk_engine.calculate_overall_risk(sample_scan, sample_vulns, sample_cves, [], [])
|
||||
print(json.dumps(results, indent=2))
|
||||
@@ -0,0 +1,221 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
AutoPentestX - Network Scanner Module
|
||||
Handles port scanning, OS detection, and service enumeration using Nmap
|
||||
"""
|
||||
|
||||
import nmap
|
||||
import subprocess
|
||||
import json
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
class Scanner:
|
||||
def __init__(self, target):
|
||||
"""Initialize scanner with target"""
|
||||
self.target = target
|
||||
self.nm = nmap.PortScanner()
|
||||
self.scan_results = {
|
||||
'target': target,
|
||||
'os_detection': 'Unknown',
|
||||
'ports': [],
|
||||
'services': [],
|
||||
'scan_time': None
|
||||
}
|
||||
|
||||
def validate_target(self):
|
||||
"""Validate target IP or domain"""
|
||||
try:
|
||||
# Try to resolve the target
|
||||
import socket
|
||||
socket.gethostbyname(self.target)
|
||||
return True
|
||||
except socket.gaierror:
|
||||
print(f"[✗] Invalid target: {self.target}")
|
||||
return False
|
||||
|
||||
def detect_os(self):
|
||||
"""Detect operating system using Nmap"""
|
||||
print(f"[*] Detecting operating system for {self.target}...")
|
||||
try:
|
||||
# Run OS detection with Nmap (requires root/sudo)
|
||||
self.nm.scan(self.target, arguments='-O -Pn')
|
||||
|
||||
if self.target in self.nm.all_hosts():
|
||||
if 'osmatch' in self.nm[self.target]:
|
||||
os_matches = self.nm[self.target]['osmatch']
|
||||
if os_matches:
|
||||
os_name = os_matches[0]['name']
|
||||
accuracy = os_matches[0]['accuracy']
|
||||
self.scan_results['os_detection'] = f"{os_name} (Accuracy: {accuracy}%)"
|
||||
print(f"[✓] OS Detected: {os_name} ({accuracy}% accuracy)")
|
||||
return self.scan_results['os_detection']
|
||||
|
||||
# Fallback: Try using TTL values
|
||||
result = subprocess.run(['ping', '-c', '1', self.target],
|
||||
capture_output=True, text=True, timeout=5)
|
||||
|
||||
if result.returncode == 0:
|
||||
ttl_match = re.search(r'ttl=(\d+)', result.stdout.lower())
|
||||
if ttl_match:
|
||||
ttl = int(ttl_match.group(1))
|
||||
if ttl <= 64:
|
||||
self.scan_results['os_detection'] = "Linux/Unix (TTL <= 64)"
|
||||
elif ttl <= 128:
|
||||
self.scan_results['os_detection'] = "Windows (TTL <= 128)"
|
||||
else:
|
||||
self.scan_results['os_detection'] = "Unknown (TTL > 128)"
|
||||
|
||||
print(f"[✓] OS Detected (TTL-based): {self.scan_results['os_detection']}")
|
||||
return self.scan_results['os_detection']
|
||||
|
||||
self.scan_results['os_detection'] = "Unknown"
|
||||
print("[!] Could not detect OS reliably")
|
||||
return "Unknown"
|
||||
|
||||
except Exception as e:
|
||||
print(f"[!] OS Detection failed: {e}")
|
||||
self.scan_results['os_detection'] = "Unknown"
|
||||
return "Unknown"
|
||||
|
||||
def scan_all_ports(self):
|
||||
"""Perform comprehensive port scan"""
|
||||
print(f"[*] Scanning all TCP ports on {self.target}...")
|
||||
try:
|
||||
start_time = datetime.now()
|
||||
|
||||
# Scan top 1000 ports first (faster)
|
||||
print("[*] Phase 1: Scanning top 1000 ports...")
|
||||
self.nm.scan(self.target, arguments='-sS -sV -T4 -Pn --top-ports 1000')
|
||||
|
||||
if self.target in self.nm.all_hosts():
|
||||
host = self.nm[self.target]
|
||||
|
||||
# Process TCP ports
|
||||
if 'tcp' in host:
|
||||
for port in host['tcp'].keys():
|
||||
port_info = host['tcp'][port]
|
||||
if port_info['state'] == 'open':
|
||||
port_data = {
|
||||
'port': port,
|
||||
'protocol': 'tcp',
|
||||
'state': port_info['state'],
|
||||
'service': port_info.get('name', 'unknown'),
|
||||
'version': port_info.get('product', '') + ' ' + port_info.get('version', ''),
|
||||
'extrainfo': port_info.get('extrainfo', '')
|
||||
}
|
||||
self.scan_results['ports'].append(port_data)
|
||||
print(f"[✓] Port {port}/tcp open - {port_data['service']} {port_data['version']}")
|
||||
|
||||
# Process UDP ports (limited scan for speed)
|
||||
print("[*] Phase 2: Scanning common UDP ports...")
|
||||
self.nm.scan(self.target, arguments='-sU -Pn --top-ports 20')
|
||||
|
||||
if 'udp' in self.nm[self.target]:
|
||||
for port in self.nm[self.target]['udp'].keys():
|
||||
port_info = self.nm[self.target]['udp'][port]
|
||||
if port_info['state'] in ['open', 'open|filtered']:
|
||||
port_data = {
|
||||
'port': port,
|
||||
'protocol': 'udp',
|
||||
'state': port_info['state'],
|
||||
'service': port_info.get('name', 'unknown'),
|
||||
'version': port_info.get('product', '') + ' ' + port_info.get('version', ''),
|
||||
'extrainfo': port_info.get('extrainfo', '')
|
||||
}
|
||||
self.scan_results['ports'].append(port_data)
|
||||
print(f"[✓] Port {port}/udp {port_info['state']} - {port_data['service']}")
|
||||
|
||||
end_time = datetime.now()
|
||||
scan_duration = (end_time - start_time).total_seconds()
|
||||
self.scan_results['scan_time'] = scan_duration
|
||||
|
||||
print(f"[✓] Port scan completed in {scan_duration:.2f} seconds")
|
||||
print(f"[✓] Total open ports found: {len(self.scan_results['ports'])}")
|
||||
|
||||
return self.scan_results['ports']
|
||||
|
||||
except Exception as e:
|
||||
print(f"[✗] Port scanning failed: {e}")
|
||||
return []
|
||||
|
||||
def enumerate_services(self):
|
||||
"""Extract detailed service information"""
|
||||
print(f"[*] Enumerating services on {self.target}...")
|
||||
|
||||
services = []
|
||||
for port_data in self.scan_results['ports']:
|
||||
service_info = {
|
||||
'port': port_data['port'],
|
||||
'protocol': port_data['protocol'],
|
||||
'service': port_data['service'],
|
||||
'version': port_data['version'].strip(),
|
||||
'banner': port_data.get('extrainfo', ''),
|
||||
'vulnerabilities': []
|
||||
}
|
||||
services.append(service_info)
|
||||
|
||||
self.scan_results['services'] = services
|
||||
print(f"[✓] Enumerated {len(services)} services")
|
||||
return services
|
||||
|
||||
def run_full_scan(self):
|
||||
"""Execute complete scan workflow"""
|
||||
print("\n" + "="*60)
|
||||
print("AutoPentestX - Network Scanner")
|
||||
print("="*60)
|
||||
print(f"Target: {self.target}")
|
||||
print(f"Scan started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print("="*60 + "\n")
|
||||
|
||||
# Validate target
|
||||
if not self.validate_target():
|
||||
return None
|
||||
|
||||
# Step 1: OS Detection
|
||||
self.detect_os()
|
||||
|
||||
# Step 2: Port Scanning
|
||||
self.scan_all_ports()
|
||||
|
||||
# Step 3: Service Enumeration
|
||||
self.enumerate_services()
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("SCAN SUMMARY")
|
||||
print("="*60)
|
||||
print(f"Target: {self.target}")
|
||||
print(f"OS: {self.scan_results['os_detection']}")
|
||||
print(f"Open Ports: {len(self.scan_results['ports'])}")
|
||||
print(f"Services Detected: {len(self.scan_results['services'])}")
|
||||
print("="*60 + "\n")
|
||||
|
||||
return self.scan_results
|
||||
|
||||
def get_results(self):
|
||||
"""Return scan results"""
|
||||
return self.scan_results
|
||||
|
||||
def save_results(self, filename):
|
||||
"""Save scan results to JSON file"""
|
||||
try:
|
||||
with open(filename, 'w') as f:
|
||||
json.dump(self.scan_results, f, indent=4)
|
||||
print(f"[✓] Scan results saved to {filename}")
|
||||
except Exception as e:
|
||||
print(f"[✗] Failed to save results: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python scanner.py <target>")
|
||||
sys.exit(1)
|
||||
|
||||
target = sys.argv[1]
|
||||
scanner = Scanner(target)
|
||||
results = scanner.run_full_scan()
|
||||
|
||||
if results:
|
||||
scanner.save_results(f"scan_{target.replace('.', '_')}.json")
|
||||
@@ -0,0 +1,344 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
AutoPentestX - Vulnerability Scanner Module
|
||||
Integrates Nikto (web scanning) and SQLMap (SQL injection detection)
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import json
|
||||
import re
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
class VulnerabilityScanner:
|
||||
def __init__(self, target, ports_data):
|
||||
"""Initialize vulnerability scanner"""
|
||||
self.target = target
|
||||
self.ports_data = ports_data
|
||||
self.web_ports = []
|
||||
self.vulnerabilities = []
|
||||
self.web_vulns = []
|
||||
self.sql_vulns = []
|
||||
|
||||
# Identify web ports
|
||||
self.identify_web_services()
|
||||
|
||||
def identify_web_services(self):
|
||||
"""Identify HTTP/HTTPS services from port scan"""
|
||||
common_web_ports = [80, 443, 8080, 8443, 8000, 8888, 3000, 5000]
|
||||
web_services = ['http', 'https', 'ssl/http', 'http-proxy', 'http-alt']
|
||||
|
||||
for port in self.ports_data:
|
||||
service = port.get('service', '').lower()
|
||||
port_num = port.get('port')
|
||||
|
||||
# Check if it's a known web service or common web port
|
||||
if any(web_svc in service for web_svc in web_services) or port_num in common_web_ports:
|
||||
protocol = 'https' if port_num == 443 or 'https' in service or 'ssl' in service else 'http'
|
||||
self.web_ports.append({
|
||||
'port': port_num,
|
||||
'protocol': protocol,
|
||||
'url': f"{protocol}://{self.target}:{port_num}"
|
||||
})
|
||||
print(f"[✓] Detected web service: {protocol}://{self.target}:{port_num}")
|
||||
|
||||
def scan_with_nikto(self, url):
|
||||
"""Run Nikto web vulnerability scanner"""
|
||||
print(f"[*] Running Nikto scan on {url}...")
|
||||
|
||||
try:
|
||||
# Check if Nikto is installed
|
||||
nikto_check = subprocess.run(['which', 'nikto'], capture_output=True)
|
||||
if nikto_check.returncode != 0:
|
||||
print("[!] Nikto not installed, skipping web vulnerability scan")
|
||||
return []
|
||||
|
||||
# Run Nikto
|
||||
cmd = [
|
||||
'nikto',
|
||||
'-h', url,
|
||||
'-Format', 'json',
|
||||
'-output', f'logs/nikto_{self.target}_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json',
|
||||
'-Tuning', '123456789', # All tests
|
||||
'-timeout', '10'
|
||||
]
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
|
||||
|
||||
# Parse Nikto output
|
||||
vulns = self.parse_nikto_output(result.stdout)
|
||||
|
||||
print(f"[✓] Nikto scan completed: {len(vulns)} vulnerabilities found")
|
||||
return vulns
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
print("[!] Nikto scan timed out")
|
||||
return []
|
||||
except Exception as e:
|
||||
print(f"[!] Nikto scan failed: {e}")
|
||||
return []
|
||||
|
||||
def parse_nikto_output(self, output):
|
||||
"""Parse Nikto JSON output"""
|
||||
vulnerabilities = []
|
||||
|
||||
try:
|
||||
# Try to parse as JSON
|
||||
if output:
|
||||
# Nikto output may have multiple JSON objects
|
||||
json_objects = re.findall(r'\{.*?\}', output, re.DOTALL)
|
||||
|
||||
for json_str in json_objects:
|
||||
try:
|
||||
data = json.loads(json_str)
|
||||
if 'vulnerabilities' in data:
|
||||
for vuln in data['vulnerabilities']:
|
||||
vulnerabilities.append({
|
||||
'type': 'web',
|
||||
'url': vuln.get('url', ''),
|
||||
'severity': self.map_nikto_severity(vuln.get('OSVDB', '')),
|
||||
'description': vuln.get('msg', 'Unknown vulnerability')
|
||||
})
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f"[!] Error parsing Nikto output: {e}")
|
||||
|
||||
# If JSON parsing fails, try text parsing
|
||||
if not vulnerabilities and output:
|
||||
lines = output.split('\n')
|
||||
for line in lines:
|
||||
if '+ ' in line and any(keyword in line.lower() for keyword in
|
||||
['vulnerable', 'outdated', 'disclosure', 'injection', 'xss', 'security']):
|
||||
vulnerabilities.append({
|
||||
'type': 'web',
|
||||
'url': self.target,
|
||||
'severity': 'MEDIUM',
|
||||
'description': line.strip()
|
||||
})
|
||||
|
||||
return vulnerabilities
|
||||
|
||||
def map_nikto_severity(self, osvdb_id):
|
||||
"""Map OSVDB ID to severity level"""
|
||||
if not osvdb_id:
|
||||
return 'MEDIUM'
|
||||
# Simplified severity mapping
|
||||
return 'MEDIUM'
|
||||
|
||||
def scan_sql_injection(self, url):
|
||||
"""Scan for SQL injection vulnerabilities using SQLMap"""
|
||||
print(f"[*] Scanning for SQL injection on {url}...")
|
||||
|
||||
try:
|
||||
# Check if SQLMap is installed
|
||||
sqlmap_check = subprocess.run(['which', 'sqlmap'], capture_output=True)
|
||||
if sqlmap_check.returncode != 0:
|
||||
print("[!] SQLMap not installed, skipping SQL injection scan")
|
||||
return []
|
||||
|
||||
# Run SQLMap with batch mode and basic options
|
||||
cmd = [
|
||||
'sqlmap',
|
||||
'-u', url,
|
||||
'--batch',
|
||||
'--crawl=2',
|
||||
'--level=1',
|
||||
'--risk=1',
|
||||
'--random-agent',
|
||||
'--timeout=30',
|
||||
'--retries=2',
|
||||
'--threads=3'
|
||||
]
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=180)
|
||||
|
||||
# Parse SQLMap output
|
||||
sql_vulns = self.parse_sqlmap_output(result.stdout, url)
|
||||
|
||||
print(f"[✓] SQL injection scan completed: {len(sql_vulns)} vulnerabilities found")
|
||||
return sql_vulns
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
print("[!] SQLMap scan timed out")
|
||||
return []
|
||||
except Exception as e:
|
||||
print(f"[!] SQLMap scan failed: {e}")
|
||||
return []
|
||||
|
||||
def parse_sqlmap_output(self, output, url):
|
||||
"""Parse SQLMap output for vulnerabilities"""
|
||||
vulnerabilities = []
|
||||
|
||||
try:
|
||||
# Look for SQL injection indicators
|
||||
if 'parameter' in output.lower() and 'injectable' in output.lower():
|
||||
# Extract injectable parameters
|
||||
param_matches = re.findall(r"Parameter: (.*?) \(.*?\) is vulnerable", output, re.IGNORECASE)
|
||||
|
||||
for param in param_matches:
|
||||
vulnerabilities.append({
|
||||
'type': 'sql_injection',
|
||||
'url': url,
|
||||
'parameter': param,
|
||||
'severity': 'HIGH',
|
||||
'description': f"SQL Injection vulnerability found in parameter: {param}"
|
||||
})
|
||||
|
||||
# Check for database information
|
||||
if 'back-end DBMS' in output:
|
||||
db_match = re.search(r'back-end DBMS: (.*?)[\n\r]', output)
|
||||
if db_match and vulnerabilities:
|
||||
vulnerabilities[0]['database'] = db_match.group(1)
|
||||
|
||||
except Exception as e:
|
||||
print(f"[!] Error parsing SQLMap output: {e}")
|
||||
|
||||
return vulnerabilities
|
||||
|
||||
def scan_common_vulnerabilities(self):
|
||||
"""Scan for common vulnerabilities based on service versions"""
|
||||
print("[*] Checking for common vulnerabilities based on service versions...")
|
||||
|
||||
common_vulns = []
|
||||
|
||||
for port in self.ports_data:
|
||||
service = port.get('service', '').lower()
|
||||
version = port.get('version', '').lower()
|
||||
port_num = port.get('port')
|
||||
|
||||
# Check for outdated/vulnerable services
|
||||
vuln_checks = [
|
||||
# SSH vulnerabilities
|
||||
{
|
||||
'service': 'ssh',
|
||||
'versions': ['openssh 5', 'openssh 6', 'openssh 7.0', 'openssh 7.1', 'openssh 7.2'],
|
||||
'vuln_name': 'Outdated SSH Version',
|
||||
'description': 'SSH service running outdated version with known vulnerabilities',
|
||||
'severity': 'MEDIUM'
|
||||
},
|
||||
# FTP vulnerabilities
|
||||
{
|
||||
'service': 'ftp',
|
||||
'versions': ['vsftpd 2.3.4', 'proftpd 1.3.3'],
|
||||
'vuln_name': 'Vulnerable FTP Service',
|
||||
'description': 'FTP service with known backdoor or vulnerabilities',
|
||||
'severity': 'HIGH'
|
||||
},
|
||||
# SMB vulnerabilities
|
||||
{
|
||||
'service': 'microsoft-ds',
|
||||
'versions': ['smb'],
|
||||
'vuln_name': 'SMB Service Exposed',
|
||||
'description': 'SMB service exposed, potential for EternalBlue or similar exploits',
|
||||
'severity': 'HIGH'
|
||||
},
|
||||
# MySQL vulnerabilities
|
||||
{
|
||||
'service': 'mysql',
|
||||
'versions': ['5.0', '5.1', '5.5'],
|
||||
'vuln_name': 'Outdated MySQL Version',
|
||||
'description': 'MySQL running outdated version with known vulnerabilities',
|
||||
'severity': 'MEDIUM'
|
||||
},
|
||||
# Apache vulnerabilities
|
||||
{
|
||||
'service': 'http',
|
||||
'versions': ['apache 2.0', 'apache 2.2'],
|
||||
'vuln_name': 'Outdated Apache Server',
|
||||
'description': 'Apache HTTP server running outdated version',
|
||||
'severity': 'MEDIUM'
|
||||
}
|
||||
]
|
||||
|
||||
# Check each vulnerability pattern
|
||||
for check in vuln_checks:
|
||||
if check['service'] in service:
|
||||
for vuln_version in check['versions']:
|
||||
if vuln_version in version or version == '':
|
||||
common_vulns.append({
|
||||
'port': port_num,
|
||||
'service': service,
|
||||
'name': check['vuln_name'],
|
||||
'description': check['description'],
|
||||
'severity': check['severity'],
|
||||
'version': version,
|
||||
'exploitable': True
|
||||
})
|
||||
print(f"[!] Found: {check['vuln_name']} on port {port_num}")
|
||||
break
|
||||
|
||||
return common_vulns
|
||||
|
||||
def run_full_scan(self):
|
||||
"""Execute complete vulnerability scan"""
|
||||
print("\n" + "="*60)
|
||||
print("AutoPentestX - Vulnerability Scanner")
|
||||
print("="*60)
|
||||
print(f"Target: {self.target}")
|
||||
print(f"Services to scan: {len(self.ports_data)}")
|
||||
print("="*60 + "\n")
|
||||
|
||||
# Scan common vulnerabilities
|
||||
common_vulns = self.scan_common_vulnerabilities()
|
||||
self.vulnerabilities.extend(common_vulns)
|
||||
|
||||
# Scan web services if found
|
||||
if self.web_ports:
|
||||
print(f"\n[*] Found {len(self.web_ports)} web service(s)")
|
||||
|
||||
for web_service in self.web_ports:
|
||||
url = web_service['url']
|
||||
|
||||
# Nikto scan
|
||||
nikto_vulns = self.scan_with_nikto(url)
|
||||
self.web_vulns.extend(nikto_vulns)
|
||||
|
||||
# SQL injection scan
|
||||
sql_vulns = self.scan_sql_injection(url)
|
||||
self.sql_vulns.extend(sql_vulns)
|
||||
|
||||
else:
|
||||
print("[!] No web services detected")
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("VULNERABILITY SCAN SUMMARY")
|
||||
print("="*60)
|
||||
print(f"Common Vulnerabilities: {len(common_vulns)}")
|
||||
print(f"Web Vulnerabilities: {len(self.web_vulns)}")
|
||||
print(f"SQL Injection Points: {len(self.sql_vulns)}")
|
||||
print(f"Total Vulnerabilities: {len(self.vulnerabilities) + len(self.web_vulns) + len(self.sql_vulns)}")
|
||||
print("="*60 + "\n")
|
||||
|
||||
return {
|
||||
'vulnerabilities': self.vulnerabilities,
|
||||
'web_vulnerabilities': self.web_vulns,
|
||||
'sql_vulnerabilities': self.sql_vulns
|
||||
}
|
||||
|
||||
def get_results(self):
|
||||
"""Return vulnerability scan results"""
|
||||
return {
|
||||
'vulnerabilities': self.vulnerabilities,
|
||||
'web_vulnerabilities': self.web_vulns,
|
||||
'sql_vulnerabilities': self.sql_vulns
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python vuln_scanner.py <target>")
|
||||
sys.exit(1)
|
||||
|
||||
# This would normally receive port data from scanner module
|
||||
# For testing, we'll use sample data
|
||||
target = sys.argv[1]
|
||||
sample_ports = [
|
||||
{'port': 80, 'service': 'http', 'version': 'Apache 2.2.8'},
|
||||
{'port': 22, 'service': 'ssh', 'version': 'OpenSSH 7.0'}
|
||||
]
|
||||
|
||||
vuln_scanner = VulnerabilityScanner(target, sample_ports)
|
||||
results = vuln_scanner.run_full_scan()
|
||||
Reference in New Issue
Block a user