sslscan
sslscan connects to a TLS service and prints exactly what it will negotiate: which protocol versions are enabled, every cipher suite it accepts, the certificate it presents, and quick checks for Heartbleed, TLS compression, and insecure renegotiation, all in a couple of seconds. It answers the first question about any encrypted port: “what does this actually speak?” Reach for it to baseline a service, or to prove a hardening change (TLS 1.0 gone, RC4 removed) really took effect instead of assuming the config applied.
Mental model: sslscan [options] host:port. No port means 443.
New to TLS? Core concepts
- Protocol version: SSLv2/SSLv3 (obsolete), TLS 1.0/1.1 (legacy), TLS 1.2/1.3 (current); each is enabled or disabled independently
- Cipher suite: the negotiated bundle of key exchange, authentication, bulk cipher, and MAC, e.g.
ECDHE-RSA-AES256-GCM-SHA384 - Preferred vs Accepted:
Preferredis the server’s first pick,Acceptedis every other suite it will also negotiate - Certificate: the identity presented, its subject, issuer, validity dates, key strength, signature algorithm, and SANs (
Altnames) - SNI (Server Name Indication): tells a shared IP which vhost’s cert to hand back (
--sni-name=) - STARTTLS: a protocol (SMTP, IMAP, POP3, LDAP, FTP) that opens in cleartext, then upgrades to TLS on request
- Built-in checks: Heartbleed, TLS compression (CRIME), and insecure renegotiation, run inline per enabled protocol
When to reach for sslscan (and when not)
Reach for it to baseline what an encrypted port actually speaks, enumerate enabled protocols and accepted cipher suites in seconds, prove a hardening change landed (TLS 1.0 gone, RC4/3DES removed), read a certificate’s dates and SANs, or run the quick Heartbleed/CRIME/renegotiation checks on one service.
Reach for something else when you need to discover which ports even speak TLS (nmap -sV or its ssl-enum-ciphers script), inspect or debug a single handshake by hand (openssl s_client), get a scored policy-aware verdict per check (testssl.sh), or structured JSON across many hosts in a pipeline (sslyze).
Install, update, verify#
sudo apt install -y sslscan # Debian / Ubuntu / Kali
sudo dnf install -y sslscan # Fedora / RHEL
brew install sslscan # macOS
sudo apt install --only-upgrade -y sslscan # update
sslscan --version # version + the OpenSSL build it links against
command -v sslscan # confirm it's on PATH
Cipher and protocol support tracks the linked OpenSSL. If SSLv2/SSLv3 show as “not tested,” a modern OpenSSL has dropped them, so build the static version whose bundled old OpenSSL can still negotiate them:
git clone https://github.com/rbsec/sslscan && cd sslscan && make static && sudo make install
Flags you’ll actually use#
Skim these first; the cheat sheet below is these flags combined.
| Flag | Does | Flag | Does |
|---|---|---|---|
host:port | Target (443 if no port) | --starttls-smtp … | Upgrade cleartext, then test TLS |
--tls10 … --tls13 | Test one protocol version | --sni-name= | Set SNI for a vhost / shared IP |
--tlsall | All TLS versions, skip SSLv2/3 | --show-certificate | Full cert: dates, key, SANs |
--show-certificates | The whole cert chain | --ocsp | Ask for the stapled OCSP status |
--certs= / --pk= | Client cert + key (mTLS) | --pkpass= | Password for the key / PKCS#12 |
--no-cipher-details | Drop curve / key-length columns | --iana-names | IANA cipher names, not OpenSSL |
--xml= | XML output (- = stdout) | --targets= | Scan a file of host:port lines |
--no-colour | Plain output for piping | --sleep= | Pause between connects (ms) |
--ipv4 / --ipv6 | Force address family | --connect-timeout= | Wait longer on a slow host |
Cheat sheet#
Each block starts bare and stacks one flag at a time, so you can stop at the line that does the job.
# Baseline: scan a TLS service (default port 443)
sslscan DOMAIN # full report: protocols, ciphers, cert, checks
sslscan TARGET_IP:8443 # same report on a non-standard port
sslscan https://DOMAIN/ # a URL works too; scheme and path are stripped
# Trim the report (stack these to cut noise)
sslscan --no-cipher-details DOMAIN # drop the EC curve / key-length columns
sslscan --no-heartbleed --no-compression --no-renegotiation --no-fallback DOMAIN # ciphers only
sslscan DOMAIN | grep -Ei 'accepted|preferred' # just the negotiable cipher lines
# Focus one protocol version (fast yes/no)
sslscan --tls10 DOMAIN # is TLS 1.0 still enabled?
sslscan --tls13 DOMAIN # TLS 1.3 ciphers only
sslscan --tlsall DOMAIN # all TLS versions, skip the SSLv2/3 probes
sslscan --ssl3 DOMAIN # probe SSLv3 only (needs the static build)
# Virtual hosts / shared IPs
sslscan --sni-name=DOMAIN TARGET_IP # request DOMAIN's cert via SNI
# STARTTLS: service opens in cleartext, upgrades on request
sslscan --starttls-smtp TARGET_IP:25 # mail submission (also 587)
sslscan --starttls-imap TARGET_IP:143 # mail retrieval
sslscan --starttls-pop3 TARGET_IP:110
sslscan --starttls-ldap TARGET_IP:389 # directory
sslscan --starttls-ftp TARGET_IP:21
sslscan --starttls-mysql TARGET_IP:3306
sslscan --starttls-psql TARGET_IP:5432
sslscan --starttls-xmpp TARGET_IP:5222 # add --xmpp-server for server-to-server
sslscan --rdp TARGET_IP:3389 # RDP preamble first, then scan
# Certificate detail
sslscan --show-certificate DOMAIN # subject, issuer, dates, key, SANs
sslscan --show-certificates DOMAIN # the full chain, not just the leaf
sslscan --ocsp DOMAIN # stapled OCSP / revocation status
sslscan --show-sigs DOMAIN # signature algorithms the server supports
# Client certificate (mutual TLS)
sslscan --certs=client.pem --pk=client.key DOMAIN:PORT
sslscan --pk=bundle.p12 --pkpass=PASS DOMAIN:PORT # PKCS#12 bundle + password
# Output, batch, pacing
sslscan --xml=tls.xml DOMAIN # machine-readable XML ("-" = stdout)
sslscan --iana-names DOMAIN # IANA/RFC cipher names, not OpenSSL's
sslscan --no-colour DOMAIN | tee tls.txt # plain text on screen and to a file
sslscan --targets=hosts.txt # one host or host:port per line
sslscan --sleep=250 DOMAIN # 250 ms between connects (quieter)
sslscan --connect-timeout=10 TARGET_IP # give a slow / filtered host longer
Command breakdowns#
Each block below explains one situation, the exact command, and what to expect back.
Scan a TLS service and see everything it offers#
sslscan DOMAIN
The complete default report for 443 in one call: protocol versions, the built-in checks, every accepted cipher, then the certificate. This is the baseline every other breakdown narrows down.
Scan TLS on a non-standard port#
sslscan TARGET_IP:8443
Same report for the service on that port. Admin panels and management interfaces love 8443, 9443, and 10443; pass the port after a colon.
List only the ciphers the server will negotiate#
sslscan DOMAIN | grep -Ei 'accepted|preferred'
Drops the protocol and certificate sections, leaving just the Preferred/Accepted cipher lines you can paste into notes. sslscan has no “accepted only” flag; a grep is the clean way to get it.
Get the right certificate on a shared or virtual host#
sslscan --sni-name=DOMAIN TARGET_IP
Sends SNI so the server returns DOMAIN’s cert instead of the default vhost’s. Without it, a shared IP hands back whichever cert is default and you read the wrong identity.
Scan a mail server that speaks STARTTLS#
sslscan --starttls-smtp TARGET_IP:25
Completes the plaintext SMTP greeting and issues STARTTLS first, then tests the upgraded session. Use the same flag on submission port 587.
Scan STARTTLS on IMAP, POP3, LDAP, FTP, or a database#
sslscan --starttls-imap TARGET_IP:143
sslscan --starttls-pop3 TARGET_IP:110
sslscan --starttls-ldap TARGET_IP:389
sslscan --starttls-ftp TARGET_IP:21
sslscan --starttls-psql TARGET_IP:5432
Any protocol that starts in cleartext needs its matching --starttls-* flag before sslscan can see the TLS behind it. --starttls-mysql and --starttls-xmpp exist too.
Confirm whether an old protocol is still enabled#
sslscan --tls10 DOMAIN
Tests only TLS 1.0: a fast yes/no when you are validating that a deprecation actually landed. Swap in --ssl3, --tls11, or --tls13 for the version you care about.
Check a service for Heartbleed#
sslscan DOMAIN | grep -i heartbleed
sslscan runs the check by default on each enabled protocol; you want to read not vulnerable to heartbleed on every line. Any vulnerable line is a critical finding.
Inspect the full certificate and its chain#
sslscan --show-certificate DOMAIN # the leaf: subject, issuer, dates, key, SANs
sslscan --show-certificates DOMAIN # the whole chain up to the root
--show-certificate prints subject, issuer, validity dates, RSA key strength, signature algorithm, and the full SAN list. Add the plural form when you need to see the intermediate/root chain the server sends.
Pull just the expiry and hostnames from a scan#
sslscan DOMAIN | grep -E "Not valid|Subject|Altnames"
The Altnames (SAN) line often reveals extra hostnames worth feeding to DNS and web enumeration, and Not valid after tells you the expiry at a glance.
Present a client certificate for mutual TLS#
sslscan --certs=client.pem --pk=client.key DOMAIN:PORT
Completes a handshake on a lab service that demands a client cert. For a combined PKCS#12 bundle use --pk=bundle.p12 --pkpass=PASS instead.
Check the certificate’s revocation status#
sslscan --ocsp DOMAIN
Asks the server for its stapled OCSP response so you can see revocation status inline, without a separate OCSP lookup.
Scan a whole list of hosts from a file#
sslscan --targets=hosts.txt
hosts.txt holds one host or host:port per line, all in scope. sslscan walks the file and prints a report per target.
Save machine-readable output for diffing over time#
sslscan --xml=scans/web-tls.xml DOMAIN
Diff the XML across runs to prove a config change removed a protocol or cipher. Use --xml=- to stream it to stdout for a pipeline.
Speed up a batch scan by skipping the extra checks#
sslscan --no-heartbleed --no-compression --no-renegotiation --no-fallback --no-groups DOMAIN
Turns off the four vulnerability probes and key-exchange group enumeration, leaving just protocol and cipher discovery. Worth it on a big --targets run where you only want the cipher inventory.
Slow the scan down to stay quiet#
sslscan --sleep=250 DOMAIN
Pauses 250 ms between connection attempts, gentler on a fragile service and less bursty in logs. Pair with --connect-timeout= when the host is slow to answer the initial connect.
Scan the TLS behind an RDP service#
sslscan --rdp TARGET_IP:3389
Sends the RDP negotiation preamble first, then scans the TLS that RDP wraps. Without --rdp the handshake never starts and 3389 looks like it speaks nothing.
Reading the output#
A scan reads top to bottom: protocols, the built-in checks, ciphers, then the certificate.
SSLv3 disabled
TLSv1.0 enabled <- legacy, note vs. policy
TLSv1.2 enabled
TLSv1.3 enabled
Preferred TLSv1.2 256 bits ECDHE-RSA-AES256-GCM-SHA384 Curve 25519 DHE 253
Accepted TLSv1.0 112 bits ECDHE-RSA-DES-CBC3-SHA Curve 25519 DHE 253 <- weak
| Line | Flag it when |
|---|---|
SSLv2 / SSLv3 ... enabled | Always: obsolete protocol still on |
TLSv1.0 / TLSv1.1 enabled | Note against your scope’s policy |
| Cipher bits / name | NULL, RC4, DES, 3DES/CBC3, EXPORT, or under 128 bits |
vulnerable to heartbleed | Critical: the opposite of what you want to see |
Compression enabled | Any “enabled” (CRIME) |
Insecure session renegotiation | Renegotiation weakness |
Not valid after | Date is in the past: expired cert |
RSA Key Strength: 1024 | Under 2048 bits |
Signature Algorithm: sha1/md5 | Weak cert signature |
Preferred is the server’s first choice, not proof it’s safe. Judge every line against policy, not against the color sslscan prints.
Troubleshooting#
| Problem | Cause | Fix |
|---|---|---|
command not found | Not installed / stale PATH | Reinstall; open a fresh shell |
Connection refused | Nothing on that port | Find TLS ports with Nmap first |
| Hangs / times out | Firewall dropping the handshake | Check reachability; raise --connect-timeout= |
| No ciphers on a live port | STARTTLS service | Add the matching --starttls-* flag |
| Wrong certificate returned | Missing SNI on a vhost | Add --sni-name=DOMAIN |
| SSLv2/SSLv3 show “not tested” | System OpenSSL dropped them | Use the static make static build |
| Cert marked untrusted | Self-signed / no CA bundle | Expected in labs; verify chain with openssl s_client |
Gotchas#
- sslscan does not find TLS ports, it reads one you already know. Scanning only
443misses TLS on mail, LDAP, database, and admin ports; run Nmap first. - A cleartext-first service needs
--starttls-*, or sslscan reports no TLS when TLS is available after the upgrade. Match the flag to the protocol. - On a shared IP, no
--sni-namereturns the default vhost’s cert. You can spend ten minutes reading the wrong certificate. - “Not tested” is not “disabled.” An old packaged sslscan cannot negotiate SSLv2/SSLv3 at all; use the static build before you conclude a legacy protocol is off.
- A supported weak protocol is a finding to verify against policy, not an automatic exploit. sslscan reports facts, it does not grade them.
Pairs with#
nmap -sV, or its ssl-enum-ciphers script, finds the TLS ports first and cross-checks the cipher list sslscan reports. When a single line looks wrong, openssl s_client -connect host:443 inspects or confirms that one handshake by hand, and curl checks what the service actually serves once you trust the handshake. For a scored, policy-aware verdict per check reach for testssl.sh; for structured JSON across many hosts in a pipeline reach for sslyze. sslscan stays the fastest way to get a readable picture of one service.