CVE-2026-105236 (GCVE-0-2026-105236)
Vulnerability from cvelistv5 – Published: 2026-10-14 16:55 – Updated: 2026-10-14 16:55
VLAI
EPSS
VEX
Title
TLS 1.2 session resumption in ssl crosses SNI virtual-host client-certificate trust boundaries
Summary
Improper Certificate Validation vulnerability in the ssl application in Erlang/OTP allows a client that holds a certificate trusted by one SNI virtual host to authenticate to a second SNI virtual host on the same listener by resuming its TLS 1.2 session there.
A server that serves several SNI names from one listen socket through sni_fun or sni_hosts keeps one pre-TLS 1.3 session cache per listener and does not bind a cached session to the SNI name or client trust policy that established it. When an SNI option set switches to verify_peer without naming reuse_sessions, ssl_config:update_options/3 inherits the base listener's reuse_sessions value of true, so the default that disables reuse for client-authenticating servers does not apply. On an abbreviated handshake ssl_session:is_resumable/4 checks only that the cached session uses one of the current server certificates and carries some peer certificate, and never revalidates that certificate against the CA selected for the requested name. The names must share a server certificate, which is common with wildcard or multi-SAN certificates. TLS 1.3 does not use this cache and is not affected.
This issue affects OTP from OTP 18.0 before OTP 27.3.4.19, OTP 28.5.0.8, and OTP 29.1.2, corresponding to ssl from 7.0 before 11.2.12.14, 11.6.0.7, and 11.7.8.
Severity
CWE
- CWE-295 - Improper Certificate Validation
Assigner
References
8 references
Impacted products
3 products
| Vendor | Product | Version | CPE status | |
|---|---|---|---|---|
| Erlang | OTP |
Affected:
18.0 , < *
(otp)
cpe:2.3:a:erlang:erlang\/otp:*:*:*:*:*:*:*:* |
||
| Erlang | OTP |
Affected:
7.0 , < *
(otp)
cpe:2.3:a:erlang:erlang\/otp:*:*:*:*:*:*:*:* |
||
| Erlang | OTP |
Affected:
4fe38c4b8b2c8024afb60990e598ff823743fd54 , < *
(git)
cpe:2.3:a:erlang:erlang\/otp:*:*:*:*:*:*:*:* |
{
"containers": {
"cna": {
"affected": [
{
"cpes": [
"cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"ssl_gen_statem",
"ssl_config",
"ssl_session",
"ssl_handshake"
],
"packageName": "otp",
"packageURL": "pkg:software-id/erlang.org/otp",
"product": "OTP",
"programFiles": [
"lib/ssl/src/ssl_gen_statem.erl",
"lib/ssl/src/ssl_config.erl",
"lib/ssl/src/ssl_session.erl",
"lib/ssl/src/ssl_handshake.erl"
],
"programRoutines": [
{
"name": "ssl_gen_statem:handle_sni_extension/2"
},
{
"name": "ssl_gen_statem:handle_sni_hostname/2"
},
{
"name": "ssl_gen_statem:update_ssl_options_from_sni/2"
},
{
"name": "ssl_gen_statem:fallback_options/2"
},
{
"name": "ssl_config:update_options/3"
},
{
"name": "ssl_config:opt_reuse_sessions/3"
},
{
"name": "ssl_session:server_select_session/5"
},
{
"name": "ssl_session:is_resumable/4"
},
{
"name": "ssl_session:reusable_options/2"
},
{
"name": "ssl_handshake:select_session/8"
}
],
"vendor": "Erlang",
"versions": [
{
"changes": [
{
"at": "27.3.4.19",
"status": "unaffected"
},
{
"at": "28.5.0.8",
"status": "unaffected"
},
{
"at": "29.1.2",
"status": "unaffected"
}
],
"lessThan": "*",
"status": "affected",
"version": "18.0",
"versionType": "otp"
}
]
},
{
"cpes": [
"cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"ssl_gen_statem",
"ssl_config",
"ssl_session",
"ssl_handshake"
],
"packageName": "ssl",
"packageURL": "pkg:otp/ssl?repository_url=https:%2F%2Fgithub.com%2Ferlang%2Fotp\u0026vcs_url=git%2Bhttps:%2F%2Fgithub.com%2Ferlang%2Fotp.git",
"product": "OTP",
"programFiles": [
"src/ssl_gen_statem.erl",
"src/ssl_config.erl",
"src/ssl_session.erl",
"src/ssl_handshake.erl"
],
"programRoutines": [
{
"name": "ssl_gen_statem:handle_sni_extension/2"
},
{
"name": "ssl_gen_statem:handle_sni_hostname/2"
},
{
"name": "ssl_gen_statem:update_ssl_options_from_sni/2"
},
{
"name": "ssl_gen_statem:fallback_options/2"
},
{
"name": "ssl_config:update_options/3"
},
{
"name": "ssl_config:opt_reuse_sessions/3"
},
{
"name": "ssl_session:server_select_session/5"
},
{
"name": "ssl_session:is_resumable/4"
},
{
"name": "ssl_session:reusable_options/2"
},
{
"name": "ssl_handshake:select_session/8"
}
],
"repo": "https://github.com/erlang/otp",
"vendor": "Erlang",
"versions": [
{
"changes": [
{
"at": "11.2.12.14",
"status": "unaffected"
},
{
"at": "11.6.0.7",
"status": "unaffected"
},
{
"at": "11.7.8",
"status": "unaffected"
}
],
"lessThan": "*",
"status": "affected",
"version": "7.0",
"versionType": "otp"
}
]
},
{
"collectionURL": "https://github.com",
"cpes": [
"cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"ssl_gen_statem",
"ssl_config",
"ssl_session",
"ssl_handshake"
],
"packageName": "erlang/otp",
"packageURL": "pkg:github/erlang/otp",
"product": "OTP",
"programFiles": [
"lib/ssl/src/ssl_gen_statem.erl",
"lib/ssl/src/ssl_config.erl",
"lib/ssl/src/ssl_session.erl",
"lib/ssl/src/ssl_handshake.erl"
],
"programRoutines": [
{
"name": "ssl_gen_statem:handle_sni_extension/2"
},
{
"name": "ssl_gen_statem:handle_sni_hostname/2"
},
{
"name": "ssl_gen_statem:update_ssl_options_from_sni/2"
},
{
"name": "ssl_gen_statem:fallback_options/2"
},
{
"name": "ssl_config:update_options/3"
},
{
"name": "ssl_config:opt_reuse_sessions/3"
},
{
"name": "ssl_session:server_select_session/5"
},
{
"name": "ssl_session:is_resumable/4"
},
{
"name": "ssl_session:reusable_options/2"
},
{
"name": "ssl_handshake:select_session/8"
}
],
"repo": "https://github.com/erlang/otp",
"vendor": "Erlang",
"versions": [
{
"changes": [
{
"at": "0c20f3683b3c81fd30b46047195be9b3d2c69d76",
"status": "unaffected"
},
{
"at": "4d76adbf39d547a931048544df09c3c087e56618",
"status": "unaffected"
},
{
"at": "10ee925d0fee8413f1170cc21bfcf123c8d22454",
"status": "unaffected"
}
],
"lessThan": "*",
"status": "affected",
"version": "4fe38c4b8b2c8024afb60990e598ff823743fd54",
"versionType": "git"
}
]
}
],
"configurations": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003eExploitation requires all of the following. One TLS listener serves at least two SNI names through \u003ccode\u003esni_fun\u003c/code\u003e or \u003ccode\u003esni_hosts\u003c/code\u003e. The names use the same server certificate but select different client CAs with \u003ccode\u003everify_peer\u003c/code\u003e. TLS 1.2 or earlier is enabled. No SNI option set sets \u003ccode\u003ereuse_sessions\u003c/code\u003e to false while the base listener\u0027s effective value is true, which is the default when the base listener uses \u003ccode\u003everify_none\u003c/code\u003e. The attacker holds a client certificate that the lower-trust name accepts.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "Exploitation requires all of the following. One TLS listener serves at least two SNI names through `sni_fun` or `sni_hosts`. The names use the same server certificate but select different client CAs with `verify_peer`. TLS 1.2 or earlier is enabled. No SNI option set sets `reuse_sessions` to false while the base listener\u0027s effective value is true, which is the default when the base listener uses `verify_none`. The attacker holds a client certificate that the lower-trust name accepts."
}
],
"value": "Exploitation requires all of the following. One TLS listener serves at least two SNI names through sni_fun or sni_hosts. The names use the same server certificate but select different client CAs with verify_peer. TLS 1.2 or earlier is enabled. No SNI option set sets reuse_sessions to false while the base listener\u0027s effective value is true, which is the default when the base listener uses verify_none. The attacker holds a client certificate that the lower-trust name accepts."
}
],
"cpeApplicability": [
{
"nodes": [
{
"cpeMatch": [
{
"criteria": "cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*",
"versionEndExcluding": "27.3.4.19",
"versionStartIncluding": "18.0",
"vulnerable": true
},
{
"criteria": "cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*",
"versionEndExcluding": "28.5.0.8",
"versionStartIncluding": "28.0",
"vulnerable": true
},
{
"criteria": "cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*",
"versionEndExcluding": "29.1.2",
"versionStartIncluding": "29.0",
"vulnerable": true
}
],
"negate": false,
"operator": "OR"
}
],
"operator": "AND"
}
],
"credits": [
{
"lang": "en",
"type": "finder",
"value": "Yonghwa Lee / Xint by Theori"
},
{
"lang": "en",
"type": "reporter",
"value": "Yonghwa Lee / Xint by Theori"
},
{
"lang": "en",
"type": "remediation developer",
"value": "Ingela Anderton Andin"
}
],
"dateAssigned": "2026-10-08T15:40:48.000Z",
"descriptions": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003eImproper Certificate Validation vulnerability in the ssl application in Erlang/OTP allows a client that holds a certificate trusted by one SNI virtual host to authenticate to a second SNI virtual host on the same listener by resuming its TLS 1.2 session there.\u003c/p\u003e\n\u003cp\u003eA server that serves several SNI names from one listen socket through \u003ccode\u003esni_fun\u003c/code\u003e or \u003ccode\u003esni_hosts\u003c/code\u003e keeps one pre-TLS 1.3 session cache per listener and does not bind a cached session to the SNI name or client trust policy that established it. When an SNI option set switches to \u003ccode\u003everify_peer\u003c/code\u003e without naming \u003ccode\u003ereuse_sessions\u003c/code\u003e, \u003ccode\u003essl_config:update_options/3\u003c/code\u003e inherits the base listener\u0027s \u003ccode\u003ereuse_sessions\u003c/code\u003e value of true, so the default that disables reuse for client-authenticating servers does not apply. On an abbreviated handshake \u003ccode\u003essl_session:is_resumable/4\u003c/code\u003e checks only that the cached session uses one of the current server certificates and carries some peer certificate, and never revalidates that certificate against the CA selected for the requested name. The names must share a server certificate, which is common with wildcard or multi-SAN certificates. TLS 1.3 does not use this cache and is not affected.\u003c/p\u003e\n\u003cp\u003eThis issue affects OTP from OTP\u0026nbsp;18.0 before OTP\u0026nbsp;27.3.4.19, OTP\u0026nbsp;28.5.0.8, and OTP\u0026nbsp;29.1.2, corresponding to ssl from 7.0 before 11.2.12.14, 11.6.0.7, and 11.7.8.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "Improper Certificate Validation vulnerability in the ssl application in Erlang/OTP allows a client that holds a certificate trusted by one SNI virtual host to authenticate to a second SNI virtual host on the same listener by resuming its TLS 1.2 session there.\n\nA server that serves several SNI names from one listen socket through `sni_fun` or `sni_hosts` keeps one pre-TLS 1.3 session cache per listener and does not bind a cached session to the SNI name or client trust policy that established it. When an SNI option set switches to `verify_peer` without naming `reuse_sessions`, `ssl_config:update_options/3` inherits the base listener\u0027s `reuse_sessions` value of true, so the default that disables reuse for client-authenticating servers does not apply. On an abbreviated handshake `ssl_session:is_resumable/4` checks only that the cached session uses one of the current server certificates and carries some peer certificate, and never revalidates that certificate against the CA selected for the requested name. The names must share a server certificate, which is common with wildcard or multi-SAN certificates. TLS 1.3 does not use this cache and is not affected.\n\nThis issue affects OTP from OTP\u00a018.0 before OTP\u00a027.3.4.19, OTP\u00a028.5.0.8, and OTP\u00a029.1.2, corresponding to ssl from 7.0 before 11.2.12.14, 11.6.0.7, and 11.7.8."
}
],
"value": "Improper Certificate Validation vulnerability in the ssl application in Erlang/OTP allows a client that holds a certificate trusted by one SNI virtual host to authenticate to a second SNI virtual host on the same listener by resuming its TLS 1.2 session there.\n\nA server that serves several SNI names from one listen socket through sni_fun or sni_hosts keeps one pre-TLS 1.3 session cache per listener and does not bind a cached session to the SNI name or client trust policy that established it. When an SNI option set switches to verify_peer without naming reuse_sessions, ssl_config:update_options/3 inherits the base listener\u0027s reuse_sessions value of true, so the default that disables reuse for client-authenticating servers does not apply. On an abbreviated handshake ssl_session:is_resumable/4 checks only that the cached session uses one of the current server certificates and carries some peer certificate, and never revalidates that certificate against the CA selected for the requested name. The names must share a server certificate, which is common with wildcard or multi-SAN certificates. TLS 1.3 does not use this cache and is not affected.\n\nThis issue affects OTP from OTP\u00a018.0 before OTP\u00a027.3.4.19, OTP\u00a028.5.0.8, and OTP\u00a029.1.2, corresponding to ssl from 7.0 before 11.2.12.14, 11.6.0.7, and 11.7.8."
}
],
"impacts": [
{
"capecId": "CAPEC-114",
"descriptions": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003eA client holding a certificate issued by the lower-trust SNI name\u0027s CA connects to the higher-trust name as a fully authenticated mTLS peer and obtains whatever that service grants on a successful client-certificate handshake. Multi-tenant or partner-facing listeners that separate trust domains by SNI name are affected.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "A client holding a certificate issued by the lower-trust SNI name\u0027s CA connects to the higher-trust name as a fully authenticated mTLS peer and obtains whatever that service grants on a successful client-certificate handshake. Multi-tenant or partner-facing listeners that separate trust domains by SNI name are affected."
}
],
"value": "A client holding a certificate issued by the lower-trust SNI name\u0027s CA connects to the higher-trust name as a fully authenticated mTLS peer and obtains whatever that service grants on a successful client-certificate handshake. Multi-tenant or partner-facing listeners that separate trust domains by SNI name are affected."
}
]
}
],
"metrics": [
{
"cvssV4_0": {
"Automatable": "NOT_DEFINED",
"Recovery": "NOT_DEFINED",
"Safety": "NOT_DEFINED",
"attackComplexity": "LOW",
"attackRequirements": "PRESENT",
"attackVector": "NETWORK",
"baseScore": 6.3,
"baseSeverity": "MEDIUM",
"privilegesRequired": "NONE",
"providerUrgency": "NOT_DEFINED",
"subAvailabilityImpact": "NONE",
"subConfidentialityImpact": "NONE",
"subIntegrityImpact": "NONE",
"userInteraction": "NONE",
"valueDensity": "NOT_DEFINED",
"vectorString": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N",
"version": "4.0",
"vulnAvailabilityImpact": "NONE",
"vulnConfidentialityImpact": "LOW",
"vulnIntegrityImpact": "LOW",
"vulnerabilityResponseEffort": "NOT_DEFINED"
},
"format": "CVSS",
"scenarios": [
{
"lang": "en",
"value": "GENERAL"
}
]
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-295",
"description": "CWE-295 Improper Certificate Validation",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-10-14T16:55:51.495Z",
"orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"shortName": "EEF"
},
"references": [
{
"name": "GitHub Advisory",
"tags": [
"related",
"vendor-advisory"
],
"url": "https://github.com/erlang/otp/security/advisories/GHSA-2g23-cgjh-34gq"
},
{
"name": "EEF CNA record for CVE-2026-105236",
"tags": [
"related"
],
"url": "https://cna.erlef.org/cves/CVE-2026-105236.html"
},
{
"name": "OSV record EEF-CVE-2026-105236",
"tags": [
"related"
],
"url": "https://osv.dev/vulnerability/EEF-CVE-2026-105236"
},
{
"name": "Erlang/OTP version ordering",
"tags": [
"x_version-scheme"
],
"url": "https://www.erlang.org/doc/system/versions.html#order-of-versions"
},
{
"name": "Introducing commit 4fe38c4 in erlang/otp",
"tags": [
"related"
],
"url": "https://github.com/erlang/otp/commit/4fe38c4b8b2c8024afb60990e598ff823743fd54"
},
{
"name": "Fix commit 0c20f36 in erlang/otp",
"tags": [
"patch"
],
"url": "https://github.com/erlang/otp/commit/0c20f3683b3c81fd30b46047195be9b3d2c69d76"
},
{
"name": "Fix commit 4d76adb in erlang/otp",
"tags": [
"patch"
],
"url": "https://github.com/erlang/otp/commit/4d76adbf39d547a931048544df09c3c087e56618"
},
{
"name": "Fix commit 10ee925 in erlang/otp",
"tags": [
"patch"
],
"url": "https://github.com/erlang/otp/commit/10ee925d0fee8413f1170cc21bfcf123c8d22454"
}
],
"source": {
"discovery": "EXTERNAL"
},
"title": "TLS 1.2 session resumption in ssl crosses SNI virtual-host client-certificate trust boundaries",
"workarounds": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cul\u003e\n\u003cli\u003eSet \u003ccode\u003e{reuse_sessions, false}\u003c/code\u003e in each SNI service\u0027s option set, or in the base listener options. The reporter\u0027s proof of concept confirmed that this blocks the cross-name resumption.\u003c/li\u003e\n\u003cli\u003eServe distinct SNI trust domains on separate listen sockets, which gives each its own session cache.\u003c/li\u003e\n\u003cli\u003eRestrict the listener to TLS 1.3 with \u003ccode\u003e{versions, [\u0027tlsv1.3\u0027]}\u003c/code\u003e. TLS 1.3 does not use the session-ID cache.\u003c/li\u003e\n\u003c/ul\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "- Set `{reuse_sessions, false}` in each SNI service\u0027s option set, or in the base listener options. The reporter\u0027s proof of concept confirmed that this blocks the cross-name resumption.\n- Serve distinct SNI trust domains on separate listen sockets, which gives each its own session cache.\n- Restrict the listener to TLS 1.3 with `{versions, [\u0027tlsv1.3\u0027]}`. TLS 1.3 does not use the session-ID cache."
}
],
"value": "* Set {reuse_sessions, false} in each SNI service\u0027s option set, or in the base listener options. The reporter\u0027s proof of concept confirmed that this blocks the cross-name resumption.\n* Serve distinct SNI trust domains on separate listen sockets, which gives each its own session cache.\n* Restrict the listener to TLS 1.3 with {versions, [\u0027tlsv1.3\u0027]}. TLS 1.3 does not use the session-ID cache."
}
],
"x_proofOfConcept": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003col\u003e\n\u003cli\u003eCreate one server certificate with subject alternative names \u003ccode\u003elow.test\u003c/code\u003e and \u003ccode\u003ehigh.test\u003c/code\u003e, two self-signed client CAs (\u003ccode\u003elow-ca\u003c/code\u003e, \u003ccode\u003ehigh-ca\u003c/code\u003e), and one client certificate signed by \u003ccode\u003elow-ca\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eStart an \u003ccode\u003essl:listen/2\u003c/code\u003e server on TLS 1.2 with \u003ccode\u003e{verify, verify_none}\u003c/code\u003e and an \u003ccode\u003esni_fun\u003c/code\u003e that returns \u003ccode\u003e{verify, verify_peer}\u003c/code\u003e, \u003ccode\u003e{fail_if_no_peer_cert, true}\u003c/code\u003e and the \u003ccode\u003elow-ca\u003c/code\u003e or \u003ccode\u003ehigh-ca\u003c/code\u003e \u003ccode\u003ecacertfile\u003c/code\u003e for the respective name. Do not set \u003ccode\u003ereuse_sessions\u003c/code\u003e anywhere.\u003c/li\u003e\n\u003cli\u003eConnect with \u003ccode\u003eopenssl s_client -servername high.test -tls1_2 -no_ticket\u003c/code\u003e using the low client certificate. The server rejects the handshake with \u003ccode\u003eunknown_ca\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eConnect with \u003ccode\u003e-servername low.test\u003c/code\u003e and the same certificate, saving the session with \u003ccode\u003e-sess_out\u003c/code\u003e. The handshake succeeds.\u003c/li\u003e\n\u003cli\u003eConnect with \u003ccode\u003e-servername high.test\u003c/code\u003e and \u003ccode\u003e-sess_in\u003c/code\u003e pointing at the saved session. The server resumes the session, \u003ccode\u003essl:connection_information/2\u003c/code\u003e reports \u003ccode\u003esession_resumption\u003c/code\u003e true for \u003ccode\u003ehigh.test\u003c/code\u003e, and the application behind the protected name serves the request to a client whose certificate \u003ccode\u003ehigh-ca\u003c/code\u003e never issued.\u003c/li\u003e\n\u003c/ol\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "1. Create one server certificate with subject alternative names `low.test` and `high.test`, two self-signed client CAs (`low-ca`, `high-ca`), and one client certificate signed by `low-ca`.\n2. Start an `ssl:listen/2` server on TLS 1.2 with `{verify, verify_none}` and an `sni_fun` that returns `{verify, verify_peer}`, `{fail_if_no_peer_cert, true}` and the `low-ca` or `high-ca` `cacertfile` for the respective name. Do not set `reuse_sessions` anywhere.\n3. Connect with `openssl s_client -servername high.test -tls1_2 -no_ticket` using the low client certificate. The server rejects the handshake with `unknown_ca`.\n4. Connect with `-servername low.test` and the same certificate, saving the session with `-sess_out`. The handshake succeeds.\n5. Connect with `-servername high.test` and `-sess_in` pointing at the saved session. The server resumes the session, `ssl:connection_information/2` reports `session_resumption` true for `high.test`, and the application behind the protected name serves the request to a client whose certificate `high-ca` never issued."
}
],
"value": "* Create one server certificate with subject alternative names low.test and high.test, two self-signed client CAs (low-ca, high-ca), and one client certificate signed by low-ca.\n* Start an ssl:listen/2 server on TLS 1.2 with {verify, verify_none} and an sni_fun that returns {verify, verify_peer}, {fail_if_no_peer_cert, true} and the low-ca or high-ca cacertfile for the respective name. Do not set reuse_sessions anywhere.\n* Connect with openssl s_client -servername high.test -tls1_2 -no_ticket using the low client certificate. The server rejects the handshake with unknown_ca.\n* Connect with -servername low.test and the same certificate, saving the session with -sess_out. The handshake succeeds.\n* Connect with -servername high.test and -sess_in pointing at the saved session. The server resumes the session, ssl:connection_information/2 reports session_resumption true for high.test, and the application behind the protected name serves the request to a client whose certificate high-ca never issued."
}
],
"x_technicalAnalysis": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003e\u003cstrong\u003e1. One session cache per listener.\u003c/strong\u003e \u003ccode\u003etls_socket:listen/3\u003c/code\u003e starts one pre-TLS 1.3 session-ID tracker per listen socket via \u003ccode\u003esession_id_tracker/2\u003c/code\u003e. Every SNI virtual host served from that socket shares it, and the cached \u003ccode\u003e#session{}\u003c/code\u003e record stores the peer and server certificates but no SNI name and no client trust policy.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. The SNI option set inherits session reuse.\u003c/strong\u003e When the ClientHello carries a server name, \u003ccode\u003essl_gen_statem:handle_sni_extension/2\u003c/code\u003e and \u003ccode\u003ehandle_sni_hostname/2\u003c/code\u003e call \u003ccode\u003eupdate_ssl_options_from_sni/2\u003c/code\u003e, which passes the base listener options as fallback into \u003ccode\u003essl_config:update_options/3\u003c/code\u003e. The base listener\u0027s \u003ccode\u003everify_none\u003c/code\u003e has already produced \u003ccode\u003ereuse_sessions = true\u003c/code\u003e. \u003ccode\u003essl_config:opt_reuse_sessions/3\u003c/code\u003e computes a \u003ccode\u003efalse\u003c/code\u003e default for \u003ccode\u003everify_peer\u003c/code\u003e, but \u003ccode\u003eget_opt_bool/4\u003c/code\u003e finds the inherited value and keeps it when the SNI option list does not mention \u003ccode\u003ereuse_sessions\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Resumption does not revalidate the peer certificate.\u003c/strong\u003e On an abbreviated handshake \u003ccode\u003essl_handshake:select_session/8\u003c/code\u003e calls \u003ccode\u003essl_session:server_select_session/5\u003c/code\u003e, which accepts the cached session when \u003ccode\u003essl_session:is_resumable/4\u003c/code\u003e finds that it is marked resumable, that \u003ccode\u003eis_owncert/2\u003c/code\u003e matches one of the current server certificates, that \u003ccode\u003ereusable_options/2\u003c/code\u003e sees any cached peer certificate when \u003ccode\u003efail_if_no_peer_cert\u003c/code\u003e is set, and that the \u003ccode\u003ereuse_session\u003c/code\u003e fun agrees. Nothing compares the ClientHello server name with the name that created the session, and the cached peer certificate is not validated against the CA selected for the new name.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e4. Result.\u003c/strong\u003e A client whose certificate chains to the lower-trust name\u0027s CA completes a full handshake there, then presents the same session ID with the higher-trust server name. The server resumes the session, reports \u003ccode\u003esession_resumption\u003c/code\u003e as true and the low-trust peer certificate through \u003ccode\u003essl:peercert/1\u003c/code\u003e, and the application behind the protected name sees an authenticated mTLS peer. \u003ccode\u003eis_owncert/2\u003c/code\u003e is why both names must share a server certificate.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "**1. One session cache per listener.** `tls_socket:listen/3` starts one pre-TLS 1.3 session-ID tracker per listen socket via `session_id_tracker/2`. Every SNI virtual host served from that socket shares it, and the cached `#session{}` record stores the peer and server certificates but no SNI name and no client trust policy.\n\n**2. The SNI option set inherits session reuse.** When the ClientHello carries a server name, `ssl_gen_statem:handle_sni_extension/2` and `handle_sni_hostname/2` call `update_ssl_options_from_sni/2`, which passes the base listener options as fallback into `ssl_config:update_options/3`. The base listener\u0027s `verify_none` has already produced `reuse_sessions = true`. `ssl_config:opt_reuse_sessions/3` computes a `false` default for `verify_peer`, but `get_opt_bool/4` finds the inherited value and keeps it when the SNI option list does not mention `reuse_sessions`.\n\n**3. Resumption does not revalidate the peer certificate.** On an abbreviated handshake `ssl_handshake:select_session/8` calls `ssl_session:server_select_session/5`, which accepts the cached session when `ssl_session:is_resumable/4` finds that it is marked resumable, that `is_owncert/2` matches one of the current server certificates, that `reusable_options/2` sees any cached peer certificate when `fail_if_no_peer_cert` is set, and that the `reuse_session` fun agrees. Nothing compares the ClientHello server name with the name that created the session, and the cached peer certificate is not validated against the CA selected for the new name.\n\n**4. Result.** A client whose certificate chains to the lower-trust name\u0027s CA completes a full handshake there, then presents the same session ID with the higher-trust server name. The server resumes the session, reports `session_resumption` as true and the low-trust peer certificate through `ssl:peercert/1`, and the application behind the protected name sees an authenticated mTLS peer. `is_owncert/2` is why both names must share a server certificate."
}
],
"value": "1. One session cache per listener. tls_socket:listen/3 starts one pre-TLS 1.3 session-ID tracker per listen socket via session_id_tracker/2. Every SNI virtual host served from that socket shares it, and the cached #session{} record stores the peer and server certificates but no SNI name and no client trust policy.\n\n2. The SNI option set inherits session reuse. When the ClientHello carries a server name, ssl_gen_statem:handle_sni_extension/2 and handle_sni_hostname/2 call update_ssl_options_from_sni/2, which passes the base listener options as fallback into ssl_config:update_options/3. The base listener\u0027s verify_none has already produced reuse_sessions = true. ssl_config:opt_reuse_sessions/3 computes a false default for verify_peer, but get_opt_bool/4 finds the inherited value and keeps it when the SNI option list does not mention reuse_sessions.\n\n3. Resumption does not revalidate the peer certificate. On an abbreviated handshake ssl_handshake:select_session/8 calls ssl_session:server_select_session/5, which accepts the cached session when ssl_session:is_resumable/4 finds that it is marked resumable, that is_owncert/2 matches one of the current server certificates, that reusable_options/2 sees any cached peer certificate when fail_if_no_peer_cert is set, and that the reuse_session fun agrees. Nothing compares the ClientHello server name with the name that created the session, and the cached peer certificate is not validated against the CA selected for the new name.\n\n4. Result. A client whose certificate chains to the lower-trust name\u0027s CA completes a full handshake there, then presents the same session ID with the higher-trust server name. The server resumes the session, reports session_resumption as true and the low-trust peer certificate through ssl:peercert/1, and the application behind the protected name sees an authenticated mTLS peer. is_owncert/2 is why both names must share a server certificate."
}
]
}
},
"cveMetadata": {
"assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"assignerShortName": "EEF",
"cveId": "CVE-2026-105236",
"datePublished": "2026-10-14T16:55:51.495Z",
"dateReserved": "2026-10-08T14:45:01.411Z",
"dateUpdated": "2026-10-14T16:55:51.495Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2",
"vulnerability-lookup:meta": {
"epss": {
"cve": "CVE-2026-105236",
"date": "2026-10-16",
"epss": "0.00363",
"percentile": "0.28583"
},
"microsoft_vex": {
"current_release_date": "2026-10-16T01:06:47.000Z",
"cve": "CVE-2026-105236",
"id": "msrc_CVE-2026-105236",
"initial_release_date": "2026-10-16T01:06:47.000Z",
"product_status:known_affected": "1",
"source": "Microsoft CSAF VEX",
"status": "final",
"title": "TLS 1.2 session resumption in ssl crosses SNI virtual-host client-certificate trust boundaries",
"url": "https://msrc.microsoft.com/csaf/vex/2026/msrc_cve-2026-105236.json",
"version": "1"
},
"nvd": {
"cve": {
"affected": [
{
"affectedData": [
{
"cpes": [
"cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"ssl_gen_statem",
"ssl_config",
"ssl_session",
"ssl_handshake"
],
"packageName": "otp",
"packageURL": "pkg:software-id/erlang.org/otp",
"product": "OTP",
"programFiles": [
"lib/ssl/src/ssl_gen_statem.erl",
"lib/ssl/src/ssl_config.erl",
"lib/ssl/src/ssl_session.erl",
"lib/ssl/src/ssl_handshake.erl"
],
"programRoutines": [
{
"name": "ssl_gen_statem:handle_sni_extension/2"
},
{
"name": "ssl_gen_statem:handle_sni_hostname/2"
},
{
"name": "ssl_gen_statem:update_ssl_options_from_sni/2"
},
{
"name": "ssl_gen_statem:fallback_options/2"
},
{
"name": "ssl_config:update_options/3"
},
{
"name": "ssl_config:opt_reuse_sessions/3"
},
{
"name": "ssl_session:server_select_session/5"
},
{
"name": "ssl_session:is_resumable/4"
},
{
"name": "ssl_session:reusable_options/2"
},
{
"name": "ssl_handshake:select_session/8"
}
],
"vendor": "Erlang",
"versions": [
{
"changes": [
{
"at": "27.3.4.19",
"status": "unaffected"
},
{
"at": "28.5.0.8",
"status": "unaffected"
},
{
"at": "29.1.2",
"status": "unaffected"
}
],
"lessThan": "*",
"status": "affected",
"version": "18.0",
"versionType": "otp"
}
]
},
{
"cpes": [
"cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"ssl_gen_statem",
"ssl_config",
"ssl_session",
"ssl_handshake"
],
"packageName": "ssl",
"packageURL": "pkg:otp/ssl?repository_url=https:%2F%2Fgithub.com%2Ferlang%2Fotp\u0026vcs_url=git%2Bhttps:%2F%2Fgithub.com%2Ferlang%2Fotp.git",
"product": "OTP",
"programFiles": [
"src/ssl_gen_statem.erl",
"src/ssl_config.erl",
"src/ssl_session.erl",
"src/ssl_handshake.erl"
],
"programRoutines": [
{
"name": "ssl_gen_statem:handle_sni_extension/2"
},
{
"name": "ssl_gen_statem:handle_sni_hostname/2"
},
{
"name": "ssl_gen_statem:update_ssl_options_from_sni/2"
},
{
"name": "ssl_gen_statem:fallback_options/2"
},
{
"name": "ssl_config:update_options/3"
},
{
"name": "ssl_config:opt_reuse_sessions/3"
},
{
"name": "ssl_session:server_select_session/5"
},
{
"name": "ssl_session:is_resumable/4"
},
{
"name": "ssl_session:reusable_options/2"
},
{
"name": "ssl_handshake:select_session/8"
}
],
"repo": "https://github.com/erlang/otp",
"vendor": "Erlang",
"versions": [
{
"changes": [
{
"at": "11.2.12.14",
"status": "unaffected"
},
{
"at": "11.6.0.7",
"status": "unaffected"
},
{
"at": "11.7.8",
"status": "unaffected"
}
],
"lessThan": "*",
"status": "affected",
"version": "7.0",
"versionType": "otp"
}
]
},
{
"collectionURL": "https://github.com",
"cpes": [
"cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"ssl_gen_statem",
"ssl_config",
"ssl_session",
"ssl_handshake"
],
"packageName": "erlang/otp",
"packageURL": "pkg:github/erlang/otp",
"product": "OTP",
"programFiles": [
"lib/ssl/src/ssl_gen_statem.erl",
"lib/ssl/src/ssl_config.erl",
"lib/ssl/src/ssl_session.erl",
"lib/ssl/src/ssl_handshake.erl"
],
"programRoutines": [
{
"name": "ssl_gen_statem:handle_sni_extension/2"
},
{
"name": "ssl_gen_statem:handle_sni_hostname/2"
},
{
"name": "ssl_gen_statem:update_ssl_options_from_sni/2"
},
{
"name": "ssl_gen_statem:fallback_options/2"
},
{
"name": "ssl_config:update_options/3"
},
{
"name": "ssl_config:opt_reuse_sessions/3"
},
{
"name": "ssl_session:server_select_session/5"
},
{
"name": "ssl_session:is_resumable/4"
},
{
"name": "ssl_session:reusable_options/2"
},
{
"name": "ssl_handshake:select_session/8"
}
],
"repo": "https://github.com/erlang/otp",
"vendor": "Erlang",
"versions": [
{
"changes": [
{
"at": "0c20f3683b3c81fd30b46047195be9b3d2c69d76",
"status": "unaffected"
},
{
"at": "4d76adbf39d547a931048544df09c3c087e56618",
"status": "unaffected"
},
{
"at": "10ee925d0fee8413f1170cc21bfcf123c8d22454",
"status": "unaffected"
}
],
"lessThan": "*",
"status": "affected",
"version": "4fe38c4b8b2c8024afb60990e598ff823743fd54",
"versionType": "git"
}
]
}
],
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db"
}
],
"cveTags": [],
"descriptions": [
{
"lang": "en",
"value": "Improper Certificate Validation vulnerability in the ssl application in Erlang/OTP allows a client that holds a certificate trusted by one SNI virtual host to authenticate to a second SNI virtual host on the same listener by resuming its TLS 1.2 session there.\n\nA server that serves several SNI names from one listen socket through sni_fun or sni_hosts keeps one pre-TLS 1.3 session cache per listener and does not bind a cached session to the SNI name or client trust policy that established it. When an SNI option set switches to verify_peer without naming reuse_sessions, ssl_config:update_options/3 inherits the base listener\u0027s reuse_sessions value of true, so the default that disables reuse for client-authenticating servers does not apply. On an abbreviated handshake ssl_session:is_resumable/4 checks only that the cached session uses one of the current server certificates and carries some peer certificate, and never revalidates that certificate against the CA selected for the requested name. The names must share a server certificate, which is common with wildcard or multi-SAN certificates. TLS 1.3 does not use this cache and is not affected.\n\nThis issue affects OTP from OTP\u00a018.0 before OTP\u00a027.3.4.19, OTP\u00a028.5.0.8, and OTP\u00a029.1.2, corresponding to ssl from 7.0 before 11.2.12.14, 11.6.0.7, and 11.7.8."
}
],
"id": "CVE-2026-105236",
"lastModified": "2026-10-15T14:45:10.783",
"metrics": {
"cvssMetricV40": [
{
"cvssData": {
"Automatable": "NOT_DEFINED",
"Recovery": "NOT_DEFINED",
"Safety": "NOT_DEFINED",
"attackComplexity": "LOW",
"attackRequirements": "PRESENT",
"attackVector": "NETWORK",
"availabilityRequirement": "NOT_DEFINED",
"baseScore": 6.3,
"baseSeverity": "MEDIUM",
"confidentialityRequirement": "NOT_DEFINED",
"exploitMaturity": "NOT_DEFINED",
"integrityRequirement": "NOT_DEFINED",
"modifiedAttackComplexity": "NOT_DEFINED",
"modifiedAttackRequirements": "NOT_DEFINED",
"modifiedAttackVector": "NOT_DEFINED",
"modifiedPrivilegesRequired": "NOT_DEFINED",
"modifiedSubAvailabilityImpact": "NOT_DEFINED",
"modifiedSubConfidentialityImpact": "NOT_DEFINED",
"modifiedSubIntegrityImpact": "NOT_DEFINED",
"modifiedUserInteraction": "NOT_DEFINED",
"modifiedVulnAvailabilityImpact": "NOT_DEFINED",
"modifiedVulnConfidentialityImpact": "NOT_DEFINED",
"modifiedVulnIntegrityImpact": "NOT_DEFINED",
"privilegesRequired": "NONE",
"providerUrgency": "NOT_DEFINED",
"subAvailabilityImpact": "NONE",
"subConfidentialityImpact": "NONE",
"subIntegrityImpact": "NONE",
"userInteraction": "NONE",
"valueDensity": "NOT_DEFINED",
"vectorString": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"version": "4.0",
"vulnAvailabilityImpact": "NONE",
"vulnConfidentialityImpact": "LOW",
"vulnIntegrityImpact": "LOW",
"vulnerabilityResponseEffort": "NOT_DEFINED"
},
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"type": "Secondary"
}
]
},
"published": "2026-10-14T17:18:14.720",
"references": [
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://cna.erlef.org/cves/CVE-2026-105236.html"
},
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://github.com/erlang/otp/commit/0c20f3683b3c81fd30b46047195be9b3d2c69d76"
},
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://github.com/erlang/otp/commit/10ee925d0fee8413f1170cc21bfcf123c8d22454"
},
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://github.com/erlang/otp/commit/4d76adbf39d547a931048544df09c3c087e56618"
},
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://github.com/erlang/otp/commit/4fe38c4b8b2c8024afb60990e598ff823743fd54"
},
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://github.com/erlang/otp/security/advisories/GHSA-2g23-cgjh-34gq"
},
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://osv.dev/vulnerability/EEF-CVE-2026-105236"
},
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://www.erlang.org/doc/system/versions.html#order-of-versions"
}
],
"sourceIdentifier": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"vulnStatus": "Deferred",
"weaknesses": [
{
"description": [
{
"lang": "en",
"value": "CWE-295"
}
],
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"type": "Secondary"
}
]
}
}
}
}
Loading…
Loading…
Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.
Experimental. This forecast is provided for visualization only and may change without notice. Do not use it for operational decisions.
Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline
Sightings and recorded dates, grouped by day · UTC
Loading timeline…
Loading…
Loading…
The MITRE ATT&CK techniques below are AI-generated suggestions, inferred from the description of the
vulnerability by the CIRCL/vulnerability-attack-technique-classification-roberta-base
model, served locally by ML-Gateway.
They have not been verified by an analyst and are provided for guidance only.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Loading…
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.