Scripting : SSL Vulnerabilities Scanner

This is a simple script that will give you a summary scan from a designated website when the script is run you get asked for the website you wish to scan as below:


Then a vulnerability assessment report card is generated from that data as below:



Remember you need to ensure that you make the script executable with :

chmod +X ssl_debug.py

Script : ssl_debug.py

#!/usr/bin/env python3
import ssl
import socket
import struct
import http.client
from urllib.parse import urlparse
import os
import time
from datetime import datetime
class VulnerabilityScanner:
    def __init__(self):
        self.results = {
            'protocol': {'status': 'green', 'data': {}},
            'certificate': {'status': 'green', 'data': {}},
            'cipher': {'status': 'green', 'data': {}},
            'headers': {'status': 'green', 'data': {}},
            'vulnerabilities': {'status': 'green', 'data': {}}
        }
    def _verify_domain(self, hostname):
        try:
            socket.gethostbyname(hostname)
            return True
        except socket.gaierror:
            raise ValueError(f"Cannot resolve domain: {hostname}. If this is an internal domain, ensure you can reach it.")
    def check_heartbleed(self, hostname):
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5)
            sock.connect((hostname, 443))
            client_hello = (b'\x16\x03\x02\x00\x31\x01\x00\x00\x2d'
                          b'\x03\x02' + os.urandom(32) +
                          b'\x00\x00\x02\x00\x2f\x01\x00')           
            sock.send(client_hello)           
            heartbeat = (b'\x18\x03\x02\x00\x03\x01\x40\x00')
            sock.send(heartbeat)
            response = sock.recv(1024)
            return len(response) > 3
        except:
            return False
        finally:
            sock.close()
    def check_poodle(self, hostname):
        try:
            context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
            context.options &= ~ssl.OP_NO_SSLv3
            with socket.create_connection((hostname, 443), timeout=5) as sock:
                with context.wrap_socket(sock, server_hostname=hostname) as ssock:
                    return True
        except:
            return False
    def check_beast(self, hostname):
        try:
            context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
            context.options &= ~ssl.OP_NO_TLSv1
            with socket.create_connection((hostname, 443), timeout=5) as sock:
                with context.wrap_socket(sock, server_hostname=hostname) as ssock:
                    cipher = ssock.cipher()
                    return 'CBC' in cipher[0]
        except:
            return False
    def check_crime(self, hostname):
        try:
            conn = http.client.HTTPSConnection(hostname, timeout=5)
            conn.request("GET", "/")
            response = conn.getresponse()
            headers = dict(response.etheaders())
            return 'Accept-Encoding' in headers and 'gzip' in headers.get('Accept-Encoding', '')
        except:
            return False
    def check_breach(self, hostname):
        try:
            conn = http.client.HTTPSConnection(hostname, timeout=5)
            conn.request("GET", "/")
            response = conn.getresponse()
            headers = dict(response.getheaders())
            return headers.get('Content-Encoding') == 'gzip' and 'text/html' in headers.get('Content-Type', '')
        except:
            return False
    def _check_headers(self, hostname):
        security_headers = {
            'Strict-Transport-Security': {'required': True, 'weight': 'critical'},
            'X-Content-Type-Options': {'required': True, 'weight': 'high'},
            'X-Frame-Options': {'required': True, 'weight': 'high'},
            'X-XSS-Protection': {'required': False, 'weight': 'medium'},
            'Content-Security-Policy': {'required': True, 'weight': 'critical'},
            'Referrer-Policy': {'required': False, 'weight': 'medium'}
        }
        try:
            conn = http.client.HTTPSConnection(hostname, timeout=5)
            conn.request("HEAD", "/")
            response = conn.getresponse()
            headers = dict(response.getheaders())
            if not headers:
                self.results['headers']['data']['status'] = 'No Headers Present'
                self.results['headers']['status'] = 'red'
                return
            missing_critical = []
            missing_high = []           
            for header, config in security_headers.items():
                if header not in headers and config['required']:
                    if config['weight'] == 'critical':
                        missing_critical.append(header)
                    elif config['weight'] == 'high':
                        missing_high.append(header)
            self.results['headers']['data'].update({
                'missing_critical': missing_critical,
                'missing_high': missing_high,
                'present_headers': list(headers.keys()) if headers else ['None Present']
            })
            if missing_critical:
                self.results['headers']['status'] = 'red'
            elif missing_high:
                self.results['headers']['status'] = 'amber'
        except Exception as e:
            self.results['headers']['data']['status'] = 'No Headers Present'
            self.results['headers']['status'] = 'red'
    def scan(self, url):
        try:
            parsed = urlparse(url if '://' in url else f'https://{url}')
            self.hostname = parsed.netloc or parsed.path           
            if not self._verify_domain(self.hostname):
                return           
            print("[+] Running vulnerability checks...")
            vulnerabilities = {
                'Heartbleed': self.check_heartbleed(self.hostname),
                'POODLE': self.check_poodle(self.hostname),
                'BEAST': self.check_beast(self.hostname),
                'CRIME': self.check_crime(self.hostname),
                'BREACH': self.check_breach(self.hostname)
            }           
            self.results['vulnerabilities']['data'] = vulnerabilities
            if any(vulnerabilities.values()):
                self.results['vulnerabilities']['status'] = 'red'           
            print("[+] Checking SSL/TLS configuration...")
            context = ssl.create_default_context()
            with socket.create_connection((self.hostname, 443), timeout=5) as sock:
                with context.wrap_socket(sock, server_hostname=self.hostname) as ssock:
                    self._check_protocol(ssock)
                    self._check_cert(ssock)
                    self._check_cipher(ssock)                   
            print("[+] Checking HTTP headers...")
            self._check_headers(self.hostname)                   
        except Exception as e:
            self.results['error'] = str(e)
            for key in self.results:
                if isinstance(self.results[key], dict):
                    self.results[key]['status'] = 'red'
    def _check_protocol(self, ssock):
        version = ssock.version()
        self.results['protocol']['data'].update({
            'version': version,
            'supported_versions': self._get_supported_versions(),
            'compression': ssock.compression()
        })       
        if version in ['SSLv2', 'SSLv3', 'TLSv1', 'TLSv1.1']:
            self.results['protocol']['status'] = 'red'
        elif version == 'TLSv1.2':
            self.results['protocol']['status'] = 'amber'
    def _check_cipher(self, ssock):
        cipher = ssock.cipher()
        self.results['cipher']['data'].update({
            'suite': cipher[0],
            'protocol': cipher[1],
            'bits': cipher[2],
            'forward_secrecy': 'DHE' in cipher[0] or 'ECDHE' in cipher[0]
        })
    def _check_cert(self, ssock):
        cert = ssock.getpeercert()
        try:
            self.results['certificate']['data'].update({
                'issuer': dict(x[0] for x in cert['issuer']).get('commonName'),
                'subject': dict(x[0] for x in cert['subject']).get('commonName'),
                'expiry': cert['notAfter'],
                'version': cert.get('version', 0),
                'serial_number': cert.get('serialNumber', ''),
            })           
            expiry = datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z')
            if expiry < datetime.now():
                self.results['certificate']['status'] = 'red'
            elif (expiry - datetime.now()).days < 30:
                self.results['certificate']['status'] = 'amber'
        except Exception as e:
            self.results['certificate']['data']['error'] = str(e)
            self.results['certificate']['status'] = 'red'
    def _get_supported_versions(self):
        versions = []
        try:
            context = ssl.create_default_context()
            with socket.create_connection((self.hostname, 443), timeout=5) as sock:
                with context.wrap_socket(sock, server_hostname=self.hostname) as ssock:
                    return [ssock.version()]
        except:
            return versions
    def generate_html(self):
        style = """
        * {margin: 0; padding: 0; box-sizing: border-box;}
        body {font-family: Arial, sans-serif; line-height: 1.6; padding: 20px; background: #f0f0f0;}
        .container {max-width: 1200px; margin: 0 auto;}
        .grid {display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 20px; margin-top: 20px;}
        .card {background: white; border-radius: 8px; padding: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);}
        .card-header {display: flex; align-items: center; margin-bottom: 15px; font-size: 18px; font-weight: bold;}
        .status {width: 12px; height: 12px; border-radius: 50%; margin-right: 10px;}
        .green {background: #22c55e;}
        .amber {background: #f59e0b;}
        .red {background: #ef4444;}
        .error {background: #fee2e2; border: 1px solid #ef4444; padding: 15px; border-radius: 8px; color: #b91c1c;}
        .details p {margin-bottom: 8px;}
        .vulnerability-item {display: flex; align-items: center; margin-bottom: 8px;}
        .vulnerability-status {width: 8px; height: 8px; border-radius: 50%; margin-right: 8px;}
        """
        html = f"""
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Vulnerability Analysis Report - {self.hostname}</title>
            <style>{style}</style>
        </head>
        <body>
            <div class="container">
                <h1>Vulnerability Analysis Report - {self.hostname}</h1>
                {'<div class="error">' + self.results["error"] + '</div>' if 'error' in self.results else ''}
                <div class="grid">
        """
        for category, info in self.results.items():
            if isinstance(info, dict):
                html += f"""
                    <div class="card">
                        <div class="card-header">
                            <span class="status {info['status']}"></span>
                            {category.title()}
                        </div>
                        <div class="details">
                """               
                if category == 'vulnerabilities':
                    for vuln, is_vulnerable in info['data'].items():
                        status = 'red' if is_vulnerable else 'green'
                        html += f"""
                        <div class="vulnerability-item">
                            <span class="vulnerability-status {status}"></span>
                            <span>{vuln}: {'Vulnerable' if is_vulnerable else 'Not Vulnerable'}</span>
                        </div>
                        """
                else:
                    for key, value in info['data'].items():
                        if isinstance(value, (list, tuple)):
                            html += f"<p><strong>{key.title()}:</strong><br>{'<br>'.join(str(v) for v in value)}</p>"
                        elif isinstance(value, dict):
                            html += f"<p><strong>{key.title()}:</strong><br>"
                            for k, v in value.items():
                                html += f"{k}: {v}<br>"
                            html += "</p>"
                        else:
                            html += f"<p><strong>{key.title()}:</strong> {value}</p>"
                html += "</div></div>"
        html += """
                </div>
            </div>
        </body>
        </html>
        """
        return html
def main():
    scanner = VulnerabilityScanner()
    url = input("Enter website to scan: ")   
    print("\nStarting vulnerability scan...")
    print("=" * 50)   
    try:
        scanner.scan(url)       
        report_path = os.path.join(os.getcwd(), 'vulnerability_report.html')
        with open(report_path, 'w') as f:
            f.write(scanner.generate_html())       
        print(f"\nReport generated: {report_path}")
    except Exception as e:
        print(f"\nError: {str(e)}")
         print("\nNote: This tool performs passive checks only.")
if __name__ == "__main__":
    main()

Permission Errors?

If you copy this file from another server you will may need to set the permission correctly in order for it to be run, if this fails to run you can use these commands to fix it:

ls -l ssl_debug.py
sudo chown $USER:$USER ssl_debug.py
chmod 755 ssl_debug.py

Previous Post Next Post

نموذج الاتصال