Years after Log4Shell, Log4j is still being downloaded at massive scale. In 2025 alone, developers downloaded Log4j nearly 300 million times, including about 40 million vulnerable downloads. One 2026 snapshot found 4.1 million vulnerable Log4j downloads in just seven days.
That is the point. Log4Shell was patched, but the component never went away. It kept moving through builds, dependencies, vendor software, containers, and production applications. And if vulnerable versions are still being downloaded years later, the next crisis will not start from zero. It will start inside code that is already running.
Log4Shell was not a normal vulnerability.
It was a CVSS 10 remote code execution vulnerability in Apache Log4j, one of the most widely used Java logging libraries in the world. It could be triggered by something as simple as a malicious string written into a logged field.
That is what made it so dangerous.
An attacker did not need a complex exploit chain. They did not need access to source code. They did not need to compromise a developer machine first. In many cases, they only needed to get a vulnerable application to log attacker-controlled input.
A username. A user agent. A header. A search field. An error message. Anything that made its way into Log4j.
Then the race began.
Security teams had to answer questions that sounded simple and were not simple at all:
- Where are we using Log4j?
- Which applications are internet-facing?
- Which versions are vulnerable?
- Which dependencies are pulling it in indirectly?
- Which vendors are affected?
- Which systems can we patch now?
- Which systems will break if we patch too quickly?
That is why Log4Shell became more than a patching problem. It became a runtime visibility problem.
This guide covers how to patch the Log4j vulnerability, how to detect Log4j in your environment, why the initial patching process was so messy, and why runtime prevention matters during the window between disclosure and remediation.
Key Takeaways
Log4Shell, tracked as CVE-2021-44228, was a critical remote code execution vulnerability in Apache Log4j caused by unsafe JNDI lookup behavior.
Attackers could exploit Log4j by placing a malicious lookup string into data that an application logged, including headers, form fields, usernames, or other inputs.
The Log4j patching process was complicated because early fixes were incomplete, follow-on CVEs appeared quickly, and vulnerable versions were often buried deep in dependency chains.
The safest remediation path is to upgrade affected Log4j 2.x deployments to a supported fixed version, remove unsafe vulnerable components where upgrade is not immediately possible, and retire unsupported Log4j 1.x usage.
Patching alone leaves a live exposure window. Runtime prevention helps protect applications while teams find, prioritize, patch, test, and redeploy vulnerable code.
What Is Log4Shell and Why It Reached CVSS 10?
Log4Shell is the name given to CVE-2021-44228, a critical remote code execution vulnerability in Apache Log4j 2.
Log4j is a logging library. It is not supposed to be exciting. It records events, errors, requests, application state, and operational data so developers and operations teams can understand what happened inside their software.
That is exactly why Log4Shell was so serious.
Logging sits everywhere.
Applications log usernames. They log headers. They log failed requests. They log API activity. They log authentication events. They log errors. They log inputs that developers never expected to become executable behavior.
In vulnerable versions of Log4j, attackers could abuse JNDI lookup functionality by getting a specially crafted string logged by the application. Once processed, that string could cause the application to reach out to an attacker-controlled server and load or execute malicious code.
A logging event became a code execution path.
That is the entire problem in one sentence.
Log4Shell reached CVSS 10 because it combined several dangerous qualities at once:
- It affected a widely used component.
- It was easy to trigger.
- It could lead to remote code execution.
- It was present in internet-facing applications.
- It was often hidden inside transitive dependencies.
- It was actively exploited quickly.
- It touched both first-party applications and third-party software.
For security teams, the challenge was not only “patch Log4j.” The challenge was finding every place Log4j existed, proving which applications were actually exposed, and protecting production systems before remediation could finish.
How Attackers Exploited Log4j Before a Patch Existed
The basic exploit path was simple.
An attacker sent a request containing a malicious lookup string. The vulnerable application logged that string. Log4j processed the lookup. The application reached out to a remote endpoint controlled by the attacker. That path could lead to code execution.
The exact payloads changed quickly. Attackers obfuscated strings. They tried different headers. They used different protocols. They scanned the internet for exposed systems. They looked for any input field that might be logged somewhere inside the application.
That is what made the vulnerability so difficult to contain.
Security teams could block one pattern. Attackers could modify the payload.
Teams could update one application. Another application might still include a vulnerable transitive dependency.
A vendor could issue an advisory. Another vendor might still be investigating whether they were affected.
The attack surface was not limited to code a team wrote directly.
It included frameworks, libraries, packaged software, SaaS platforms, vendor appliances, internal tools, and Java applications that had been running quietly for years.
This is where Log4Shell exposed a structural weakness in application security.
Most organizations had a list of assets. Fewer had a live map of which applications were running which libraries, which vulnerable functions were reachable, and which runtime paths could be triggered from the outside.
That gap matters.
Because when exploitation starts before patching is complete, inventory is not enough.
You need to know what is actually exposed and executing.
How to Detect Log4j in Your Environment
Before you can patch Log4j, you have to find it.
That sounds obvious, but during Log4Shell, it was the hardest part for many organizations.
Log4j could appear as a direct dependency in a Java application. It could also appear as a transitive dependency pulled in by another library. It could be packaged inside a JAR, WAR, EAR, container image, vendor application, or legacy service. It could exist in a build artifact that no one had touched in years.
The goal is not only to find “Log4j exists somewhere.”
The goal is to determine where vulnerable Log4j exists, whether it is loaded, whether it is reachable, and whether it is running in an environment attackers can access.
Check Dependency Manifests
Start with dependency files and build manifests.
For Maven, check pom.xml files and dependency trees.
For Gradle, check build.gradle, lockfiles, and resolved dependency output.
For packaged Java applications, inspect JAR, WAR, and EAR files for Log4j components, especially log4j-core.
Do not stop at direct dependencies. Log4j may be pulled in indirectly by frameworks, internal packages, or third-party libraries.
Scan Container Images and Build Artifacts
Log4j often lives inside container images and packaged application artifacts.
Scan images in registries, running containers, CI/CD artifacts, and deployed workloads. Look for vulnerable Log4j versions inside application layers, embedded JARs, and bundled libraries.
This matters because source code repositories do not always reflect what is actually running in production.
The deployed artifact is the thing attackers reach.
Review Vendor and Third-Party Software
Many Log4Shell exposures were not in code owned by the affected organization.
They were in vendor products, appliances, SaaS integrations, internal platforms, and third-party tools.
That makes vendor disclosure tracking critical. Security teams should review vendor advisories, confirm affected versions, validate patch availability, and track compensating controls for systems that cannot be immediately updated.
A vendor saying “we are investigating” is not protection.
Use Runtime Application Detection
Static scanning tells you where Log4j may exist.
Runtime application detection tells you where Log4j is actually loaded and executing.
That distinction matters.
A vulnerable library buried in an unused build artifact is not the same as a vulnerable library loaded inside an internet-facing production service. Both may need remediation, but they do not carry the same urgency.
Runtime visibility helps teams prioritize based on what is real in production:
- Is the vulnerable package present?
- Is it loaded?
- Is the vulnerable function reachable?
- Is the application internet-facing?
- Has anything attempted to trigger the vulnerable path?
- Which service, container, pod, node, workload, and application owner are involved?
That is the difference between a long vulnerability list and an actionable response plan.
The Log4j Patching Process: Step by Step
Patching Log4Shell was not a single update.
It became a sequence of updates, mitigations, retesting, and redeployments because the initial fixes did not fully close the risk.
Here is the practical patching process security and engineering teams should follow.
Step 1: Identify Every Affected Version
Start by identifying all instances of vulnerable Log4j across code repositories, build artifacts, container images, deployed workloads, vendor software, and internal tools.
For Log4j 2.x, the most urgent focus is vulnerable log4j-core versions associated with CVE-2021-44228 and related follow-on vulnerabilities.
Do not assume that because a service is internal, it does not matter. Internal services may still be reachable from compromised systems, CI/CD environments, service-to-service traffic, VPN users, or exposed management interfaces.
Also do not assume that because a repository has been updated, production is safe.
The patched version must be built, deployed, and running.
Step 2: Upgrade to a Fixed Version
The preferred fix is to upgrade Log4j to a safe, supported version.
During the original incident, many teams first moved to 2.15.0. Then CVE-2021-45046 showed that the initial fix was incomplete in certain conditions. Teams then moved again. Additional issues such as CVE-2021-45105 and CVE-2021-44832 created more remediation work.
That history is important.
The lesson is not that patching failed. The lesson is that patching was a moving target under active exploitation.
For current remediation, teams should use the latest supported Log4j 2.x version available for their environment and follow Apache’s current security guidance.
Step 3: Remove JndiLookup Where Immediate Upgrade Is Not Possible
In some cases, teams cannot upgrade immediately.
Maybe the application is legacy. Maybe compatibility is unclear. Maybe the vendor has not released a supported fix. Maybe a production deployment window is not available.
In those cases, temporary mitigation may be necessary.
One historical mitigation was removing the JndiLookup class from vulnerable Log4j packages. This reduced exposure by removing the lookup behavior attackers were abusing.
But mitigation is not the same as remediation.
Temporary measures should be treated as a bridge to patching, not a permanent fix.
Step 4: Retire Unsupported Log4j 1.x
Log4j 1.x is a separate problem.
It is end-of-life and should not be treated as a safe long-term alternative. Organizations still using Log4j 1.x should plan migration to a supported logging framework rather than relying on unsupported software inside production systems.
This work is often slower than a version bump.
It may require code changes, dependency changes, testing, and compatibility work. But keeping unsupported logging infrastructure in production creates long-term risk.
Step 5: Rebuild, Redeploy, and Verify
Updating a dependency file is not enough.
The patched dependency has to make it into the running application.
That means rebuilding artifacts, redeploying services, updating container images, replacing vulnerable packages, restarting applications where necessary, and verifying that production is actually running the fixed version.
This is where teams often lose the thread.
A ticket may show “patched.” A repository may show “updated.” A container registry may contain a fixed image.
But an old workload may still be running.
Verification should happen at runtime, not just in the pipeline.
Step 6: Monitor for Exploitation Attempts
Patching does not erase previous exposure.
Teams should review logs, outbound connections, process activity, DNS lookups, suspicious LDAP or RMI traffic, unexpected child processes, unusual downloads, persistence attempts, and signs of compromise.
The goal is to answer a separate question:
Was this vulnerability exploited before we patched it?
That investigation requires more than a vulnerability scanner. It requires runtime and application context.
Why Patching Alone Left Organizations Exposed
Every security team knows patching matters.
Log4Shell did not disprove patching. It proved that patching alone is not enough when exploitation is active, widespread, and faster than enterprise remediation cycles.
There were several reasons.
Hidden Dependencies
Many vulnerable instances of Log4j were not direct dependencies.
They were buried inside frameworks, libraries, vendor software, internal platforms, packaged Java applications, and container images.
Security teams could scan source repositories and still miss the vulnerable component running in production.
That is the dependency problem.
You cannot patch what you cannot find.
The Patch Testing Window
Even when teams found vulnerable Log4j instances, patching production systems still required engineering work.
Applications needed to be updated, built, tested, deployed, and verified. Some patches created compatibility issues. Some applications were business-critical. Some teams had to coordinate with vendors. Some systems had limited maintenance windows.
That time mattered.
Attackers were not waiting.
During that window, organizations needed protection for the applications still running vulnerable code.
The Initial Fix Was Incomplete
The first remediation path did not end the story.
Log4j 2.15.0 addressed the original issue, but follow-on vulnerabilities forced additional updates. 2.16.0 addressed more risk by disabling JNDI functionality and message lookups. Then additional updates followed.
This created confusion for teams trying to move quickly.
A system could be “patched” in the morning and still need another update by the end of the week.
That is what happens when a vulnerability is being understood, exploited, and remediated at the same time.
Exploitation During the Patching Window
For many organizations, the question was never just “have we patched?”
It was also “what happened before we patched?”
Attackers scanned aggressively. Exploit attempts began quickly. Some were noisy. Some were automated. Some were opportunistic. Some were designed to drop crypto miners, establish access, or test whether a target was reachable.
Patching closes the known vulnerability going forward.
It does not automatically tell you whether code executed yesterday.
Runtime Prevention as the Missing Layer
Log4Shell showed why runtime prevention belongs beside patching, not after it.
Patching reduces the vulnerable code surface. Runtime prevention protects the live execution window.
Those are different jobs.
A scanner can tell you that Log4j exists.
SCA can tell you that a dependency version is vulnerable.
A ticket can tell you that engineering has a patch assigned.
Runtime prevention answers a different question:
What is the application doing right now?
That question matters most during active exploitation.
Runtime prevention can observe dangerous execution behavior while the application is running. It can see when a library, function, or call chain attempts suspicious actions such as unexpected outbound connections, process spawning, command execution, or other behavior that does not belong in that code path.
The value is not only detection.
The value is attribution.
Which application triggered the behavior?
Which library caused it?
Which function path was involved?
Which container, image, pod, node, or workload was affected?
Which team owns the fix?
Without that context, security teams end up stitching together logs, endpoint alerts, network events, scanner output, and ticket data while attackers are already moving.
Runtime application security closes that gap.
Where Raven Fits
Raven is built to protect applications at runtime.
For a Log4Shell-style incident, Raven helps security teams understand which vulnerable libraries are actually present, loaded, reachable, and executing in production. It ties runtime behavior back to the specific application, library, function, call chain, container, image, pod, node, and workload involved.
That context changes the response.
Instead of treating every vulnerable package as equal, teams can prioritize what is actually exposed. Instead of waiting for every patch to move through every pipeline, they can protect applications during the remediation window. Instead of seeing only a process or network event, they can understand the application code path behind it.
Log4Shell was a patching crisis.
But it was also a runtime crisis.
Raven is designed for that exact gap.
Conclusion: Patch Log4j, But Do Not Stop There
The right answer to Log4Shell was always to patch.
Find vulnerable Log4j versions. Upgrade to supported fixed versions. Remove unsafe components where immediate upgrade is impossible. Retire unsupported Log4j 1.x usage. Rebuild, redeploy, and verify.
But Log4Shell also made something clear.
Patching is not instant. Visibility is not complete. Vendor timelines are not always fast. Transitive dependencies are hard to find. Production systems do not update themselves. And attackers move during the gap.
AI makes that gap more dangerous. It can help attackers find the next vulnerable open-source package faster, generate exploit paths faster, and adapt payloads faster than most teams can inventory, test, patch, and redeploy. Finding and patching every vulnerable dependency before it can be exploited is not a scalable defense model on its own.
That is why runtime prevention matters.
The next Log4Shell may not be Log4j. It may be another open-source package. Another Java library. Another framework. Another dependency buried inside a vendor product. Another exploit path that becomes active before most organizations even know where they are exposed.
Security teams still need to patch. They also need protection while patching happens.
They also need protection while patching happens.
That is the layer Log4Shell proved was missing.


