!sslscan example.com
• !sslscan github.com:443
• !sslscan localhost:8443
Tests Performed:
• SSL/TLS protocol support and versions
• Certificate chain validation and expiration
• Cipher suite strength and configuration
• Security headers and extensions
• Handshake simulation and vulnerabilities
• PCI DSS compliance checking
• SSL/TLS best practices assessment
"""
await bot.api.send_markdown_message(room.room_id, usage)
async def perform_ssl_scan(room, bot, target, port):
"""Perform comprehensive SSL/TLS security scan."""
try:
await bot.api.send_text_message(room.room_id, f"🔍 Starting comprehensive SSL/TLS scan for {target}:{port}...")
scan_results = {
'target': target,
'port': port,
'certificate': {},
'protocols': {},
'ciphers': {},
'vulnerabilities': [],
'recommendations': [],
'security_score': 0
}
# Test basic connectivity
if not await test_connectivity(target, port):
await bot.api.send_text_message(room.room_id, f"❌ Cannot connect to {target}:{port}")
return
# Perform comprehensive tests
await get_certificate_info(scan_results, target, port)
await test_protocol_support(scan_results, target, port)
await test_cipher_suites(scan_results, target, port)
await check_vulnerabilities(scan_results)
await calculate_security_score(scan_results)
await generate_recommendations(scan_results)
# Format and send results
output = await format_ssl_scan_results(scan_results)
await bot.api.send_markdown_message(room.room_id, output)
logging.info(f"Completed SSL scan for {target}:{port}")
except Exception as e:
await bot.api.send_text_message(room.room_id, f"Error performing SSL scan: {str(e)}")
logging.error(f"Error in perform_ssl_scan: {e}")
async def test_connectivity(target, port):
"""Test basic connectivity to the target."""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(10)
result = sock.connect_ex((target, port))
sock.close()
return result == 0
except:
return False
async def get_certificate_info(scan_results, target, port):
"""Get comprehensive certificate information."""
try:
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
with socket.create_connection((target, port), timeout=10) as sock:
with context.wrap_socket(sock, server_hostname=target) as ssock:
cert_bin = ssock.getpeercert(binary_form=True)
cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_ASN1, cert_bin)
# Basic certificate info
subject = cert.get_subject()
issuer = cert.get_issuer()
scan_results['certificate'] = {
'subject': {
'common_name': subject.CN,
'organization': subject.O,
'organizational_unit': subject.OU,
'country': subject.C,
'state': subject.ST,
'locality': subject.L
},
'issuer': {
'common_name': issuer.CN,
'organization': issuer.O,
'organizational_unit': issuer.OU
},
'serial_number': cert.get_serial_number(),
'version': cert.get_version(),
'not_before': cert.get_notBefore().decode('utf-8'),
'not_after': cert.get_notAfter().decode('utf-8'),
'signature_algorithm': cert.get_signature_algorithm().decode('utf-8'),
'extensions': []
}
# Parse extensions
for i in range(cert.get_extension_count()):
ext = cert.get_extension(i)
scan_results['certificate']['extensions'].append({
'name': ext.get_short_name().decode('utf-8'),
'value': str(ext)
})
# Calculate days until expiration
not_after = datetime.datetime.strptime(scan_results['certificate']['not_after'], '%Y%m%d%H%M%SZ')
days_until_expiry = (not_after - datetime.datetime.utcnow()).days
scan_results['certificate']['days_until_expiry'] = days_until_expiry
except Exception as e:
scan_results['certificate_error'] = str(e)
async def test_protocol_support(scan_results, target, port):
"""Test support for various SSL/TLS protocols."""
protocols = {
'SSLv2': False,
'SSLv3': False,
'TLSv1': False,
'TLSv1.1': False,
'TLSv1.2': False,
'TLSv1.3': False
}
# Test available protocols
for protocol_name in protocols.keys():
try:
if protocol_name in TLS_VERSIONS:
context = ssl.SSLContext(TLS_VERSIONS[protocol_name])
else:
# For protocols not available in this Python version, assume False
protocols[protocol_name] = False
continue
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
with socket.create_connection((target, port), timeout=5) as sock:
with context.wrap_socket(sock, server_hostname=target) as ssock:
protocols[protocol_name] = True
# Get negotiated protocol
if hasattr(ssock, 'version'):
scan_results['negotiated_protocol'] = ssock.version()
except:
protocols[protocol_name] = False
scan_results['protocols'] = protocols
async def test_cipher_suites(scan_results, target, port):
"""Test supported cipher suites."""
try:
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
# Get default cipher suites
context.set_ciphers('ALL:COMPLEMENTOFALL')
with socket.create_connection((target, port), timeout=10) as sock:
with context.wrap_socket(sock, server_hostname=target) as ssock:
cipher = ssock.cipher()
scan_results['ciphers'] = {
'negotiated_cipher': cipher[0] if cipher else 'Unknown',
'supported_ciphers': await get_supported_ciphers(target, port),
'weak_ciphers': [],
'strong_ciphers': []
}
except Exception as e:
scan_results['cipher_error'] = str(e)
async def get_supported_ciphers(target, port):
"""Get list of supported cipher suites."""
supported_ciphers = []
# Test common cipher suites
test_ciphers = [
'ECDHE-RSA-AES256-GCM-SHA384',
'ECDHE-ECDSA-AES256-GCM-SHA384',
'ECDHE-RSA-AES256-SHA384',
'ECDHE-ECDSA-AES256-SHA384',
'ECDHE-RSA-AES256-SHA',
'ECDHE-ECDSA-AES256-SHA',
'AES256-GCM-SHA384',
'AES256-SHA256',
'AES256-SHA',
'CAMELLIA256-SHA',
'PSK-AES256-CBC-SHA',
'ECDHE-RSA-AES128-GCM-SHA256',
'ECDHE-ECDSA-AES128-GCM-SHA256',
'ECDHE-RSA-AES128-SHA256',
'ECDHE-ECDSA-AES128-SHA256',
'ECDHE-RSA-AES128-SHA',
'ECDHE-ECDSA-AES128-SHA',
'AES128-GCM-SHA256',
'AES128-SHA256',
'AES128-SHA',
'CAMELLIA128-SHA',
'PSK-AES128-CBC-SHA',
'DES-CBC3-SHA',
'RC4-SHA',
'RC4-MD5'
]
for cipher in test_ciphers:
try:
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
context.set_ciphers(cipher)
with socket.create_connection((target, port), timeout=5) as sock:
with context.wrap_socket(sock, server_hostname=target) as ssock:
if ssock.cipher():
supported_ciphers.append(cipher)
except:
pass
return supported_ciphers
async def check_vulnerabilities(scan_results):
"""Check for common SSL/TLS vulnerabilities."""
vulnerabilities = []
# Check for weak protocols
if scan_results['protocols'].get('SSLv2', False):
vulnerabilities.append({
'name': 'SSLv2 Support',
'severity': 'CRITICAL',
'description': 'SSLv2 is obsolete and contains critical vulnerabilities',
'cve': 'Multiple CVEs'
})
if scan_results['protocols'].get('SSLv3', False):
vulnerabilities.append({
'name': 'SSLv3 Support',
'severity': 'HIGH',
'description': 'SSLv3 is vulnerable to POODLE attack',
'cve': 'CVE-2014-3566'
})
# Check certificate expiration
cert = scan_results.get('certificate', {})
if cert.get('days_until_expiry', 0) < 30:
vulnerabilities.append({
'name': 'Certificate Expiring Soon',
'severity': 'MEDIUM',
'description': f"Certificate expires in {cert['days_until_expiry']} days",
'cve': 'N/A'
})
# Check for weak ciphers
supported_ciphers = scan_results.get('ciphers', {}).get('supported_ciphers', [])
weak_ciphers_found = []
for cipher in supported_ciphers:
if any(weak in cipher.upper() for weak in CIPHER_CATEGORIES['WEAK']):
weak_ciphers_found.append(cipher)
if weak_ciphers_found:
vulnerabilities.append({
'name': 'Weak Cipher Suites',
'severity': 'HIGH',
'description': f'Weak ciphers supported: {", ".join(weak_ciphers_found[:3])}',
'cve': 'Multiple CVEs'
})
# Check for missing modern protocols
if not scan_results['protocols'].get('TLSv1.2', False):
vulnerabilities.append({
'name': 'TLS 1.2 Not Supported',
'severity': 'HIGH',
'description': 'TLS 1.2 is required for modern security',
'cve': 'N/A'
})
if not scan_results['protocols'].get('TLSv1.3', False):
vulnerabilities.append({
'name': 'TLS 1.3 Not Supported',
'severity': 'MEDIUM',
'description': 'TLS 1.3 provides improved security and performance',
'cve': 'N/A'
})
scan_results['vulnerabilities'] = vulnerabilities
async def calculate_security_score(scan_results):
"""Calculate overall security score."""
score = 100
# Protocol penalties
if scan_results['protocols'].get('SSLv2', False):
score -= 30
if scan_results['protocols'].get('SSLv3', False):
score -= 20
if not scan_results['protocols'].get('TLSv1.2', False):
score -= 15
if not scan_results['protocols'].get('TLSv1.3', False):
score -= 10
# Certificate penalties
cert = scan_results.get('certificate', {})
if cert.get('days_until_expiry', 0) < 30:
score -= 10
if cert.get('days_until_expiry', 0) < 7:
score -= 20
# Cipher penalties
supported_ciphers = scan_results.get('ciphers', {}).get('supported_ciphers', [])
weak_cipher_count = sum(1 for cipher in supported_ciphers
if any(weak in cipher.upper() for weak in CIPHER_CATEGORIES['WEAK']))
score -= min(weak_cipher_count * 5, 25)
# Vulnerability penalties
for vuln in scan_results.get('vulnerabilities', []):
if vuln['severity'] == 'CRITICAL':
score -= 20
elif vuln['severity'] == 'HIGH':
score -= 15
elif vuln['severity'] == 'MEDIUM':
score -= 10
elif vuln['severity'] == 'LOW':
score -= 5
scan_results['security_score'] = max(0, score)
async def generate_recommendations(scan_results):
"""Generate security recommendations."""
recommendations = []
# Protocol recommendations
if scan_results['protocols'].get('SSLv2', False):
recommendations.append("🔴 IMMEDIATELY disable SSLv2 - critically vulnerable")
if scan_results['protocols'].get('SSLv3', False):
recommendations.append("🔴 Disable SSLv3 - vulnerable to POODLE attack")
if not scan_results['protocols'].get('TLSv1.3', False):
recommendations.append("🟡 Enable TLSv1.3 for best security and performance")
# Certificate recommendations
cert = scan_results.get('certificate', {})
if cert.get('days_until_expiry', 0) < 30:
recommendations.append("🟡 Renew SSL certificate - expiring soon")
# Cipher recommendations
supported_ciphers = scan_results.get('ciphers', {}).get('supported_ciphers', [])
weak_ciphers = [c for c in supported_ciphers
if any(weak in c.upper() for weak in CIPHER_CATEGORIES['WEAK'])]
if weak_ciphers:
recommendations.append("🔴 Remove weak cipher suites (RC4, DES, 3DES, NULL)")
# General recommendations
if scan_results['security_score'] < 80:
recommendations.append("🛡️ Implement modern TLS configuration following Mozilla guidelines")
if not any('ECDHE' in c for c in supported_ciphers):
recommendations.append("🟡 Enable Forward Secrecy with ECDHE cipher suites")
# Add note about Python version limitations
recommendations.append("ℹ️ Note: SSLv2/SSLv3 testing limited by Python security features")
scan_results['recommendations'] = recommendations
async def format_ssl_scan_results(scan_results):
"""Format comprehensive SSL scan results."""
output = f"🔐 SSL/TLS Security Scan: {scan_results['target']}:{scan_results['port']}!sslscan <domain[:port]> – Tests protocols, cipher suites, certificate validity, vulnerabilities.
Provides a security score (0-100) and actionable recommendations.