QCecuring - Enterprise Security Solutions

Live: Finding Expiring Certificates Across Subdomains

Certificate Discovery 09 Aug, 2026 · 04 Mins read

A technical walkthrough of subdomain enumeration combined with certificate scanning, using tools like subfinder, sslyze, and crt.sh to find expiring certificates before they cause outages.


The Problem: You Don’t Know What You Don’t Know

Most organizations can name their primary domains. They know about www.company.com and mail.company.com. But when asked how many total subdomains they have with active certificates, the answer is usually a guess — and that guess is almost always low by 40-60%.

This walkthrough demonstrates a real subdomain enumeration and certificate scanning process. We’ll go from zero visibility to a complete expiry report in under an hour.

Tools You’ll Need

Before starting, ensure you have these tools installed:

# Install subfinder (subdomain enumeration)
go install -v github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest

# Install httpx (HTTP probing with TLS info)
go install -v github.com/projectdiscovery/httpx/cmd/httpx@latest

# Install sslyze (deep TLS analysis)
pip install sslyze

# OpenSSL (usually pre-installed)
openssl version
ToolPurposeSpeedDepth
subfinderPassive subdomain discoveryFastBroad
crt.shCT log queriesMediumHistorical
httpxActive probing + TLS grabFastCurrent
sslyzeDeep TLS/cert analysisSlowDetailed
OpenSSLManual cert inspectionManualFull

Phase 1: Subdomain Enumeration

Passive Discovery with subfinder

# Run subfinder with all passive sources
subfinder -d example.com -all -silent -o subdomains.txt

# Sample output:
# www.example.com
# mail.example.com
# vpn.example.com
# api.example.com
# staging.example.com
# portal.example.com
# cdn.example.com
# ...

The Certificate Visibility Gap

Typical enterprise: what's tracked vs. what actually exists

Tracking Coverage

Certificates Found by Source

⚠️ In the average mid-market enterprise, less than 15% of certificates have both ownership documentation and expiry monitoring.

CT Log Enrichment

# Query crt.sh for additional subdomains not found by subfinder
curl -s "https://crt.sh/?q=%.example.com&output=json" | \
  jq -r '.[].name_value' | \
  sed 's/\*\.//g' | \
  sort -u > ct-subdomains.txt

# Merge both sources
cat subdomains.txt ct-subdomains.txt | sort -u > all-subdomains.txt
echo "Total unique subdomains: $(wc -l < all-subdomains.txt)"

Real Results Example

For a typical mid-size enterprise, here’s what enumeration reveals:

Discovery Source Breakdown:
━━━━━━━━━━━━━━━━━━━━━━━━━
subfinder passive:     142 subdomains
crt.sh CT logs:        287 subdomains
Combined unique:       341 subdomains
With active DNS:       198 subdomains
Serving HTTPS:         156 subdomains

The gap between “subdomains IT knows about” (typically 30-50) and “subdomains that actually exist” (198 in this case) is your certificate visibility gap.

Phase 2: Active Certificate Scanning

Quick Scan with httpx

# Probe all subdomains and extract TLS certificate information
cat all-subdomains.txt | \
  httpx -silent -tls-grab -json -o tls-results.json

# Extract expiry dates
cat tls-results.json | \
  jq -r '{
    host: .host,
    issuer: .tls.issuer_organization,
    not_after: .tls.not_after,
    subject_cn: .tls.subject_cn,
    fingerprint: .tls.fingerprint_hash.sha256
  }' > cert-inventory.json

Deep Analysis with sslyze

For endpoints requiring detailed analysis:

# Run sslyze against specific targets
sslyze --regular api.example.com portal.example.com vpn.example.com

# JSON output for automation
sslyze --json_out=sslyze-results.json \
  --certinfo \
  --targets_in=high-priority-hosts.txt

OpenSSL for Individual Inspection

# Detailed certificate inspection
echo | openssl s_client -connect api.example.com:443 \
  -servername api.example.com 2>/dev/null | \
  openssl x509 -noout \
  -subject -issuer -dates -serial -fingerprint -ext subjectAltName

# Output:
# subject=CN = api.example.com
# issuer=C = US, O = DigiCert Inc, CN = DigiCert TLS RSA SHA256 2020 CA1
# notBefore=Jan 15 00:00:00 2026 GMT
# notAfter=Feb 14 23:59:59 2027 GMT
# serial=0A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D
# SHA1 Fingerprint=AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD

Phase 3: Expiry Analysis

The Complete Scanning Script

#!/bin/bash
# cert-expiry-scanner.sh - Find expiring certificates across subdomains
# Usage: ./cert-expiry-scanner.sh example.com [days-threshold]

DOMAIN="${1:?Usage: $0 domain.com [days]}"
THRESHOLD="${2:-30}"
DATE=$(date +%Y%m%d)
OUTPUT_DIR="cert-scan-$DOMAIN-$DATE"

mkdir -p "$OUTPUT_DIR"

echo "=== Certificate Expiry Scanner ==="
echo "Domain: $DOMAIN"
echo "Threshold: $THRESHOLD days"
echo "Output: $OUTPUT_DIR/"
echo ""

# Step 1: Enumerate subdomains
echo "[1/4] Enumerating subdomains..."
subfinder -d "$DOMAIN" -all -silent > "$OUTPUT_DIR/subdomains-raw.txt"
curl -s "https://crt.sh/?q=%.$DOMAIN&output=json" | \
  jq -r '.[].name_value' 2>/dev/null | \
  sed 's/\*\.//g' >> "$OUTPUT_DIR/subdomains-raw.txt"
sort -u "$OUTPUT_DIR/subdomains-raw.txt" > "$OUTPUT_DIR/subdomains.txt"
TOTAL=$(wc -l < "$OUTPUT_DIR/subdomains.txt")
echo "    Found $TOTAL unique subdomains"

# Step 2: Check which are alive on 443
echo "[2/4] Probing HTTPS endpoints..."
cat "$OUTPUT_DIR/subdomains.txt" | \
  httpx -silent -ports 443 -tls-grab -json \
  > "$OUTPUT_DIR/tls-scan.json" 2>/dev/null
ALIVE=$(wc -l < "$OUTPUT_DIR/tls-scan.json")
echo "    $ALIVE endpoints responding on HTTPS"

# Step 3: Parse and analyze expiry
echo "[3/4] Analyzing certificate expiry..."
NOW=$(date +%s)

echo "STATUS,DAYS_LEFT,HOST,ISSUER,EXPIRY_DATE,SUBJECT" \
  > "$OUTPUT_DIR/expiry-report.csv"

cat "$OUTPUT_DIR/tls-scan.json" | while read line; do
  host=$(echo "$line" | jq -r '.host')
  not_after=$(echo "$line" | jq -r '.tls.not_after // empty')
  issuer=$(echo "$line" | jq -r '.tls.issuer_organization // "Unknown"')
  subject=$(echo "$line" | jq -r '.tls.subject_cn // "N/A"')
  
  if [ -n "$not_after" ]; then
    expiry_epoch=$(date -d "$not_after" +%s 2>/dev/null)
    if [ -n "$expiry_epoch" ]; then
      days_left=$(( (expiry_epoch - NOW) / 86400 ))
      
      if [ $days_left -lt 0 ]; then
        status="EXPIRED"
      elif [ $days_left -lt $THRESHOLD ]; then
        status="EXPIRING"
      else
        status="OK"
      fi
      
      echo "$status,$days_left,$host,$issuer,$not_after,$subject" \
        >> "$OUTPUT_DIR/expiry-report.csv"
    fi
  fi
done

# Step 4: Generate summary
echo "[4/4] Generating summary..."
EXPIRED=$(grep -c "^EXPIRED" "$OUTPUT_DIR/expiry-report.csv" 2>/dev/null || echo 0)
EXPIRING=$(grep -c "^EXPIRING" "$OUTPUT_DIR/expiry-report.csv" 2>/dev/null || echo 0)
OK=$(grep -c "^OK" "$OUTPUT_DIR/expiry-report.csv" 2>/dev/null || echo 0)

echo ""
echo "=== RESULTS ==="
echo "Total subdomains found:  $TOTAL"
echo "HTTPS endpoints alive:   $ALIVE"
echo "Certificates expired:    $EXPIRED"
echo "Certificates expiring:   $EXPIRING (within $THRESHOLD days)"
echo "Certificates OK:         $OK"
echo ""
echo "Report saved: $OUTPUT_DIR/expiry-report.csv"

# Show critical findings
if [ $EXPIRED -gt 0 ] || [ $EXPIRING -gt 0 ]; then
  echo ""
  echo "=== CRITICAL FINDINGS ==="
  grep -E "^(EXPIRED|EXPIRING)" "$OUTPUT_DIR/expiry-report.csv" | \
    sort -t',' -k2 -n | \
    column -t -s','
fi

Sample Output

=== Certificate Expiry Scanner ===
Domain: example.com
Threshold: 30 days
Output: cert-scan-example.com-20260717/

[1/4] Enumerating subdomains...
    Found 341 unique subdomains
[2/4] Probing HTTPS endpoints...
    156 endpoints responding on HTTPS
[3/4] Analyzing certificate expiry...
[4/4] Generating summary...

=== RESULTS ===
Total subdomains found:  341
HTTPS endpoints alive:   156
Certificates expired:    7
Certificates expiring:   12 (within 30 days)
Certificates OK:         137

=== CRITICAL FINDINGS ===
EXPIRED   -45  old-portal.example.com    Let's Encrypt    2026-06-01  old-portal.example.com
EXPIRED   -23  staging-v1.example.com    DigiCert         2026-06-24  *.example.com
EXPIRED   -12  dev-api.example.com       Let's Encrypt    2026-07-05  dev-api.example.com
EXPIRING   3   vpn.example.com           Sectigo          2026-07-20  vpn.example.com
EXPIRING   7   mail.example.com          DigiCert         2026-07-24  mail.example.com
EXPIRING  14   api-v2.example.com        Let's Encrypt    2026-07-31  api-v2.example.com
EXPIRING  21   portal.example.com        DigiCert         2026-08-07  portal.example.com

Interpreting the Findings

Pattern Recognition

When analyzing scan results, look for these patterns:

1. Cluster Expiry Multiple certificates expiring within the same week often indicate a batch purchase that was never set up for staggered renewal.

2. Let’s Encrypt Gaps Let’s Encrypt certificates expiring suggest automation failure. These should auto-renew at 60 days — if they’re expiring, certbot or the ACME client is broken.

3. Wildcard Drift When you see the same wildcard cert (*.example.com) reported across many hosts but with different expiry dates, you have key distribution sprawl.

4. Orphaned Services Expired certificates on hosts that still resolve but return errors indicate decommissioned services that were never fully cleaned up.

Risk Classification

┌─────────────────────────────────────────────────┐
│ RISK CLASSIFICATION MATRIX                       │
├────────────┬────────────────────────────────────┤
│ Category   │ Criteria                           │
├────────────┼────────────────────────────────────┤
│ Critical   │ Expired + customer-facing          │
│            │ Expiring &lt;7 days + production     │
├────────────┼────────────────────────────────────┤
│ High       │ Expired + internal-reachable       │
│            │ Expiring &lt;14 days + production    │
├────────────┼────────────────────────────────────┤
│ Medium     │ Expiring &lt;30 days                 │
│            │ Expired + non-production           │
├────────────┼────────────────────────────────────┤
│ Low        │ Expiring 30-60 days               │
│            │ Non-critical environments          │
└────────────┴────────────────────────────────────┘

Automation: Making This Repeatable

Cron-Based Weekly Scanning

# /etc/cron.d/cert-scanner
# Run every Monday at 6 AM
0 6 * * 1 /opt/scripts/cert-expiry-scanner.sh company.com 30 2>&1 | \
  mail -s "Weekly Certificate Expiry Report" security-team@company.com

Integration with Monitoring

# prometheus-cert-exporter config
modules:
  https:
    prober: tcp
    tls_config:
      insecure_skip_verify: false
    tcp:
      tls: true

# Alert rule
groups:
  - name: certificate_expiry
    rules:
      - alert: CertificateExpiringSoon
        expr: probe_ssl_earliest_cert_expiry - time() < 86400 * 14
        for: 1h
        labels:
          severity: warning
        annotations:
          summary: "Certificate expiring within 14 days"
          description: "{{ $labels.instance }} cert expires in {{ $value | humanizeDuration }}"

What You’ll Typically Find

Based on running this process across dozens of organizations:

FindingFrequencyTypical Count
Unknown subdomains95% of orgs40-200 extra domains
Expired certificates80% of orgs5-25 expired certs
Certificates expiring <30 days90% of orgs8-15 certs
Multiple CA providers85% of orgs3-7 different CAs
Shadow IT certificates70% of orgs10-50 unknown certs
Weak key algorithms40% of orgs5-20 weak certs

Next Steps After Discovery

  1. Triage — Sort findings by risk level, address critical items within 24 hours
  2. Assign ownership — Every certificate needs an owner, not just an issuer
  3. Establish baselines — Run the scan weekly to track improvement
  4. Automate monitoring — Move from periodic scanning to continuous monitoring
  5. Consider CLM — If you have >100 certificates, manual processes won’t scale

About QCecuring

QCecuring helps enterprises move from reactive certificate scanning to proactive lifecycle management. Our platform provides continuous discovery, automated alerting, and complete inventory management across all certificate types and environments.

Tags: subdomain enumeration, certificate scanning, expiring certificates, sslyze, subfinder, crt.sh, CT logs, certificate discovery, TLS analysis, security automation

Stay Ahead on Crypto & PKI

Monthly insights on certificate management, post-quantum readiness, and enterprise security.

Subscribe Free

Related Insights

Certificate Lifecycle Management

What a $0 Certificate Outage Prevention Strategy Looks Like

Free and open-source approaches to certificate monitoring using PowerShell scripts, certutil queries, cron jobs, and Prometheus exporters — when free is enough and when you've outgrown it.

By Mani sri kumar

18 Aug, 2026 · 06 Mins read

Certificate Lifecycle ManagementEnterprise Security

Certificate Lifecycle Management

How to Convince Your Manager You Need Certificate Visibility

Champion enablement content with talking points for budget approval, cost justification frameworks, risk framing, one-pager templates, and objection handling for certificate lifecycle management.

By Mani sri kumar

17 Aug, 2026 · 06 Mins read

Certificate Lifecycle ManagementEnterprise Security

PKI Architecture

PKI for IT Teams: What You Actually Need to Know (No Crypto Theory)

A practical PKI explainer for IT operations teams — skip the math, focus on what breaks, how certs work in enterprise, CA hierarchy simplified, and what IT teams interact with daily.

By Mani sri kumar

16 Aug, 2026 · 05 Mins read

PKI ArchitectureEnterprise Security

Ready to Secure Your Enterprise?

Experience how our cryptographic solutions simplify, centralize, and automate identity management for your entire organization.

Stay ahead on cryptography & PKI

Get monthly insights on certificate management, post-quantum readiness, and enterprise security. No spam.

We respect your privacy. Unsubscribe anytime.