CVE-2026-34477
Overview
Apache Log4j 2 is a widely deployed Java logging framework whose Socket, Syslog, and SMTP appenders can wrap their TCP transport in TLS via a nested <Ssl> configuration element. The <Ssl> element exposes a verifyHostName attribute (introduced in 2.12.0) that operators set to true to require the receiver's certificate identity to match the configured peer host during the TLS handshake.
A medium-severity vulnerability (CVE-2026-34477, classified as CWE-297 Improper Validation of Certificate with Host Mismatch) has been identified in SslConfiguration, the plugin class that backs the <Ssl> element. The @PluginFactory annotation that the Log4j plugin loader uses to bind XML attributes to factory parameters was placed on the legacy 3-argument createSSLConfiguration(...) factory, which hard-codes verifyHostName=false, while the 4-argument variant that actually honors the verifyHostName attribute carried no @PluginFactory annotation and was therefore never selected by the plugin system. The result was that every <Ssl verifyHostName="true"> element in every Log4j configuration since 2.12.0 produced an SslConfiguration with verifyHostName=false, and the resulting TLS appenders skipped hostname verification on the handshake. A network attacker who can intercept traffic and present any certificate signed by a CA in the appender's trust store can transparently impersonate the log receiver and capture or forge the entire log stream.
This is the incomplete-fix follow-up to CVE-2025-68161. The 2.25.3 patch enabled hostname verification only when the log4j2.sslVerifyHostName JVM system property was set; it never touched the <Ssl verifyHostName="true"> attribute path, so the documented configuration mechanism remained silently ignored until 2.25.4. The HTTP appender is unaffected because it uses HttpURLConnection and Apache HttpClient and verifies host names by default through a separate verifyHostname attribute on a different code path.
Per OWASP: "A man-in-the-middle (MITM) attack is a general term for when a perpetrator positions himself in a conversation between a user and an application, either to eavesdrop or to impersonate one of the parties." Wiring the user-facing verifyHostName switch to a factory method that hard-codes false makes the documented defense a no-op without any error or warning, which is a particularly dangerous failure mode because operators see the configuration in their log4j2.xml and reasonably assume it is being honored.
The CVSS v4.0 base score is 6.3 (Medium) with vector CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:L/SA:N. Attack complexity is high because the attacker must both be on the network path between the application and the log receiver and hold a CA-trusted certificate; no privileges and no user interaction are required.
This issue affects Apache Log4j 2 versions 2.12.0 through 2.25.3, and pre-release versions 3.0.0-alpha1 through 3.0.0-beta3.
Details
Module Info
- Product: Apache Log4j 2
- Affected packages: log4j-core
- Affected versions: >=2.12.0 <=2.25.3, >=3.0.0-alpha1 <=3.0.0-beta3
- GitHub repository: https://github.com/apache/logging-log4j2
- Published packages: https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-core
- Package manager: Maven
- Fixed in:
- NES for Apache Log4j 2 2.17.x
- OSS Apache Log4j 2 2.25.4
Vulnerability Info
The vulnerability is in org.apache.logging.log4j.core.net.ssl.SslConfiguration in the log4j-core module, specifically in the placement of the @PluginFactory annotation across two overloaded createSSLConfiguration(...) factory methods.
An application is vulnerable when all of the following are true:
- The application uses Apache Log4j 2 between 2.12.0 and 2.25.3 inclusive (or a 3.x pre-release in the affected range)
- The application configures an SMTP, Socket, or Syslog appender with TLS enabled via a nested <Ssl> element (or the equivalent programmatic ConfigurationBuilder.newComponent("Ssl") form)
- The application relies on verifyHostName="true" on that <Ssl> element to require RFC 6125 hostname matching during the handshake
- A network attacker can position themselves between the application and the log receiver and can present a server certificate signed by any CA in the appender's trust store
When those conditions are met, the <Ssl verifyHostName="true"> attribute is silently dropped during plugin instantiation; the resulting SslConfiguration carries verifyHostName=false, and the TLS appender opens connections without hostname verification.
Prior to the fix, SslConfiguration declared two overloaded factories with the @PluginFactory annotation on the wrong one (pre-fix code at rel/2.25.3):
@NullUnmarked
@PluginFactory
public static SslConfiguration createSSLConfiguration(
@PluginAttribute("protocol") final String protocol,
@PluginElement("KeyStore") final KeyStoreConfiguration keyStoreConfig,
@PluginElement("TrustStore") final TrustStoreConfiguration trustStoreConfig) {
return new SslConfiguration(protocol, false, keyStoreConfig, trustStoreConfig);
}
/**
* @since 2.12
*/
@NullUnmarked
public static SslConfiguration createSSLConfiguration(
@PluginAttribute("protocol") final String protocol,
@PluginElement("KeyStore") final KeyStoreConfiguration keyStoreConfig,
@PluginElement("TrustStore") final TrustStoreConfiguration trustStoreConfig,
@PluginAttribute("verifyHostName") final boolean verifyHostName) {
return new SslConfiguration(protocol, verifyHostName, keyStoreConfig, trustStoreConfig);
}
The Log4j plugin loader selects the factory annotated with @PluginFactory and binds its parameters via the @PluginAttribute and @PluginElement annotations on the parameters. Because the annotation sat on the 3-argument variant, the verifyHostName parameter on the 4-argument variant was never wired up. Any <Ssl verifyHostName="true"> configuration produced a 3-argument call that hard-coded verifyHostName=false. Programmatic builds via ConfigurationBuilder.newComponent("Ssl").addAttribute("verifyHostName", "true") failed even more loudly: the configuration produced an internal status-logger error Ssl contains an invalid element or attribute "verifyHostName" because the plugin's published metadata in Log4j2Plugins.dat did not include verifyHostName as a recognized attribute, but the misconfiguration was non-fatal and the connection still came up, just without hostname verification.
The fix is a minimal +4 / -6 adjustment: move @PluginFactory to the 4-argument variant and strip the now-redundant @PluginAttribute and @PluginElement annotations off the 3-argument variant's parameters (since it is no longer the plugin entry point):
@NullUnmarked
public static SslConfiguration createSSLConfiguration(
final String protocol,
final KeyStoreConfiguration keyStoreConfig,
final TrustStoreConfiguration trustStoreConfig) {
return new SslConfiguration(protocol, false, keyStoreConfig, trustStoreConfig);
}
/**
* @since 2.12
*/
@NullUnmarked
@PluginFactory
public static SslConfiguration createSSLConfiguration(
@PluginAttribute("protocol") final String protocol,
@PluginElement("KeyStore") final KeyStoreConfiguration keyStoreConfig,
@PluginElement("TrustStore") final TrustStoreConfiguration trustStoreConfig,
@PluginAttribute("verifyHostName") final boolean verifyHostName) {
return new SslConfiguration(protocol, verifyHostName, keyStoreConfig, trustStoreConfig);
}
After the fix, <Ssl verifyHostName="true"> flows into the constructor's verifyHostName parameter, the resulting SslConfiguration reports isVerifyHostName() == true, and the SslSocketManager (already patched in 2.25.3 for CVE-2025-68161) sets SSLParameters.setEndpointIdentificationAlgorithm("HTTPS") on the socket so the JSSE engine performs RFC 6125 hostname matching during the handshake. New regression tests verifyHostNameFromXml and TlsSocketAppenderTest exercise both the XML and programmatic configuration paths and assert that a certificate whose CN/SAN does not match the configured peer host is rejected.
The defect is as old as the feature: verifyHostName and the 4-argument factory both first shipped in Log4j 2.12.0 (LOG4J2-2819 backport, December 2021), and the 4-argument variant was never @PluginFactory-annotated in any release between 2.12.0 and 2.25.3. The 2.25.0 refactor in PR #2767 ("Fix the reload of key and trust stores on reconfiguration") restructured the constructor signature but preserved the same mis-wiring. The <Ssl> element does not exist in Apache Log4j 1.x, so 1.x lines are not in scope.
Mitigation
Only recent versions of Apache Log4j 2 receive community support and updates. Older versions have no publicly available fixes for this vulnerability.
The HTTP appender is unaffected; only TLS-wrapped SMTP, Socket, and Syslog appenders need attention.
Users of the affected components should apply one of the following mitigations:
- Upgrade to a currently supported version of Apache Log4j 2. The OSS fix ships in Apache Log4j 2.25.4 and later.
- As a temporary measure on 2.25.3, set -Dlog4j2.sslVerifyHostName=true as a JVM argument; this engages the system-property pathway introduced for CVE-2025-68161, which is a separate code branch from the broken <Ssl verifyHostName="true"> attribute parsing and is honored on 2.25.3 and later.
- Restrict the trust store configured for the affected appender to contain only the CA certificates required for the intended log-receiver communication scope, or front the receiver with mutual TLS so client and server cross-authenticate.
- Where feasible, replace the affected appender with the HTTP appender, which uses a separate code path that already verifies host names by default.
- Leverage a commercial support partner like HeroDevs for post-EOL security support through Never-Ending Support (NES) for Apache Log4j 2.
Credits
- Samuli Leinonen (finder)
- Naresh Kandula (independent finder)
- Vitaly Simonovich (independent finder)
- Raijuna (independent finder)
- Danish Siddiqui (djvirus) (independent finder)
- Markus Magnuson (independent finder)
- Haruki Oyama, Waseda University (independent finder)
- Piotr P. Karwasz (ppkarwasz) (fixer)