← Back to Research

Log4Shell, Four Years Later: We Still Find It Twice a Month

Twenty-four confirmed-exploitable Log4Shell instances in Q1 2026. Across 612 customer scans, our adaptive engine landed a working jndi:ldap callback on twenty-four production targets. Fourteen lived in shipped vendor appliances — NetApp controllers, Cisco firmware images, ServiceNow plugins. Seven sat in container base layers nobody had rebuilt since 2022. Three were in active dev branches that re-imported a vulnerable transitive dependency last quarter.

CVE-2021-44228 turned four years old in December. Every CISO in the world ran a Log4Shell sprint. Most of them remember it as the worst weekend of their career. Most of them also remember it as finished. It is not finished. It is the most reliable long-tail finding our engine produces, and the regression rate is climbing, not falling.

Here is what we found, where it hid, why your scans missed it, and the specific regression path that keeps reintroducing it on patched fleets.

What happened

In Q1 2026 we ran the standard Log4Shell deep-scan profile across 612 in-scope customer assets. The profile is not a banner-grab — it sends a structured set of ${jndi:ldap://…} payloads through every header, query parameter, form field, and JSON value our crawler collects, then waits for an out-of-band DNS or LDAP callback against an attacker-controlled collaborator. Twenty-four targets called back. Every one of those callbacks was reproducible. Every one of those targets was, by their own asset inventory, "patched."

Here is how the twenty-four broke down:

None of the twenty-four customers had skimped on patching. Every one of them had remediated Log4Shell at the application layer, generated a SBOM, and signed off on the incident. The vulnerable code reappeared anyway, through paths their patching program was never structured to see.

Why it kept working

Three regression mechanics are responsible for nearly all of it. Once you see them, you understand why "we patched Log4Shell" is one of the most misleading sentences in security operations.

The vendor appliance fiction

When you run a vulnerability scanner against a NetApp controller or a Cisco IOS-XE box, you get back what the appliance's SNMP or REST endpoint says is installed. You do not get back the JAR files inside the embedded Java services that the appliance vendor ships as black-box firmware. The vendor releases a signed firmware image, and unless that image was rebuilt and re-signed against a patched log4j, your appliance is still vulnerable — regardless of what the vendor's own advisory page claims, regardless of what the appliance reports about its own version.

We routinely find Log4Shell in appliances whose vendors quietly issued a "fixed" minor-release months after the public advisory and never pushed a hard-deprecation notice. Customers are still running the original "patched" build, which fixed the headline but not the embedded transitive log4j buried four directories deep in a Tomcat the appliance does not document.

The container layer time capsule

A pinned base image is a frozen snapshot of whatever was vulnerable the day the pin was made. The whole point of immutable infra is that nothing changes in production without an explicit rebuild. The whole problem of immutable infra is that nothing changes in production without an explicit rebuild — including the layer with the vulnerable JAR.

Most Log4Shell remediation scripts patched the running JVM's classpath, the application's pom.xml, and the library cache. They did not advance the FROM line in the Dockerfile. A rebuild months later picked up the original pinned digest, layered the patch on top, and produced a "patched" image that still contained the vulnerable JAR earlier in the layer stack — which the JVM's classloader would still find under specific runtime conditions, and which any sidecar or init container reading the same volume could load.

The transitive re-import

Maven Central does not unpublish artifacts. Older log4j versions are still resolvable, still cacheable, and still pulled by anything whose dependency tree references them — including third-party libraries that pinned an old log4j inside their own POM and never bumped it. We have seen pristine Spring Boot services on the latest framework version transitively pull a 2021-era log4j through a logging adapter that hadn't released a new build since 2022. The application team never typed log4j into a build file in their lives. It was on their classpath anyway.

Dependabot and Renovate alert on direct dependencies. They alert on transitive dependencies only when configured to, and only when the security advisory database has fingerprinted the specific transitive path. For long-tail CVEs four years past disclosure, that fingerprinting coverage is uneven. The dependency arrives. The alert does not.

What to check today

  1. Treat appliance firmware as untrusted at the JAR level. Run a live jndi callback test, with an attacker-controlled collaborator, against every management interface, reporting console, and admin UI exposed by every appliance in your environment. Do not trust the vendor's advisory page. Do not trust the appliance's self-reported version. Send the payload and watch for the callback. If a callback lands, you have a confirmed exploitable instance regardless of what the vendor claims.
  2. Re-derive your container image inventory by cryptographic digest, not tag. List every running pod's image digest. Diff that list against a list of base-image digests known to ship vulnerable log4j classes. Anything that matches is a regression candidate that needs an explicit rebuild — not a re-pull of the same tag, an actual FROM bump and a fresh layer hash.
  3. Audit transitive dependencies, not direct ones. Run mvn dependency:tree or gradle dependencies across every active branch and every release branch. Grep the output for log4j-core at any version below 2.17.1. Anything you find is something Dependabot didn't catch — usually because it is buried inside a third-party package whose own dependency tree your alerting never traced.
  4. Scan dev branches with the same payload set as production. The three regressions in active dev branches we found in Q1 had not been promoted yet — but they would have been by April. Catch the regression before it ships, not after.
  5. Stop trusting the December 2021 sign-off. The remediation memo from four years ago is not a continuing assertion. Re-run the active-payload test quarterly and treat it as the authoritative answer to "are we exploitable." Inventories drift. SBOMs drift. The exploit either lands or it doesn't.

How CELVEX Group tests for this

The Log4Shell deep-scan lives in core/test_catalog/_supplement_longtail_2026-03.py as LONGTAIL-LOG4SHELL-DEEP-SCAN-001. It is not a banner check. It is an active end-to-end exploit attempt: every header (including User-Agent, X-Forwarded-For, X-Api-Version, Referer), every query parameter, every form field, and every JSON value receives a structured set of ${jndi:ldap://…}, ${jndi:dns://…}, and obfuscation-bypass payloads (${${lower:j}ndi}, ${${::-j}${::-n}${::-d}${::-i}}, and the upper/lower variants threat actors are still using in 2026). Each payload encodes a unique target identifier so a callback from a collaborator infrastructure server can be unambiguously attributed to the originating asset.

The test fires across the full crawled surface, including authenticated paths when an engagement provides credentials, and against management interfaces of recognized appliances (NetApp, Cisco, ServiceNow, VMware, F5, Palo Alto management planes) that we have fingerprinted as historically affected. A confirmed callback produces a proof-of-concept that names the vulnerable parameter, the working payload, the attacker-controlled host the callback hit, the timestamp, and a reproducer command — not a "potential" finding, a confirmed one with the network evidence attached.

The test runs on every CELVEX Group scan profile that includes the long-tail catalog. We re-run it quarterly across every customer fleet without being asked, because the regression mechanics described above guarantee that "patched in 2021" decays back to "exploitable in 2026" on a predictable schedule.

Bottom line

Log4Shell is not finished. It is the canonical example of a long-tail vulnerability whose regression mechanics — appliance firmware opacity, container layer pinning, transitive dependency re-import — guarantee its return on a fleet whose patching program was structured around the December 2021 emergency and never updated for the four years that followed. Twice a month, on average, our engine lands a working jndi callback on a target whose owner believed they were patched. They believed it because their patching program was structurally incapable of seeing where the vulnerability had moved.

Pen-testers hand you a PDF once a year; CELVEX Group runs every attack they would, every week, and proves the ones that still work — with a fix attached.

Sources

Run a free Exposure Check — 60 seconds, no signup

See the publicly visible signals an attacker would use to fingerprint a Log4Shell-vulnerable target on your perimeter. No account required.

Start your Exposure Check