Back to Articles

tlsx: How ProjectDiscovery Built a Multi-Protocol TLS Scanner for Offensive Reconnaissance

[ View on GitHub ]

tlsx: How ProjectDiscovery Built a Multi-Protocol TLS Scanner for Offensive Reconnaissance

Hook

Most TLS scanners fail silently on legacy servers running TLS 1.0. tlsx solves this by bundling three complete TLS implementations—ztls, crypto/tls, and OpenSSL—with automatic fallback, making it the Swiss Army knife of certificate reconnaissance.

Context

Security researchers and bug bounty hunters face a recurring challenge: mapping the TLS landscape of large networks quickly and comprehensively. Traditional tools like openssl s_client work fine for one-off certificate checks, but fail at scale. You need to enumerate thousands of hosts, extract SANs for subdomain discovery, fingerprint server implementations with JARM and JA3, and detect misconfigurations like expired certificates—all without timing out or missing data because a target runs an ancient TLS version.

The ecosystem offered incomplete solutions. Nmap’s ssl-enum-ciphers is thorough but slow. testssl.sh provides extensive checks but lacks the speed and structured output needed for pipeline integration. sslyze hits a sweet spot for compliance scanning but isn’t optimized for offensive reconnaissance workflows where you’re dumping results into nuclei or httpx for follow-up exploitation. ProjectDiscovery built tlsx to fill this gap: a purpose-built TLS grabber that prioritizes speed, flexibility, and integration with their security toolchain.

Technical Insight

Target list

Distribute

Handshake request

Fallback chain

TLS metadata

TLS metadata

TLS metadata

Input Parser

IP/CIDR/ASN/URL

Work Queue

Target Hosts + Ports

Worker Pool

300 Goroutines

TLS Engine

Auto-Select

ztls

Fingerprinting

crypto/tls

Standard Go

OpenSSL

CGO Bindings

Probe Extractors

SANs/CN

JA3/JARM

Cipher Suites

Misconfiguration

Detection

Output Formatter

JSON/Text

System architecture — auto-generated

The core architectural decision in tlsx is its multi-implementation TLS engine. Rather than relying solely on Go’s crypto/tls, the tool integrates three distinct backends: ztls (a fork focused on fingerprinting), ctls (the standard Go crypto/tls), and OpenSSL via CGO bindings. This matters because TLS version negotiation is messy in the real world—servers might reject connections from modern clients that don’t advertise legacy protocols, or implementations might have quirks that cause handshake failures with specific client libraries.

The scanning engine accepts inputs through a flexible pipeline that understands multiple formats. You can feed it bare IPs, CIDRs for subnet scanning, ASN numbers for cloud provider enumeration, or full URLs. Here’s a practical example scanning a CIDR range with JSON output and fingerprinting enabled:

echo '192.168.1.0/24' | tlsx -p 443,8443 -json -jarm -ja3 -san -cn

This command performs TLS handshakes on ports 443 and 8443 across 256 hosts, extracting JARM fingerprints (a hash of server TLS behavior), JA3 fingerprints (client-side fingerprinting data), Subject Alternative Names for subdomain enumeration, and Common Names. The JSON output structures this data for downstream processing—you might pipe it to jq to extract unique SANs, then feed those to a subdomain takeover scanner.

The concurrency model uses a worker pool pattern with 300 default goroutines, each maintaining its own connection state. This aggressive parallelism is deliberate: network I/O dominates CPU time in TLS handshakes, so the overhead of context switching is negligible compared to the gains from parallel operations. However, tlsx includes a pre-handshake termination mode that aborts connections after the ServerHello message. This optimization matters when you only need server certificates and don’t care about completing the full handshake—you save round trips and reduce detection risk.

The probe system is particularly elegant. Each probe (cipher, version, cert-expiry, revoked, self-signed) operates as an independent module that processes the TLS connection state. Want to enumerate supported cipher suites and categorize them by security? The cipher probe attempts connections with different cipher configurations and tags results:

tlsx -u example.com -cipher -cipher-enum

This triggers iterative handshakes with different cipher suites, classifying them into secure (AES-GCM, ChaCha20), weak (3DES, RC4), and insecure (NULL, EXPORT) categories. The implementation reuses connection pools intelligently, avoiding unnecessary TCP setup overhead when testing multiple configurations against the same target.

For large-scale reconnaissance, the ASN input mode shines. You can target entire cloud providers or organizations by their autonomous system number:

tlsx -asn AS15169 -silent -json -o google-tls.json

This queries public BGP data for Google’s IP ranges (AS15169), then scans all discovered hosts. The silent flag suppresses progress output, making it suitable for cron jobs or automated pipelines. The tool handles IP expansion internally, saving you from writing brittle shell scripts to resolve ASNs to prefixes.

Certificate validation checking demonstrates the tool’s depth. Beyond basic expiration checks, tlsx can verify revocation status via OCSP and CRL:

tlsx -u example.com -revoked -resp

The implementation queries OCSP responders defined in certificate Authority Information Access extensions, caching responses to avoid redundant lookups when scanning multiple hosts with certificates from the same CA. This is computationally expensive—OCSP responders are often slow or unreliable—but essential for compliance audits where you need proof that certificates haven’t been revoked.

Gotcha

The documentation claims Go 1.24 as a requirement, which doesn’t exist yet—this is almost certainly a typo for Go 1.21 or similar. It’s a minor issue but reflects a broader pattern in ProjectDiscovery tools where documentation lags behind rapid development. You’ll frequently need to read source code or check GitHub issues to understand undocumented flags or behaviors.

The revocation checking features sound powerful but are practically unreliable at scale. OCSP responders frequently timeout or rate-limit requests, and CRL downloads can be massive (some are hundreds of megabytes). In testing against large datasets, expect 20-30% of revocation checks to fail with network errors or timeouts, requiring you to implement retry logic or accept incomplete data. The tool doesn’t provide fine-grained timeout controls per probe, so slow OCSP responders can bottleneck your entire scan.

High concurrency defaults will absolutely trigger intrusion detection systems and rate limiters if you’re scanning corporate infrastructure without tuning. The 300-worker default is optimized for bug bounty scenarios where you’re scanning distributed targets across the internet, not penetration tests against a single organization’s IP space. You’ll need to dial back concurrency with -c and add delays with -delay to avoid getting blocked or generating alerts. This isn’t documented prominently, leading to newcomers inadvertently DoS’ing targets or getting their source IPs blacklisted.

Verdict

Use tlsx if you’re doing security reconnaissance at scale—bug bounties, red team engagements, or infrastructure audits where you need to map TLS configurations across hundreds or thousands of hosts. It excels in ProjectDiscovery workflows where you’re chaining tools together: tlsx for TLS enumeration → httpx for web probing → nuclei for vulnerability scanning. The fingerprinting capabilities (JARM, JA3) and SAN extraction make it invaluable for passive subdomain discovery and server profiling. Skip it if you need deep SSL/TLS analysis for compliance reporting (testssl.sh provides better documentation and human-readable reports), you’re doing one-off certificate debugging (openssl s_client is simpler), or you work in environments where installing Go tools is restricted and you need Python-based alternatives like sslyze. Also skip if you need stability guarantees—ProjectDiscovery tools move fast and break things, prioritizing features over backward compatibility.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/data-knowledge/projectdiscovery-tlsx.svg)](https://starlog.is/api/badge-click/data-knowledge/projectdiscovery-tlsx)