sslscan_audit.py is a Python wrapper that drives sslscan
across large lists of hosts so you can audit TLS configuration in bulk — across
whole subnets, domain lists, or both at once — and get a single consolidated
report.
It runs sslscan --xml=- per (host, port) pair, parses the XML directly (no
scraping of human-readable output), and produces a posture report covering:
- Enabled / disabled protocol versions (SSLv2/v3, TLS 1.0–1.3)
- Every accepted cipher suite with key size, ECDHE curve, bit strength and per-cipher weakness tags (NULL, anon, EXPORT, RC4, DES/3DES, SHA1-MAC, CBC on legacy TLS, no-PFS)
- TLS 1.3 / 1.2 supported key-exchange groups, including post-quantum hybrids
(
X25519MLKEM768,SecP256r1MLKEM768,X25519Kyber768Draft00, …) - Certificate details (subject, SANs, issuer, signature algorithm, validity, PQ signature detection)
- Vulnerability flags: Heartbleed, insecure renegotiation, CRIME (TLS
compression),
TLS_FALLBACK_SCSV - Per-endpoint sslscan strength score and post-quantum handshake readiness
- Python 3.11 or newer
sslscanonPATH(or pass--sslscan-path)dnspython(used to resolve domains and follow CNAME chains)
The script ships with PEP 723 inline
metadata and a pipx run shebang, so the simplest way to run it is:
pipx run ./sslscan_audit.py --domains targets.txtpipx will create an isolated venv with the right Python and dnspython
version. Alternatively, install dnspython yourself and invoke it as a
regular script:
pip install dnspython
python sslscan_audit.py --domains targets.txtAt least one of --cidr or --domains is required.
At least one of --host, --cidr, or --domains is required; they may be combined freely.
| Flag | Description |
|---|---|
--host HOST [HOST ...] |
One or more hostnames or IP addresses to scan directly. Hostnames are resolved via DNS (CNAME chains followed, SNI set); bare IP addresses are scanned without SNI, equivalent to a /32 CIDR entry. |
--cidr CIDR [CIDR ...] |
One or more subnets in CIDR notation (e.g. 10.0.0.0/24 192.168.1.0/28). Every host in each subnet is scanned without SNI. |
--domains FILE |
Path to a file with one domain per line. Blank lines and lines starting with # are ignored. Each domain is resolved via DNS (CNAME chains are followed). |
--ports PORT [PORT ...] |
Ports to probe. Default: 21 25 110 143 389 443 465 587 993 995 8443. Ports with a well-known STARTTLS mapping (see below) automatically receive --starttls-<proto>; all others are treated as implicit TLS. |
The following ports automatically trigger the corresponding --starttls-<proto> sslscan flag.
Any other port (443, 465, 993, …) is treated as direct / implicit TLS.
| Port | Protocol | sslscan flag |
|---|---|---|
| 21 | FTP | --starttls-ftp |
| 25 | SMTP | --starttls-smtp |
| 110 | POP3 | --starttls-pop3 |
| 143 | IMAP | --starttls-imap |
| 389 | LDAP | --starttls-ldap |
| 587 | SMTP submission | --starttls-smtp |
| 3306 | MySQL | --starttls-mysql |
| 5222 | XMPP client | --starttls-xmpp |
| 5432 | PostgreSQL | --starttls-psql |
| Flag | Default | Description |
|---|---|---|
--workers N |
20 |
Maximum parallel sslscan processes. |
--connect-timeout N |
5 |
Seconds passed to sslscan --connect-timeout (TCP connect). |
--socket-timeout N |
5 |
Seconds passed to sslscan --timeout (per-socket I/O). |
--sslscan-path PATH |
auto | Explicit path to the sslscan binary. Auto-detected from PATH, /usr/local/bin, /usr/bin, and (on Windows) Program Files. |
--iana-names |
off | Pass --iana-names to sslscan so cipher suites are reported using RFC names (TLS_AES_128_GCM_SHA256) instead of OpenSSL names (AES128-GCM-SHA256). |
--show-times |
off | Include per-handshake timing data (sslscan --show-times). |
These promote benign-looking configurations into findings so a CI run will
fail (exit code 1) until they are fixed:
| Flag | Description |
|---|---|
--strict-pq |
Treat any endpoint that doesn't negotiate a post-quantum key-exchange group as a finding. Useful for enforcing PQ readiness across a fleet. |
--min-score LABEL |
Treat any endpoint scored below LABEL as a finding. Choices, worst → best: null, anonymous, weak, medium, acceptable, good, strong. |
| Flag | Default | Description |
|---|---|---|
--format FMT [FMT ...] |
md |
One or more output formats. Choices: md, csv, json, html. |
--output STEM |
stdout | Destination. With a single --format it's the filename; with multiple, it's a stem and the format extension is appended (e.g. --output report --format md json → report.md, report.json). |
--verbose, -v |
off | Enable DEBUG-level logging on stderr. |
| Code | Meaning |
|---|---|
0 |
Scan completed, no findings. |
1 |
Scan completed, one or more endpoints were flagged (weak ciphers/protocols, vulnerabilities, or any CI gate hit). |
2 |
Execution error (sslscan missing, fatal exception, etc.). 130 if interrupted with Ctrl-C. |
Scan a single host directly:
sslscan_audit.py --host mail.example.comScan a mix of hostnames and IPs on the command line:
sslscan_audit.py --host mail.example.com smtp.example.com 10.0.0.5Scan a single domain list, write a Markdown report to stdout:
sslscan_audit.py --domains targets.txtScan two subnets on the default ports and save Markdown to a file:
sslscan_audit.py --cidr 10.0.0.0/24 192.168.1.0/28 --output internal-tls.mdCombine domains and a subnet, probe non-default ports, and crank parallelism up:
sslscan_audit.py \
--domains prod-domains.txt \
--cidr 10.20.0.0/22 \
--ports 443 8443 9443 \
--workers 40 \
--output prod-tls.mdEmit Markdown, JSON, and HTML in one run (sharing a stem):
sslscan_audit.py --domains targets.txt --format md json html --output audit-2025
# → audit-2025.md, audit-2025.json, audit-2025.htmlUse IANA cipher names and capture handshake timings:
sslscan_audit.py --domains targets.txt --iana-names --show-times --output report.mdCI gate — fail the build unless every reachable endpoint scores at least
good and negotiates a post-quantum key-exchange group:
sslscan_audit.py \
--cidr 10.0.0.0/16 \
--min-score good \
--strict-pq \
--format json --output ci-report.jsonPoint at a non-default sslscan binary (e.g. one you built from source with
PQ support):
sslscan_audit.py \
--sslscan-path /opt/sslscan-pq/bin/sslscan \
--domains targets.txtDomain-list file format:
# one host per line; comments and blank lines are ignored
example.com
api.example.com
mail.example.com