CVE-2026-117666: ssl_verify_fun: partial-label wildcard certificate matches hostnames across a label boundary

Published Oct 30, 2026
·
Updated

Summary

Improper Validation of Certificate with Host Mismatch vulnerability in deadtrickster sslverifyfun allows an adversary in the middle to impersonate a TLS peer via a certificate that carries a partial-label wildcard. trymatchwildcard/4 in src/sslverifyhostname.erl anchors only the text before and after the of an identifier such as f.example.net, so the wildcard also consumes dots and the certificate is accepted for f.evil.example.net. RFC 6125 confines a wildcard to the left-most label. The same matcher lets .168.1.1 match the IP literal 192.168.1.1. Whole-label wildcards are matched correctly.

Erlang/OTP 20.0 and later check the hostname in ssl before a user verifyfun sees validpeer, so a client that keeps servernameindication set rejects the mismatch regardless. Exposed are clients that disable SNI, servers that verify client certificate hostnames with this library, and direct callers of verifycerthostname/2.

This issue affects sslverifyfun: from 1.0.1 before 1.1.8.

Details

1. Identifier parsing. sslverifyhostname:trymatchhostname/2 strips trailing dots, tries an exact case-insensitive match, and otherwise calls parseandvalidatewildcardidentifier/2. That function accepts an identifier with one in the left-most label and at least two labels after it, and classifies it as SingleCharW when the label is exactly .

2. Whole-label wildcard, correct. For .example.net the matcher compares the text after the with the hostname from its first dot onward, so a.b.example.net fails because .b.example.net differs from .example.net.

3. Partial-label wildcard, incorrect. For f.example.net the SingleCharW branch is skipped. trymatchwildcard/4 then checks only that the hostname ends with .example.net and starts with f. Nothing checks the span consumed by the , so f.evil.example.net and f.a.b.example.net pass. RFC 6125 section 6.4.3 limits a wildcard to the left-most label. The wildcardnotinalabel/2 guard only rejects internationalized xn-- labels and does not help.

4. IP literals. The reference identifier is treated as a DNS name whatever its shape, so verifycerthostname/2 with hostname 192.168.1.1 matches a dNSName of .168.1.1 through the whole-label branch. RFC 6125 does not apply wildcard matching to IP addresses, and OTP's ssl converts an IP-literal string to an {ip, ...} reference that only matches iPAddress entries.

5. Interaction with Erlang/OTP. When sslverifyhostname:verifyfun/3 is used as verifyfun, OTP 20.0 and later call sslcertificate:validate/4 for validpeer before the user fun. In the client role with servernameindication set to a hostname, or with a peer IP from a socket upgrade, OTP runs publickey:pkixverifyhostname/3 and hands the user fun {badcert, hostnamecheckfailed} instead of validpeer, which sslverifyfun turns into {fail, ...}. OTP's default match function allows no wildcards, and its https match function (publickey:pkixverifyhostnamematchfun/1) splits both names on dots and matches the wildcard within the first label only. The sslverifyfun result therefore decides the outcome only with {servernameindication, disable}, in the server role, on direct calls to verifycerthostname/2, or on OTP older than 20.0.

Proof of concept

1. Issue a leaf certificate whose only subjectAltName entry is dNSName f.example.net, for example with publickey:pkixtestdata/1, and decode it with publickey:pkixdecodecert(Der, otp). 2. Call sslverifyhostname:verifycerthostname(Cert, "foo.example.net"). It returns {valid, "foo.example.net"}, as expected. 3. Call sslverifyhostname:verifycerthostname(Cert, "f.evil.example.net"). It returns {valid, "f.evil.example.net"} although the hostname spans two labels where the wildcard sits. 4. Call sslverifyhostname:verifycerthostname(Cert, "a.b.example.net") as a control. It returns {fail, unabletomatchaltnames}. 5. For the IP-literal variant, issue a leaf with dNSName .168.1.1 and call verifycerthostname(Cert, "192.168.1.1"). It returns {valid, "192.168.1.1"}. 6. To observe the full TLS path, serve the first certificate from ssl:listen/2 and connect with {verify, verifypeer}, the CA in cacerts, {verifyfun, {fun sslverifyhostname:verifyfun/3, [{checkhostname, "f.evil.example.net"}]}} and {servernameindication, disable}. The handshake succeeds. With {servernameindication, "f.evil.example.net"} instead, OTP 20.0 or later rejects the certificate with hostnamecheckfailed before sslverifyfun runs.

Impact

A holder of a certificate for f.example.net from a CA the client trusts can impersonate any host named f.<label>.example.net to a client that relies on sslverifyfun alone for hostname verification, and read or alter the traffic of that connection.

Workarounds

On Erlang/OTP 21 or later, keep servernameindication set to the target hostname and pass {customizehostnamecheck, [{matchfun, publickey:pkixverifyhostnamematchfun(https)}]} in the ssl options. The hostname check in ssl then rejects the mismatch before sslverifyfun runs, and legitimate wildcard certificates continue to match. hackney and tlscertificatecheck already set both options.

Configurations

sslverifyfun decides the outcome only where it is the sole hostname check. Erlang/OTP 20.0 and later verify the hostname in ssl before the user verifyfun is called with validpeer, as long as servernameindication is a hostname or the connection was upgraded from a socket. A deployment is exposed when it sets {servernameindication, disable}, when a TLS server uses sslverifyhostname:verifyfun/3 to verify client certificates, when it calls sslverifyhostname:verifycerthostname/2 directly, or when it runs Erlang/OTP older than 20.0. The attacker also needs a certificate with a partial-label wildcard from a CA the client trusts, which the CA/Browser Forum Baseline Requirements do not allow, so the realistic exposure is private PKI.

Other sources

Improper Validation of Certificate with Host Mismatch vulnerability in deadtrickster sslverifyfun allows an adversary in the middle to impersonate a TLS peer via a certificate that carries a partial-label wildcard. trymatchwildcard/4 in src/sslverifyhostname.erl anchors only the text before and after the of an identifier such as f.example.net, so the wildcard also consumes dots and the certificate is accepted for f.evil.example.net. RFC 6125 confines a wildcard to the left-most label. The same matcher lets .168.1.1 match the IP literal 192.168.1.1. Whole-label wildcards are matched correctly.

Erlang/OTP 20.0 and later check the hostname in ssl before a user verifyfun sees validpeer, so a client that keeps servernameindication set rejects the mismatch regardless. Exposed are clients that disable SNI, servers that verify client certificate hostnames with this library, and direct callers of verifycerthostname/2.

This issue affects sslverifyfun: from 1.0.1 before 1.1.8.

— MITRE

Affected Software

1 affected componentFixes available
erlang/ssl_verify_fun>=1.0.1<1.1.8
1.1.8

Event History

Oct 30, 2026
CVE Published
via MITRE·08:22 PM
Data Sourced
via MITRE·08:22 PM
DescriptionWeakness
Data Sourced
via OSV·08:22 PM
DescriptionSeverityWeakness
Data Sourced
via OSV·08:22 PM
Affected Software
Data Sourced
via NVD·09:16 PM
DescriptionSeverityWeakness

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203