!sslscan example.com
• !sslscan github.com:443
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 wrappers for blocking socket calls -----
async def _run_blocking(func, *args, **kwargs):
loop = asyncio.get_running_loop()
return await loop.run_in_executor(None, lambda: func(*args, **kwargs))
def _test_connectivity(target, port):
"""Test basic connectivity."""
try:
with socket.create_connection((target, port), timeout=10):
return True
except:
return False
def _get_certificate_info(target, port):
"""Retrieve detailed certificate info."""
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)
subject = cert.get_subject()
issuer = cert.get_issuer()
not_before = cert.get_notBefore().decode('utf-8')
not_after = cert.get_notAfter().decode('utf-8')
sig_alg = cert.get_signature_algorithm().decode('utf-8')
not_after_dt = datetime.datetime.strptime(not_after, '%Y%m%d%H%M%SZ')
days_remaining = (not_after_dt - datetime.datetime.utcnow()).days
# Extensions summary
extensions = []
for i in range(cert.get_extension_count()):
ext = cert.get_extension(i)
extensions.append({
'name': ext.get_short_name().decode('utf-8'),
'value': str(ext)
})
return {
'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': not_before,
'not_after': not_after,
'signature_algorithm': sig_alg,
'days_until_expiry': days_remaining,
'extensions': extensions
}
return None
def _test_protocols(target, port):
"""Test support for various SSL/TLS protocols."""
protocols = {}
for proto_name in ['SSLv2', 'SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3']:
if proto_name not in TLS_VERSIONS:
protocols[proto_name] = False
continue
try:
ctx = ssl.SSLContext(TLS_VERSIONS[proto_name])
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
with socket.create_connection((target, port), timeout=5) as sock:
with ctx.wrap_socket(sock, server_hostname=target):
protocols[proto_name] = True
except:
protocols[proto_name] = False
return protocols
def _test_cipher_suites(target, port):
"""Return list of supported cipher suite names."""
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'
]
supported = []
for cipher in test_ciphers:
try:
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
ctx.set_ciphers(cipher)
with socket.create_connection((target, port), timeout=5) as sock:
with ctx.wrap_socket(sock, server_hostname=target):
supported.append(cipher)
except:
pass
return supported
# ----- analysis helpers (same logic as original) -----
def _check_vulnerabilities(protocols, cert_info, supported_ciphers):
vulns = []
if protocols.get('SSLv2'):
vulns.append({
'name': 'SSLv2 Support',
'severity': 'CRITICAL',
'description': 'SSLv2 is obsolete and contains critical vulnerabilities',
'cve': 'Multiple CVEs'
})
if protocols.get('SSLv3'):
vulns.append({
'name': 'SSLv3 Support',
'severity': 'HIGH',
'description': 'SSLv3 is vulnerable to POODLE attack',
'cve': 'CVE-2014-3566'
})
if cert_info and cert_info.get('days_until_expiry', 0) < 30:
vulns.append({
'name': 'Certificate Expiring Soon',
'severity': 'MEDIUM',
'description': f"Certificate expires in {cert_info['days_until_expiry']} days",
'cve': 'N/A'
})
weak_ciphers = [c for c in supported_ciphers
if any(weak in c.upper() for weak in CIPHER_CATEGORIES['WEAK'])]
if weak_ciphers:
vulns.append({
'name': 'Weak Cipher Suites',
'severity': 'HIGH',
'description': f'Weak ciphers supported: {", ".join(weak_ciphers[:3])}',
'cve': 'Multiple CVEs'
})
if not protocols.get('TLSv1.2', False):
vulns.append({
'name': 'TLS 1.2 Not Supported',
'severity': 'HIGH',
'description': 'TLS 1.2 is required for modern security',
'cve': 'N/A'
})
if not protocols.get('TLSv1.3', False):
vulns.append({
'name': 'TLS 1.3 Not Supported',
'severity': 'MEDIUM',
'description': 'TLS 1.3 provides improved security and performance',
'cve': 'N/A'
})
return vulns
def _calculate_score(protocols, cert_info, supported_ciphers, vulnerabilities):
score = 100
if protocols.get('SSLv2'): score -= 30
if protocols.get('SSLv3'): score -= 20
if not protocols.get('TLSv1.2'): score -= 15
if not protocols.get('TLSv1.3'): score -= 10
if cert_info and cert_info.get('days_until_expiry', 0) < 30: score -= 10
if cert_info and cert_info.get('days_until_expiry', 0) < 7: score -= 20
weak_cipher_count = sum(1 for c in supported_ciphers
if any(w in c.upper() for w in CIPHER_CATEGORIES['WEAK']))
score -= min(weak_cipher_count * 5, 25)
for vuln in 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
return max(0, score)
def _generate_recommendations(protocols, cert_info, supported_ciphers, score):
recs = []
if protocols.get('SSLv2'): recs.append("🔴 IMMEDIATELY disable SSLv2 - critically vulnerable")
if protocols.get('SSLv3'): recs.append("🔴 Disable SSLv3 - vulnerable to POODLE attack")
if not protocols.get('TLSv1.3'): recs.append("🟡 Enable TLSv1.3 for best security and performance")
if cert_info and cert_info.get('days_until_expiry', 0) < 30:
recs.append("🟡 Renew SSL certificate - expiring soon")
weak_ciphers = [c for c in supported_ciphers
if any(w in c.upper() for w in CIPHER_CATEGORIES['WEAK'])]
if weak_ciphers:
recs.append("🔴 Remove weak cipher suites (RC4, DES, 3DES, NULL)")
if score < 80:
recs.append("🛡️ Implement modern TLS configuration following Mozilla guidelines")
if not any('ECDHE' in c for c in supported_ciphers):
recs.append("🟡 Enable Forward Secrecy with ECDHE cipher suites")
recs.append("ℹ️ Note: SSLv2/SSLv3 testing limited by Python security features")
return recs
def _format_cert_date(date_str):
try:
dt = datetime.datetime.strptime(date_str, '%Y%m%d%H%M%SZ')
return dt.strftime('%Y-%m-%d %H:%M:%S UTC')
except:
return date_str
# ----- main scan orchestration -----
async def perform_ssl_scan(room, bot, target, port):
safe_target = html_escape(target)
await bot.api.send_text_message(room.room_id, f"🔍 Starting comprehensive SSL/TLS scan for {safe_target}:{port}...")
if not await _run_blocking(_test_connectivity, target, port):
await bot.api.send_text_message(room.room_id, f"❌ Cannot connect to {safe_target}:{port}")
return
# Run blocking checks in parallel
cert_task = _run_blocking(_get_certificate_info, target, port)
proto_task = _run_blocking(_test_protocols, target, port)
cipher_task = _run_blocking(_test_cipher_suites, target, port)
cert_info, protocols, supported_ciphers = await asyncio.gather(cert_task, proto_task, cipher_task)
vulnerabilities = _check_vulnerabilities(protocols, cert_info, supported_ciphers)
score = _calculate_score(protocols, cert_info, supported_ciphers, vulnerabilities)
recommendations = _generate_recommendations(protocols, cert_info, supported_ciphers, score)
# Build output (using safe domain/port)
output = await _format_results(target, port, cert_info, protocols, supported_ciphers,
vulnerabilities, score, recommendations)
await bot.api.send_markdown_message(room.room_id, output)
logging.info(f"Completed SSL scan for {target}:{port}")
async def _format_results(target, port, cert_info, protocols, supported_ciphers,
vulnerabilities, score, recommendations):
safe_target = html_escape(target)
score_emoji = "🟢" if score >= 90 else "🟡" if score >= 80 else "🟠" if score >= 60 else "🔴"
rating = "Excellent" if score >= 90 else "Good" if score >= 80 else "Fair" if score >= 60 else "Poor"
body = f"🔐 SSL/TLS Security Scan: {safe_target}:{port}!sslscan <domain[:port]> – Tests protocols, cipher suites, certificate validity, vulnerabilities.
Provides a security score (0-100) and actionable recommendations.