#!/usr/bin/env python3 import socket import ssl from datetime import timezone from cryptography import x509 from cryptography.hazmat.backends import default_backend from modules.export import export from modules.write_log import log_writer R = "\033[31m" # red G = "\033[32m" # green C = "\033[36m" # cyan W = "\033[0m" # white Y = "\033[33m" # yellow HEADER = "\033[1;35m" # bold magenta def cert(hostname, sslp, output, data): result = {} presence = False print(f"\n{HEADER}━━━ TLS/SSL {'━' * 30}{W}\n") port_test = socket.socket(socket.AF_INET, socket.SOCK_STREAM) port_test.settimeout(5) try: port_test.connect((hostname, sslp)) port_test.close() presence = True except Exception: port_test.close() print(f"{R}[-]{W} SSL is not Present on Target URL...Skipping...") result.update({"Error": "SSL is not Present on Target URL"}) log_writer("[sslinfo] SSL is not Present on Target URL...Skipping...") def unpack(nested_tuple, pair): for item in nested_tuple: if isinstance(item, tuple): if len(item) == 2: pair[item[0]] = item[1] else: unpack(item, pair) else: pair[nested_tuple.index(item)] = item def process_cert(info): pair = {} for key, val in info.items(): if isinstance(val, tuple): print(f"{G}{key}{W}") unpack(val, pair) for sub_key, sub_val in pair.items(): print(f" └╴{sub_key} : {sub_val}") result.update({f"{key}-{sub_key}": sub_val}) pair.clear() print() elif isinstance(val, dict): print(f"{G}{key}{W}") for sub_key, sub_val in val.items(): print(f" └╴{sub_key} : {sub_val}") result.update({f"{key}-{sub_key}": sub_val}) print() elif isinstance(val, list): print(f"{G}{key}{W}") for sub_val in val: print(f" └╴{val.index(sub_val)} : {sub_val}") result.update({f"{key}-{val.index(sub_val)}": sub_val}) print() else: print(f"{G}{key}{W} : {val}") result.update({key: val}) print() if presence: ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE sock = socket.socket() sock.settimeout(5) ssl_conn = ctx.wrap_socket(sock, server_hostname=hostname) ssl_conn.connect((hostname, sslp)) x509_cert = ssl_conn.getpeercert(binary_form=True) decoded_cert = x509.load_der_x509_certificate(x509_cert, default_backend()) subject_dict = {} issuer_dict = {} def name_to_dict(attribute): attr_name = attribute.oid._name attr_value = attribute.value return attr_name, attr_value for attribute in decoded_cert.subject: name, value = name_to_dict(attribute) subject_dict[name] = value for attribute in decoded_cert.issuer: name, value = name_to_dict(attribute) issuer_dict[name] = value # Handle `not_valid_before` and `not_valid_after` with compatibility if hasattr(decoded_cert, "not_valid_before_utc") and hasattr( decoded_cert, "not_valid_after_utc" ): not_before = decoded_cert.not_valid_before_utc not_after = decoded_cert.not_valid_after_utc else: # Fallback for older versions not_before = decoded_cert.not_valid_before.replace(tzinfo=timezone.utc) not_after = decoded_cert.not_valid_after.replace(tzinfo=timezone.utc) cert_dict = { "protocol": ssl_conn.version(), "cipher": ssl_conn.cipher(), "subject": subject_dict, "issuer": issuer_dict, "version": decoded_cert.version, "serialNumber": decoded_cert.serial_number, "notBefore": not_before.strftime("%b %d %H:%M:%S %Y GMT"), "notAfter": not_after.strftime("%b %d %H:%M:%S %Y GMT"), } extensions = decoded_cert.extensions for ext in extensions: if ext.oid != x509.ExtensionOID.SUBJECT_ALTERNATIVE_NAME: continue san_entries = ext.value subject_alt_names = [] for entry in san_entries: if isinstance(entry, x509.DNSName): subject_alt_names.append(entry.value) cert_dict["subjectAltName"] = subject_alt_names process_cert(cert_dict) result.update({"exported": False}) if output: fname = f"{output['directory']}/ssl.{output['format']}" output["file"] = fname data["module-SSL Certificate Information"] = result export(output, data) log_writer("[sslinfo] Completed")