CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Dashboard
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2022-23632
7.40.56%

The Dot That Killed mTLS: Traefik CVE-2022-23632

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 21, 2026·6 min read·1 visit

PoC Available

Executive Summary (TL;DR)

Traefik versions < 2.6.1 fail to normalize Fully Qualified Domain Names (FQDNs) during TLS configuration lookups. Sending a Host header like 'example.com.' causes Traefik to miss the custom security policy (like mTLS) and revert to the default policy, potentially allowing unauthenticated access.

A logic error in Traefik's TCP router allows attackers to bypass specific TLS configurations—including Mutual TLS (mTLS) requirements—simply by appending a trailing dot to the Host header. This forces the proxy to fall back to the default, often less secure, TLS configuration.

The Hook: The Syntax Sugar of the Internet

In the world of DNS, we often forget that google.com is actually google.com.. That trailing dot represents the root of the DNS hierarchy. Browsers hide it, marketing teams hate it, but to a resolver, it is the absolute truth. It is the Fully Qualified Domain Name (FQDN). Most web servers normalize this automatically, stripping the dot before processing the request to ensure example.com and example.com. are treated as the same entity.

But what happens when a modern, "cloud-native" reverse proxy like Traefik decides to take things literally? You get CVE-2022-23632. This isn't a buffer overflow or a complex heap grooming exercise. It is a fundamental disagreement between how a user types a domain and how Go's map lookups handle strings.

Imagine you have a fortress. The front door is guarded by a bouncer who checks IDs (mTLS). The window, however, just has a sign that says "Default Entry." If you walk up to the bouncer and say "I am here for the Fortress," he checks your ID. If you say "I am here for the Fortress..." (with a dramatic pause/dot), the bouncer gets confused, checks his list, doesn't see an exact string match for the name with the pause, and shrugs: "I guess you go to the Default Entry then." You just walked past the security because of a punctuation mark.

The Flaw: Exact Matches in a Fuzzy World

The vulnerability resides in pkg/server/router/tcp/router.go. Traefik uses a map to store TLS configurations, keyed by the hostname. When a request comes in, Traefik extracts the Host header (or SNI) and looks it up in this map to determine which security settings to apply—cipher suites, min/max TLS versions, and crucially, ClientAuth (mTLS) settings.

The logic was painfully simple: exact string matching. In Go, map["example.com"] is a completely different memory address than map["example.com."]. There was no normalization step. If your configuration defined rules for secure.internal, the map key was secure.internal.

When an attacker sends a request with Host: secure.internal., the router successfully routes the traffic (because the routing logic often handles FQDNs or wildcards differently), but the TLS configuration lookup fails. Instead of rejecting the connection, Traefik's fail-safe mechanism kicks in: it applies the DefaultTLSConfigName. If your default configuration is permissive (which it usually is to support public traffic), the attacker just downgraded the security of that specific connection to the baseline.

The Code: The Smoking Gun

Let's look at the vulnerable function findTLSOptionName. It takes a map of options and the host string. Notice the lack of preprocessing on the host variable.

// Vulnerable Code (simplified)
func findTLSOptionName(tlsOptionsForHost map[string]string, host string) string {
    // Direct map lookup. If host is "example.com.", this returns nil
    tlsOptions, ok := tlsOptionsForHost[host]
    if ok {
        return tlsOptions
    }
    
    // ... (logic to check wildcards omitted) ...
    
    // Fallback to default if no match found
    return traefiktls.DefaultTLSConfigName
}

The fix introduced in version 2.6.1 is a classic "try it both ways" approach. It acknowledges that the input might be an FQDN and tries to normalize it by stripping or adding the dot to find a match.

// Patched Code in v2.6.1
func findTLSOptName(tlsOptionsForHost map[string]string, host string, fqdn bool) string {
    // 1. Try exact match
    tlsOptions, ok := tlsOptionsForHost[host]
    if ok { return tlsOptions }
 
    if !fqdn { return "" }
 
    // 2. Try STRIPPING the trailing dot
    if last := len(host) - 1; last >= 0 && host[last] == '.' {
        tlsOptions, ok = tlsOptionsForHost[host[:last]]
        if ok { return tlsOptions }
        return ""
    }
 
    // 3. Try ADDING a trailing dot
    tlsOptions, ok = tlsOptionsForHost[host+"."]
    if ok { return tlsOptions }
 
    return ""
}

This simple change closes the gap. Now, example.com. will successfully resolve to the config for example.com.

The Exploit: Bypassing mTLS

The most dangerous scenario for this bug is an mTLS bypass. Mutual TLS is often used for internal services, where the server requires the client to present a trusted certificate. This is common in Zero Trust architectures.

The Setup:

  1. Traefik Config: One router for api.secret.corp with strict mTLS required.
  2. Default Config: Standard TLS (no client cert required) for public pages.

The Attack: A standard request gets blocked:

$ curl https://api.secret.corp/admin
> 400 Bad Request (No Client Certificate)

The exploit request adds the dot:

$ curl -k --header "Host: api.secret.corp." https://api.secret.corp/admin

What happens internally:

  1. SNI: The attacker sends api.secret.corp in the ClientHello (standard TLS).
  2. Handshake: Traefik accepts the connection. It looks up the TLS options for api.secret.corp. (from the header/SNI logic depending on setup).
  3. Lookup Fail: It finds no config for the FQDN version.
  4. Fallback: It applies DefaultTLSConfig (no mTLS).
  5. Routing: The HTTP router still matches the host api.secret.corp. to the correct backend service because the routing rules often treat FQDNs leniently or use regex.
  6. Success: The request reaches the backend without a client certificate. If the backend relies solely on Traefik for authentication, the attacker is in.

The Impact: Security Theater

This vulnerability turns robust security configurations into security theater. The impact is technically rated as High (7.4), but functionally, it can be Critical depending on your architecture.

If you rely on Traefik as an Ingress Controller for Kubernetes, and you use it to terminate mTLS for sensitive pods, this bug renders that protection null and void against a trivial manipulation. It also allows attackers to bypass specific cipher suite restrictions. For example, if secure.bank.com requires TLS 1.3, but the default config allows TLS 1.0, an attacker could force a protocol downgrade by using the FQDN trick, potentially opening the door to other cryptographic attacks.

The real kicker? This leaves almost no trace in standard logs other than a slightly weird Host header, which most analysts would ignore as a typo or browser quirk.

The Fix: Upgrade or Normalize

The remediation is straightforward: Upgrade to Traefik v2.6.1. The patch handles the FQDN normalization logic internally, ensuring that host and host. share the same security context.

Emergency Workaround: If you cannot upgrade immediately (perhaps you are stuck on an old vendor fork), you must explicitly define the FQDN in your router rules. If your rule is Host(\example.com`)`, you need to change it to:

rule = "Host(`example.com`) || Host(`example.com.`)"

However, this is tedious and error-prone. The upgrade is the only scalable fix. This serves as a reminder to developers: inputs are messy, humans are creative, and DNS is a ancient beast that will bite you if you don't respect the dot.

Official Patches

TraefikPull Request #8764 fixing the issue

Fix Analysis (1)

Technical Appendix

CVSS Score
7.4/ 10
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N
EPSS Probability
0.56%
Top 32% most exploited

Affected Systems

Traefik v2.0 through v2.6.0Oracle Communications Unified Inventory Management 7.5.0

Affected Versions Detail

Product
Affected Versions
Fixed Version
Traefik
Traefik Labs
< 2.6.12.6.1
AttributeDetail
CWE IDCWE-295
Attack VectorNetwork
CVSS v3.17.4 (High)
EPSS Score0.00557 (Low)
ImpactSecurity Bypass (mTLS/TLS Options)
Exploit StatusPoC Available

MITRE ATT&CK Mapping

T1557Adversary-in-the-Middle
Credential Access
T1090Proxy
Command and Control
CWE-295
Improper Certificate Validation

Improper Certificate Validation

Known Exploits & Detection

GitHubUnit test case demonstrating the mismatch behavior

Vulnerability Timeline

Fix committed to master
2022-02-14
CVE Published & Traefik v2.6.1 Released
2022-02-17
Oracle releases patch for affected products
2022-07-19

References & Sources

  • [1]GitHub Security Advisory
  • [2]Traefik TLS Documentation

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.