Safeguard
Vulnerability Analysis

Log4j second RCE bypass (CVE-2021-45046)

The Log4j 2.15.0 patch for Log4Shell was incomplete. CVE-2021-45046 shows how attackers bypassed it to achieve remote code execution.

Nayan Dey
Security Researcher
7 min read

On December 14, 2021, five days after the world scrambled to patch Log4Shell (CVE-2021-44228), the Apache Logging Services team published a second CVE for Apache Log4j2: CVE-2021-45046. The initial fix shipped in Log4j 2.15.0 turned out to be incomplete — under non-default configurations, attackers could still achieve remote code execution (RCE), and in nearly all configurations they could trigger information leakage or denial of service. What started as a "low severity" advisory on December 14 was revised to CVSS 9.0 (Critical) within 24 hours once researchers demonstrated working RCE exploit paths, turning a five-day-old crisis into a rolling one.

CVE-2021-45046 matters not just because of its own impact, but because of what it represents: the first fix for a headline vulnerability was rushed, scoped too narrowly, and bypassed almost immediately. Security teams that considered the incident "closed" after patching to 2.15.0 were, in fact, still exposed.

Affected Versions and Components

CVE-2021-45046 affects Apache Log4j2 versions 2.0-beta9 through 2.15.0, excluding 2.12.2 (a backport for Java 7 environments that was itself later found vulnerable to a related issue, CVE-2021-44832). In practical terms, if an application had already been "patched" to Log4j 2.15.0 in response to Log4Shell, it remained exploitable under this CVE.

The vulnerable component is the same core logging engine implicated in the original Log4Shell disclosure: Log4j's lookup mechanism, specifically its handling of JNDI (Java Naming and Directory Interface) lookups embedded in log messages and in the Thread Context Map (MDC). Because Log4j2 is one of the most widely embedded logging libraries in the Java ecosystem — pulled in transitively through Spring Boot, Elasticsearch, Apache Struts, countless internal Java services, and third-party SDKs — the affected footprint largely overlapped with, and extended, the original Log4Shell blast radius.

CVSS, EPSS, and KEV Context

  • CVSS: NVD initially scored CVE-2021-45046 at 3.7 (Low), reflecting an assessment that the flaw only enabled denial of service. That score was revised upward to 9.0 (Critical) on December 15, 2021, once it was confirmed that non-default logging configurations using a Pattern Layout with a Context Lookup (for example, ${ctx:loginId}) or a Thread Context Map pattern (%X, %mdc, or %MDC) could be abused to achieve full remote code execution, not just DoS.
  • EPSS: Given its lineage to Log4Shell — one of the most actively and broadly exploited vulnerabilities in recent memory — CVE-2021-45046 has consistently scored in the highest percentiles for Exploit Prediction Scoring System (EPSS), reflecting sustained real-world scanning and exploitation activity well after initial disclosure. Log4j-family CVEs remain some of the most frequently probed vulnerabilities in internet-wide scanning telemetry to this day.
  • KEV: The Log4j vulnerability cluster, including CVE-2021-45046, is represented in CISA's Known Exploited Vulnerabilities (KEV) catalog, which mandates remediation deadlines for U.S. federal agencies and is widely used as a prioritization signal by security teams industry-wide.

The severity revision itself is a useful case study: the initial "Low" rating led some organizations to deprioritize a second patch cycle, under the assumption that 2.15.0 was sufficient and any residual issue was a minor DoS. That assumption proved costly once RCE was confirmed.

Timeline

  • December 9, 2021 — CVE-2021-44228 ("Log4Shell") publicly disclosed. Log4j 2.15.0 released as the fix, disabling JNDI lookups by default and introducing the log4j2.formatMsgNoLookups flag.
  • December 10–13, 2021 — Mass exploitation of Log4Shell begins across the internet; organizations race to patch or mitigate. Independent researchers continue probing the 2.15.0 fix.
  • December 14, 2021 — Apache discloses CVE-2021-45046, initially rated Low (3.7), describing a scenario where non-default configurations remained vulnerable to a Denial of Service condition via crafted Thread Context Map input.
  • December 15, 2021 — After public proof-of-concept exploits demonstrate full remote code execution (not just DoS) under certain non-default logging configurations, NVD and Apache revise the CVSS score to 9.0 Critical. Log4j 2.16.0 is released, removing message lookups entirely and disabling JNDI functionality by default at a deeper level.
  • December 17, 2021 — CVE-2021-45105 (uncontrolled recursion / DoS) disclosed; Log4j 2.17.0 released.
  • December 28, 2021 — CVE-2021-44832 (RCE via JDBC Appender, requiring attacker control of logging configuration) disclosed; Log4j 2.17.1 released, closing out the disclosure cluster.

In under three weeks, the Log4j ecosystem cycled through four distinct CVEs and five release versions — an unusually compressed remediation timeline that forced security and engineering teams to repeatedly re-verify "patched" systems.

Why the First Fix Wasn't Enough

Log4j 2.15.0 attempted to close Log4Shell by restricting JNDI lookups in the message-formatting path and by defaulting log4j2.formatMsgNoLookups to true. This addressed the most common exploitation pattern: attacker-controlled strings (like a User-Agent header) flowing directly into a logged message that Log4j would evaluate as a lookup expression.

What 2.15.0 missed was that lookups could also be triggered through the Thread Context Map (MDC) and Context Lookups in a Pattern Layout, which were not fully covered by the same restriction. If an application's logging configuration used patterns like %X, %mdc, %MDC, or ${ctx:...} — common in applications that enrich log lines with contextual, user-supplied data such as session IDs, usernames, or request parameters — an attacker who could influence that contextual data could still smuggle a malicious JNDI lookup string past the 2.15.0 mitigation and reach the same vulnerable lookup evaluation logic that powered the original Log4Shell RCE.

This is the crux of why CVE-2021-45046 is often described as a "bypass": it wasn't a new vulnerability class, it was a fix that patched one input path while leaving a structurally similar, less obvious path open. Log4j 2.16.0 closed the gap definitively by removing the Message Lookup feature entirely and disabling JNDI-based lookups at the source, rather than filtering specific input paths.

Remediation Steps

  1. Upgrade, don't just patch to 2.15.0. Update Log4j2 to 2.17.1 (or the current maintained release), which incorporates fixes for all four CVEs in this disclosure cluster. For legacy Java 7 environments, use the corresponding backport line, and verify it addresses CVE-2021-44832 as well as CVE-2021-45046.
  2. Re-audit systems patched immediately after Log4Shell. Any system updated only to 2.15.0 between December 9 and December 14, 2021 should be treated as still exposed and re-patched. This is the single most common gap teams miss when they consider the incident "closed" after the first response wave.
  3. Inventory Thread Context Map / MDC usage. Search logging configurations for %X, %mdc, %MDC patterns and ${ctx:...} Context Lookups, and confirm whether attacker-influenced data (session identifiers, headers, form fields, usernames) flows into those context values. This is the specific configuration shape CVE-2021-45046 requires to be exploitable.
  4. Remove the JndiLookup class as a compensating control where immediate upgrade isn't feasible: zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class. This is effective against both the original Log4Shell path and the Context Lookup bypass, since it removes the underlying JNDI capability rather than filtering specific inputs.
  5. Apply egress filtering. Restrict outbound LDAP, RMI, and DNS connections from application servers to only known-necessary destinations, limiting the impact of any residual or future lookup-based exploitation, including variants not yet disclosed.
  6. Confirm coverage across transitive dependencies, not just direct declarations. Log4j2 is frequently pulled in indirectly through frameworks and third-party libraries; a mvn dependency:tree or equivalent SBOM-based check is necessary to catch versions bundled inside other artifacts.

How Safeguard Helps

The Log4j disclosure cluster is the clearest recent example of why "patched" is not the same as "not exploitable" — and why point-in-time patching without continuous verification leaves organizations exposed to bypasses like CVE-2021-45046. Safeguard's reachability analysis determines whether the specific vulnerable code paths — Thread Context Map lookups, Context Lookup patterns, JNDI resolution — are actually invoked in your application's runtime call graph, so teams can distinguish genuinely exploitable instances from theoretical ones and prioritize accordingly. Griffin AI continuously correlates newly disclosed CVEs, including fix-bypass follow-ups like this one, against your deployed software inventory, flagging when a previously "resolved" finding needs re-evaluation because the underlying advisory changed. Safeguard's SBOM generation and ingestion capabilities give you accurate, continuously updated visibility into every transitive Log4j2 instance across your services, including those bundled inside third-party dependencies where manual dependency:tree audits often miss coverage. When a fix is available, Safeguard can open auto-fix pull requests that bump affected artifacts to a verified safe version, closing the gap between disclosure and remediation before the next bypass has a chance to surface.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.