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: Preferred is the server’s first pick, Accepted is 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.

FlagDoesFlagDoes
host:portTarget (443 if no port)--starttls-smtpUpgrade cleartext, then test TLS
--tls10--tls13Test one protocol version--sni-name=Set SNI for a vhost / shared IP
--tlsallAll TLS versions, skip SSLv2/3--show-certificateFull cert: dates, key, SANs
--show-certificatesThe whole cert chain--ocspAsk for the stapled OCSP status
--certs= / --pk=Client cert + key (mTLS)--pkpass=Password for the key / PKCS#12
--no-cipher-detailsDrop curve / key-length columns--iana-namesIANA cipher names, not OpenSSL
--xml=XML output (- = stdout)--targets=Scan a file of host:port lines
--no-colourPlain output for piping--sleep=Pause between connects (ms)
--ipv4 / --ipv6Force 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
LineFlag it when
SSLv2 / SSLv3 ... enabledAlways: obsolete protocol still on
TLSv1.0 / TLSv1.1 enabledNote against your scope’s policy
Cipher bits / nameNULL, RC4, DES, 3DES/CBC3, EXPORT, or under 128 bits
vulnerable to heartbleedCritical: the opposite of what you want to see
Compression enabledAny “enabled” (CRIME)
Insecure session renegotiationRenegotiation weakness
Not valid afterDate is in the past: expired cert
RSA Key Strength: 1024Under 2048 bits
Signature Algorithm: sha1/md5Weak 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#

ProblemCauseFix
command not foundNot installed / stale PATHReinstall; open a fresh shell
Connection refusedNothing on that portFind TLS ports with Nmap first
Hangs / times outFirewall dropping the handshakeCheck reachability; raise --connect-timeout=
No ciphers on a live portSTARTTLS serviceAdd the matching --starttls-* flag
Wrong certificate returnedMissing SNI on a vhostAdd --sni-name=DOMAIN
SSLv2/SSLv3 show “not tested”System OpenSSL dropped themUse the static make static build
Cert marked untrustedSelf-signed / no CA bundleExpected in labs; verify chain with openssl s_client

Gotchas#

  • sslscan does not find TLS ports, it reads one you already know. Scanning only 443 misses 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-name returns 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.

Find us elsewhere

Merch, stickers, and moreSupport the work at the Solvere Labs shop