Skip to content

CVE-2021-44228 — Log4Shell

The Log4j 2.x logger interpolated ${jndi:ldap://...} strings when logging arbitrary content. An attacker who could get a string into a log statement (almost any user-controlled field — User-Agent, search query, username) could trigger an LDAP/RMI lookup, fetch a remote class, and execute it. The naive fix (upgrade to 2.15.0) had a follow-up CVE because the fix was incomplete; the durable fix is 2.17.1+ plus class removal.

Affected versions

  • Log4j-core 2.0-beta9 through 2.14.1 — vulnerable.
  • Log4j-core 2.15.0 — incomplete fix (CVE-2021-45046).
  • Log4j-core 2.16.0 — DoS via uncontrolled recursion (CVE-2021-45105).
  • Log4j-core 2.17.0 — JDBC Appender RCE under specific config (CVE-2021-44832).
  • Log4j-core 2.17.1+ (or 2.12.4 / 2.3.2 for older Java versions) — the durable fix.

log4j-api alone is not vulnerable; the bug is in log4j-core’s pattern-substitution code path.

Indicator-of-exposure

Having log4j-core in the classpath is the necessary condition. Sufficient exposure also requires:

  • A vulnerable version (per the above).
  • A code path that logs untrusted input. In a typical web application, almost every code path qualifies — request headers, paths, bodies are routinely logged.

If the only logging in the application is a fixed string with no user-controlled fields and no exception traces (rare), exposure may be limited. Don’t rely on that for triage; the attacker’s job is to find one untrusted input that makes it to a log line.

Remediation strategy

The full fix is all of:

  1. Upgrade log4j-core to 2.17.1+ (or 2.12.4 / 2.3.2 on older Java versions).
  2. Set the JVM flag -Dlog4j2.formatMsgNoLookups=true (or the LOG4J_FORMAT_MSG_NO_LOOKUPS=true environment variable) for defence-in-depth.
  3. Remove JndiLookup.class from any vendored Log4j JAR that cannot be upgraded.
  4. Audit for log4j-1.x. The 1.x branch has different CVEs (CVE-2019-17571, CVE-2022-23305) and is end-of-life.

Steps 2 and 3 are the mitigation path when the upgrade can’t ship immediately; both are still reasonable defence-in-depth after the upgrade.

The prompt

You are remediating Log4Shell (CVE-2021-44228 / 45046 / 45105
/ 44832) in this repository. Output a PR (or set of PRs, one
per Maven module) or a TRIAGE.md.

## Step 0 — Inventory

1. Locate every `log4j-core` dependency in the dependency
   graph: `pom.xml`, `build.gradle`, `build.sbt`, the lockfile
   if any, and shaded/uber-JAR contents.
2. Record the current version of each. Note any vendored or
   shaded copies.
3. List Log4j 1.x usages separately — they need a different
   fix path (replace with reload4j or upgrade to 2.x).

## Step 1 — Apply the upgrade

For each `log4j-core` reference:

1. Bump the version to 2.17.1+ (or 2.12.4 on Java 7, 2.3.2 on
   Java 6). Match the major-runtime constraint of the project.
2. Bump `log4j-api` to the same version. Mismatch breaks at
   runtime.
3. Run the project's test suite. If tests fail because of
   genuinely-changed behaviour (rare in 2.14 → 2.17), document
   the change in the PR.

## Step 2 — Defence-in-depth

1. Add `-Dlog4j2.formatMsgNoLookups=true` to the JVM args
   in the project's launcher / Dockerfile / deploy
   configuration.
2. For any vendored / shaded JAR that cannot be upgraded
   immediately, remove `JndiLookup.class` from the JAR:
   `zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class`.
3. Verify with `unzip -l log4j-core-*.jar | grep JndiLookup`
   showing no entry.

## Step 3 — Verify

1. Re-run the SBOM / SCA scan against the post-upgrade
   artifact. CVE-2021-44228, -45046, -45105, and -44832 must
   all show as "fixed" or "not present."
2. Run a behaviour test: a log statement that includes
   `${jndi:ldap://example.invalid/x}` must log the literal
   string, not trigger an LDAP request.
3. Confirm the JVM flag is being applied at runtime by
   inspecting `java -XshowSettings:properties` output (or the
   container's startup logs).

## Step 4 — Open the PR

- Branch: `remediate/cve-2021-44228-log4shell`.
- Title: `[Security][CVE-2021-44228] upgrade log4j-core to 2.17.1+`.
- Body must include:
  - CVE summary and link to the advisory.
  - Per-module list of versions bumped.
  - JVM flag changes.
  - Class-removal actions on any vendored JARs.
  - SCA scan output before/after.
  - Behaviour-test result.
  - Rollback plan.
- Label: `sec-auto-remediation`.

## Stop conditions

- A vendored / shaded JAR cannot be safely modified
  (signed, license-restricted). Triage with a note about the
  vendor.
- The application uses Log4j 1.x and replacement requires API
  changes. Triage; the right path is a separate
  Log4j-1-to-2-or-reload4j migration.
- Tests fail in a way that suggests a real behaviour change in
  Log4j 2.17.1+ — read the release notes and document.

## Scope

- Do not bundle other CVE bumps in this PR.
- Do not modify application logging configuration beyond what
  the recipe requires.
- Do not remove `JndiManager` or other classes; only
  `JndiLookup.class` is the fix.

Verification — what the reviewer looks for

  • Both log4j-core and log4j-api are at the same patched version.
  • The JVM flag is applied at the deployment layer (Dockerfile, Helm values, systemd unit), not just in a test config.
  • For vendored JARs: the JndiLookup.class entry is gone. Confirm with unzip -l.
  • The behaviour test in the PR exercised the actual logger path the application uses (not a synthetic logger).
  • The PR did not silently bump unrelated dependencies.

Watch for

  • Shaded uber-JARs. Some applications shade Log4j into a bigger JAR. Bumping the dependency upstream isn’t enough; the shaded copy needs the patched version baked in.
  • Java 6 / Java 7 targets. The patch backports for those runtimes are 2.3.2 and 2.12.4. Do not silently bump to 2.17.1 on a Java 7 build.
  • log4j-1.x. A different bug surface (CVE-2019-17571 deserialization, CVE-2022-23305 SQL appender). The fix is migration to 2.x or reload4j; the recipe above does not cover it.
  • JNDI is broader than Log4j. A Log4j-clean codebase can still have JNDI-injection bugs in other libraries (Spring, H2 Console, JNDI lookups in custom code). This recipe doesn’t catch those.
  • The flag is not enough alone. formatMsgNoLookups=true was a partial mitigation pre-2.16; some pattern-layout configurations still resolved lookups. The upgrade is the durable fix.

Related