From 9da1009c0ea66010651f37815fcc85ae46bda279 Mon Sep 17 00:00:00 2001 From: Hash Borgir Date: Thu, 16 Oct 2025 16:33:23 -0500 Subject: [PATCH] Headers plugin added --- README.md | 37 ++++ funguy.py | 2 +- plugins/headers.py | 387 ++++++++++++++++++++++++++++++++++++++++++ plugins/help.py | 39 +++++ plugins/loadplugin.py | 3 +- 5 files changed, 466 insertions(+), 2 deletions(-) create mode 100644 plugins/headers.py diff --git a/README.md b/README.md index 73e1307..75a9f71 100644 --- a/README.md +++ b/README.md @@ -233,6 +233,43 @@ A security plugin that searches Exploit-DB for vulnerabilities and exploits dire - Automatically falls back to search links if CSV database is unavailable +### 🔒 HTTP Security Headers Analysis + +**🛡️ !headers [url]** +Comprehensive HTTP security header analysis with security scoring and recommendations. + +**Features:** +- **Security Scoring**: 0-100 rating based on headers configuration +- **Header Validation**: Checks presence and proper configuration of critical security headers +- **Redirect Analysis**: Follows HTTP to HTTPS redirect chain +- **SSL Certificate**: Basic SSL/TLS certificate information +- **Information Disclosure**: Identifies revealing server headers +- **Actionable Recommendations**: Specific guidance for security improvements + +**Security Headers Analyzed:** +- `Strict-Transport-Security` (HSTS) - HTTP to HTTPS enforcement +- `Content-Security-Policy` (CSP) - XSS and content injection protection +- `X-Frame-Options` - Clickjacking protection +- `X-Content-Type-Options` - MIME type sniffing prevention +- `Referrer-Policy` - Referrer information control +- `Feature-Policy` / `Permissions-Policy` - Browser feature restrictions +- Information disclosure headers (`Server`, `X-Powered-By`) + +**Security Ratings:** +- **🟢 Excellent (80-100)**: Strong security headers configuration +- **🟡 Good (60-79)**: Moderate security, room for improvement +- **🟠 Fair (40-59)**: Basic security, significant improvements needed +- **🔴 Poor (0-39)**: Weak security headers configuration + +**Examples:** +```bash +!headers example.com +!headers https://github.com +!headers localhost:8080 +!headers subdomain.target.com +``` + + ### AI & Generation Commands **🤖 AI Commands (!tech, !music, !eth, etc.)** diff --git a/funguy.py b/funguy.py index ed92296..6aa904f 100755 --- a/funguy.py +++ b/funguy.py @@ -22,7 +22,7 @@ ALLOWED_PLUGINS = { 'ai', 'config', 'cron', 'date', 'fortune', 'help', 'isup', 'karma', 'loadplugin', 'plugins', 'proxy', 'sd_text', 'stable-diffusion', 'xkcd', 'youtube-preview', 'youtube-search', 'weather', 'urbandictionary', - 'bitcoin', 'dns', 'shodan', 'dnsdumpster', 'exploitdb' + 'bitcoin', 'dns', 'shodan', 'dnsdumpster', 'exploitdb', 'headers' } class FunguyBot: diff --git a/plugins/headers.py b/plugins/headers.py new file mode 100644 index 0000000..819fca6 --- /dev/null +++ b/plugins/headers.py @@ -0,0 +1,387 @@ +""" +This plugin provides comprehensive HTTP security header analysis. +""" + +import logging +import requests +import simplematrixbotlib as botlib +from urllib.parse import urlparse +import ssl +import socket + +async def handle_command(room, message, bot, prefix, config): + """ + Function to handle !headers command for HTTP security header analysis. + + 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("headers"): + logging.info("Received !headers command") + + args = match.args() + + if len(args) < 1: + await show_usage(room, bot) + return + + url = args[0].strip() + + # Add protocol if missing + if not url.startswith(('http://', 'https://')): + url = 'https://' + url + + await analyze_headers(room, bot, url) + +async def show_usage(room, bot): + """Display headers command usage.""" + usage = """ +🔒 HTTP Security Headers Analysis + +!headers <url> - Comprehensive HTTP security header analysis + +Examples: +• !headers example.com +• !headers https://github.com +• !headers http://localhost:8080 + +Analyzes: +• Security headers presence and configuration +• SSL/TLS certificate information +• HTTP to HTTPS redirects +• Security scoring and recommendations +""" + await bot.api.send_markdown_message(room.room_id, usage) + +async def analyze_headers(room, bot, url): + """Perform comprehensive HTTP security header analysis.""" + try: + await bot.api.send_text_message(room.room_id, f"🔍 Analyzing security headers for: {url}") + + results = { + 'url': url, + 'http_headers': {}, + 'https_headers': {}, + 'redirect_chain': [], + 'ssl_info': {}, + 'security_score': 0, + 'recommendations': [] + } + + # Test HTTP first (if HTTPS was provided, we'll still check redirects) + parsed = urlparse(url) + http_url = f"http://{parsed.netloc or parsed.path}" + https_url = f"https://{parsed.netloc or parsed.path}" + + # Analyze HTTP response and redirects + await analyze_http_response(results, http_url if not url.startswith('https://') else https_url) + + # Analyze HTTPS response + if url.startswith('https://') or results.get('redirects_to_https'): + await analyze_https_response(results, https_url) + + # Analyze SSL certificate if HTTPS + if url.startswith('https://') or results.get('redirects_to_https'): + await analyze_ssl_certificate(results, parsed.netloc or parsed.path) + + # Calculate security score + await calculate_security_score(results) + + # Generate recommendations + await generate_recommendations(results) + + # Format and send results + output = await format_header_analysis(results) + await bot.api.send_markdown_message(room.room_id, output) + + logging.info(f"Completed header analysis for {url}") + + except Exception as e: + await bot.api.send_text_message(room.room_id, f"Error analyzing headers: {str(e)}") + logging.error(f"Error in analyze_headers: {e}") + +async def analyze_http_response(results, url): + """Analyze HTTP response and redirect chain.""" + try: + session = requests.Session() + session.max_redirects = 5 + + response = session.get(url, timeout=10, allow_redirects=True) + results['final_url'] = response.url + results['status_code'] = response.status_code + results['http_headers'] = dict(response.headers) + + # Check if redirects to HTTPS + results['redirects_to_https'] = response.url.startswith('https://') + + # Store redirect history + results['redirect_chain'] = [{ + 'url': resp.url, + 'status_code': resp.status_code, + 'headers': dict(resp.headers) + } for resp in response.history] + + except requests.exceptions.SSLError: + results['ssl_error'] = True + except requests.exceptions.RequestException as e: + results['http_error'] = str(e) + +async def analyze_https_response(results, url): + """Analyze HTTPS response headers.""" + try: + response = requests.get(url, timeout=10, allow_redirects=False) + results['https_headers'] = dict(response.headers) + results['https_status'] = response.status_code + except requests.exceptions.RequestException as e: + results['https_error'] = str(e) + +async def analyze_ssl_certificate(results, domain): + """Analyze SSL certificate information.""" + try: + context = ssl.create_default_context() + with socket.create_connection((domain, 443), timeout=10) as sock: + with context.wrap_socket(sock, server_hostname=domain) as ssock: + cert = ssock.getpeercert() + + results['ssl_info'] = { + 'subject': dict(x[0] for x in cert['subject']), + 'issuer': dict(x[0] for x in cert['issuer']), + 'not_before': cert['notBefore'], + 'not_after': cert['notAfter'], + 'san': cert.get('subjectAltName', []), + 'version': cert.get('version'), + 'serial_number': cert.get('serialNumber') + } + + except Exception as e: + results['ssl_error'] = str(e) + +async def calculate_security_score(results): + """Calculate overall security score based on headers and configuration.""" + score = 100 + missing_headers = [] + + # Critical security headers + critical_headers = [ + 'Strict-Transport-Security', + 'Content-Security-Policy', + 'X-Content-Type-Options', + 'X-Frame-Options', + 'X-XSS-Protection' + ] + + headers = results.get('https_headers') or results.get('http_headers', {}) + + for header in critical_headers: + if header not in headers: + score -= 15 + missing_headers.append(header) + + # Check HSTS configuration + hsts = headers.get('Strict-Transport-Security', '') + if 'max-age=31536000' not in hsts: + score -= 10 + if 'includeSubDomains' not in hsts: + score -= 5 + if 'preload' not in hsts: + score -= 5 + + # Check CSP configuration + csp = headers.get('Content-Security-Policy', '') + if not csp: + score -= 10 + elif "default-src 'none'" not in csp and "default-src 'self'" not in csp: + score -= 5 + + # Check for insecure headers + insecure_headers = ['Server', 'X-Powered-By', 'X-AspNet-Version'] + for header in insecure_headers: + if header in headers: + score -= 5 + + # Bonus for good practices + if headers.get('Referrer-Policy'): + score += 5 + if headers.get('Feature-Policy') or headers.get('Permissions-Policy'): + score += 5 + if headers.get('X-Content-Type-Options') == 'nosniff': + score += 5 + if headers.get('X-Frame-Options') in ['DENY', 'SAMEORIGIN']: + score += 5 + + # HTTPS enforcement bonus + if results.get('redirects_to_https'): + score += 10 + + results['security_score'] = max(0, score) + results['missing_headers'] = missing_headers + +async def generate_recommendations(results): + """Generate security recommendations based on analysis.""" + recommendations = [] + headers = results.get('https_headers') or results.get('http_headers', {}) + + # HSTS recommendations + if 'Strict-Transport-Security' not in headers: + recommendations.append("🔒 Implement HSTS header with max-age=31536000, includeSubDomains, and preload") + else: + hsts = headers['Strict-Transport-Security'] + if 'max-age=31536000' not in hsts: + recommendations.append("🔒 Increase HSTS max-age to 31536000 (1 year)") + if 'includeSubDomains' not in hsts: + recommendations.append("🔒 Add includeSubDomains to HSTS header") + if 'preload' not in hsts: + recommendations.append("🔒 Consider adding preload directive to HSTS for browser preloading") + + # CSP recommendations + if 'Content-Security-Policy' not in headers: + recommendations.append("🛡️ Implement Content Security Policy to prevent XSS attacks") + else: + csp = headers['Content-Security-Policy'] + if "default-src 'self'" not in csp and "default-src 'none'" not in csp: + recommendations.append("🛡️ Restrict CSP default-src to 'self' or specific origins") + + # Frame options + if 'X-Frame-Options' not in headers: + recommendations.append("🚫 Add X-Frame-Options header to prevent clickjacking (DENY or SAMEORIGIN)") + + # Content type options + if 'X-Content-Type-Options' not in headers: + recommendations.append("📄 Add X-Content-Type-Options: nosniff to prevent MIME type sniffing") + + # Referrer policy + if 'Referrer-Policy' not in headers: + recommendations.append("🔗 Implement Referrer-Policy to control referrer information leakage") + + # Feature policy + if 'Feature-Policy' not in headers and 'Permissions-Policy' not in headers: + recommendations.append("⚙️ Implement Feature-Policy/Permissions-Policy to restrict browser features") + + # Remove server information + if 'Server' in headers or 'X-Powered-By' in headers: + recommendations.append("🕵️ Remove Server and X-Powered-By headers to avoid information disclosure") + + # HTTPS enforcement + if not results.get('redirects_to_https') and not results['url'].startswith('https://'): + recommendations.append("🔐 Implement HTTP to HTTPS redirects") + + results['recommendations'] = recommendations + +async def format_header_analysis(results): + """Format the header analysis results for display.""" + output = f"🔒 Security Headers Analysis: {results['url']}

" + + # Security Score + score = results['security_score'] + score_emoji = "🟢" if score >= 80 else "🟡" if score >= 60 else "🔴" + output += f"{score_emoji} Security Score: {score}/100

" + + # Basic Information + output += "📊 Basic Information
" + output += f" • Final URL: {results.get('final_url', 'N/A')}
" + output += f" • Status Code: {results.get('status_code', 'N/A')}
" + if results.get('redirects_to_https'): + output += f" • HTTPS Redirect: ✅ Enforced
" + else: + output += f" • HTTPS Redirect: ❌ Not enforced
" + output += f" • Redirect Chain: {len(results.get('redirect_chain', []))} hops
" + output += "
" + + # Security Headers Analysis + headers = results.get('https_headers') or results.get('http_headers', {}) + output += "🛡️ Security Headers Analysis
" + + security_headers = { + 'Strict-Transport-Security': ('🔒', 'HSTS - HTTP Strict Transport Security'), + 'Content-Security-Policy': ('🛡️', 'CSP - Content Security Policy'), + 'X-Frame-Options': ('🚫', 'Clickjacking Protection'), + 'X-Content-Type-Options': ('📄', 'MIME Type Sniffing Protection'), + 'X-XSS-Protection': ('❌', 'XSS Protection (Deprecated)'), + 'Referrer-Policy': ('🔗', 'Referrer Policy'), + 'Feature-Policy': ('⚙️', 'Feature Policy'), + 'Permissions-Policy': ('🔧', 'Permissions Policy'), + } + + for header, (emoji, description) in security_headers.items(): + if header in headers: + value = headers[header] + if len(value) > 100: + value = value[:100] + "..." + output += f" • {emoji} {header}: ✅ {value}
" + else: + output += f" • {emoji} {header}: ❌ Missing
" + + output += "
" + + # Other Headers (Information Disclosure) + output += "📋 Other Headers
" + info_headers = ['Server', 'X-Powered-By', 'X-AspNet-Version'] + for header in info_headers: + if header in headers: + output += f" • 🔍 {header}: {headers[header]}
" + + output += "
" + + # SSL Certificate Information (if available) + if results.get('ssl_info'): + output += "🔐 SSL Certificate
" + ssl_info = results['ssl_info'] + if ssl_info.get('subject'): + output += f" • Subject: {ssl_info['subject'].get('commonName', 'N/A')}
" + if ssl_info.get('issuer'): + output += f" • Issuer: {ssl_info['issuer'].get('organizationName', 'N/A')}
" + if ssl_info.get('not_after'): + output += f" • Expires: {ssl_info['not_after']}
" + if ssl_info.get('san'): + san_count = len([san for san in ssl_info['san'] if san[0] == 'DNS']) + output += f" • SAN Entries: {san_count}
" + output += "
" + + # Recommendations + if results.get('recommendations'): + output += "💡 Security Recommendations
" + for rec in results['recommendations'][:8]: # Show first 8 recommendations + output += f" • {rec}
" + + if len(results['recommendations']) > 8: + output += f" • ... and {len(results['recommendations']) - 8} more recommendations
" + output += "
" + + # Missing Headers Summary + if results.get('missing_headers'): + output += "⚠️ Critical Headers Missing
" + for header in results['missing_headers']: + output += f" • ❌ {header}
" + output += "
" + + # Security Rating + score = results['security_score'] + if score >= 80: + rating = "🟢 Excellent" + description = "Strong security headers configuration" + elif score >= 60: + rating = "🟡 Good" + description = "Moderate security, room for improvement" + elif score >= 40: + rating = "🟠 Fair" + description = "Basic security, significant improvements needed" + else: + rating = "🔴 Poor" + description = "Weak security headers configuration" + + output += f"📈 Security Rating: {rating}
" + output += f"📝 Assessment: {description}
" + + # Wrap in collapsible if content is large + if len(output) > 1000: + output = f"
🔒 Security Headers Analysis: {results['url']}{output}
" + + return output diff --git a/plugins/help.py b/plugins/help.py index 7b3fad9..3cc164c 100644 --- a/plugins/help.py +++ b/plugins/help.py @@ -164,6 +164,45 @@ Search Exploit-DB for security vulnerabilities and exploits. Returns detailed in ⚠️ Use responsibly and only on systems you have permission to test. +
🛡️ !headers <url> +

Comprehensive HTTP security header analysis with security scoring and recommendations.

+

Features:

+ +

Security Headers Analyzed:

+ +

Security Ratings:

+ +

Examples:

+ +

Provides enterprise-grade security analysis for penetration testers and developers

+
+ +
📸 !sd [prompt]

Generates images using self-hosted Stable Diffusion. Supports options: --steps, --cfg, --h, --w, --neg, --sampler. Uses queuing system to handle multiple requests. See available options using just '!sd'.

diff --git a/plugins/loadplugin.py b/plugins/loadplugin.py index 71f6a20..82f7e32 100644 --- a/plugins/loadplugin.py +++ b/plugins/loadplugin.py @@ -62,7 +62,8 @@ async def load_plugin(plugin_name): 'dns':'plugins.dns', 'shodan':'plugins.shodan', 'dnsdumpster': 'plugins.dnsdumpster', - 'exploitdb': 'plugins.exploitdb' + 'exploitdb': 'plugins.exploitdb', + 'headers': 'plugins.headers' } # Get the module path from the mapping