Wan Litong (Alibaba) and Yu Fanghai (independent researcher) attacked a stalled problem in Java application security: finding new memory shells (fileless backdoors that live entirely inside a running JVM's memory, injected via reflection into a legitimate web framework's internals). Known types had barely grown in two years, because finding a new one meant manually auditing Spring or Tomcat's internals class by class. Their fix was to stop reading and start measuring: a four-stage automated pipeline combining static analysis (SAST) to shrink the search space, dynamic instrumentation to prove candidates actually fire under real traffic, an automated JVM memory-graph search to find how to reach them, and AI-assisted proof-of-concept generation to turn a confirmed reachable target into working exploit code. The result: 7 previously unknown Spring memory-shell families, more than tripling the previously known count from 2 to 9. This is defensive tooling research aimed at closing a detection gap; I explain the pipeline's design, not the working exploit code it generates.
Post 32 of my Black Hat Asia 2026 series. The value here is methodological: it's an approach to systematically finding an entire bug class faster than manual review can, applicable well beyond Java memory shells.
1. What a JVM memory shell actually is
A memory shell is fileless malware that lives entirely inside a running Java application's memory. Once an attacker has any code-execution primitive (a deserialization bug, an unsafe file upload, an injection flaw, anything that lets arbitrary Java run once), instead of dropping a webshell file to disk, they use reflection to reach into the live application framework's internal objects and quietly modify or replace a component so it now runs attacker logic on every future request. Because nothing is written to disk, filesystem-based antivirus and file-integrity monitoring see nothing. Because the malicious behaviour arrives disguised as ordinary HTTP traffic on the application's normal port, it slips past most network monitoring too, and it persists until the process itself restarts.
The classic example is a Filter memory shell: an attacker uses reflection to inject a malicious Java Servlet Filter directly into Tomcat's internal filter chain, ahead of the application's own legitimate filters, so it intercepts every incoming request before normal processing even begins.
2. Why discovery stalled
Memory shells have gone through a recognisable arc: an initial concept phase (timer-based backdoors, malicious code injected into memory), an early-development phase building shells around Tomcat's own extension points (Filters, Servlets, Listeners), a rapid-development phase as public tools like Behinder and Godzilla made building and using such shells easy and added support for newer mechanisms like WebSocket and Spring's own registration APIs, and then, by the researchers' account, a multi-year creative bottleneck: essentially no genuinely new shell types discovered in about two years.
The reason is structural, not a lack of interest. Early shell types exploited native Tomcat mechanisms that were already well-understood and easy to reason about by hand. But the space of possible injection points is actually the entire Spring framework's internals, an enormous, sprawling codebase, and manually auditing it class by class for "could I hijack this?" hits diminishing returns fast. Researchers, understandably, stopped looking as hard.
3. Why existing defences don't close the gap
Two defensive layers already exist, and both have real but limited value against new shell types specifically:
- Runtime injection defence (RASP): traces the call stack back from known vulnerable entry points and watches for sensitive operations like dynamic bytecode loading, blocking based on characteristics of the injected bytecode itself. Effective against known injection mechanisms, but it's reactive to patterns it already knows to watch for.
- Resident memory-shell detection: scans live JVM memory for loaded classes that have no corresponding file on disk, a strong generic signal, and dumps suspicious classes for pattern analysis (the open-source
java-memshell-scannertool is cited as a reference implementation).
Both are valuable, but neither one discovers new injection points before an attacker does. That gap, proactively finding the next hijackable Spring component before it appears in the wild, is what the automated pipeline targets.
4. The four-stage pipeline
Fig. 1 — Static filtering narrows thousands of classes to hundreds; dynamic proof narrows candidates to confirmed hits; memory search supplies the reach path; AI turns a confirmed target into a reviewable PoC.
4.1 SAST: compressing the framework surface
The pipeline starts with semantic reduction, not exploit generation, using CodeQL-style static analysis to build a structural profile of an exploitable class. A candidate must be: public, non-final, non-abstract, non-anonymous (so it can be subclassed or replaced without breaking type contracts); in a Spring namespace, not the standard Java library (excluding java.*, javax.*, sun.* automatically rules out an enormous share of irrelevant code); instantiated and held as a field in some other class (so replacing that field's value is meaningful); and that field must actually be invoked via method calls whose methods accept request- or response-shaped parameters, since a component that's never called can't intercept traffic no matter how cleanly it's replaced. Applying this filter to the whole Spring framework surface produced 374 deduplicated candidate entries, from thousands of classes down to a manageable, structurally-plausible shortlist.
Static filtering is a real reduction, but it's not proof: some candidates only ever appear on startup or in test code paths, never during real request handling. Static suspicion needs to become runtime evidence.
4.2 Java Agent instrumentation: turning suspicion into evidence
A candidate that actually fires under real HTTP traffic is evidence; a candidate that never fires is noise, and the pipeline's core insight is to treat memory shells as fundamentally runtime entities, verified by runtime observation, borrowing directly from how IAST (Interactive Application Security Testing) tools work. A Java Agent (supporting both attach-at-startup and attach-to-a-running-process modes) is loaded at the bootstrap classloader level specifically so it survives container and application isolation boundaries, with a single central bytecode transformer managing hook rules generated from the SAST-filtered candidate list. Traffic is then replayed against the instrumented application, and the agent records, for each candidate, whether its method actually executes during real request processing. This flips the problem from "does this class look like it could be hijacked" to "was this class actually invoked while handling a request", a much stronger signal.
4.3 JVM memory search: finding the reach path
Confirming a target is reachable during request processing is only half the problem, an attacker with a code-execution primitive still needs to know how to reach and modify that specific object from wherever their injected code runs. The pipeline solves this with an automated breadth-first search over live JVM object graphs, triggered at the moment a runtime breakpoint hits: starting from a stable anchor (the current thread), it walks field references outward (thread → thread group → other threads → internal server objects → framework context → target field), guided by keyword hints for likely field names, a blacklist to prune obviously irrelevant branches, and a maximum hop-depth limit, until it finds a complete field-by-field path from the anchor to the confirmed target object. The output is a concrete, step-by-step reflection chain (a sequence of field lookups) that, mechanically translated, becomes the actual reflective code needed to reach and replace the target at runtime.
4.4 AI-assisted PoC generation: grounded, not exploratory
The last stage uses an LLM to turn a confirmed, reachable target into an actual proof-of-concept, deliberately constrained rather than left to freely generate exploit ideas: it's given the target's real decompiled source to read before generating anything, required to preserve the exact existing method signature rather than inventing plausible-looking APIs that don't actually exist, instructed to minimise the scope of what it overrides to only what's strictly needed, told to keep the malicious payload class separate from the injection-mechanism logic, and required to return structured JSON output rather than free-form prose, producing a reviewable artefact a human can actually audit rather than an opaque code blob. Generated PoCs are then automatically rebuilt and executed for verification; documented failure modes include method-signature drift across different framework versions, constructor argument mismatches, an injection point that turns out to be unstable at runtime under real request conditions, or a reflection-path assumption breaking when the live object graph doesn't match what was expected during the search phase.
5. A worked example: the FlashMap shell
One of the newly discovered families, found by the lead researcher, replaces Spring MVC's FlashMapManager inside DispatcherServlet via reflection, reached through an eight-layer field-access chain from a JVM thread down through Tomcat's internals into the Spring application context. It's notable for three properties the team generalised into their reusable exploitability profile: it's a standard Spring component with a standard interface (so replacing it doesn't break normal application behaviour, keeping the compromise stealthy); it's invoked on every single HTTP request, before authentication and independent of the requested URL, giving it a universal, highly reliable trigger; and it required no active request context to inject, the injection itself is purely memory-based reflection work. Those three properties, standard-interface component, actually-invoked-on-every-request, and public/non-final so it can be subclassed, became exactly the structural profile the SAST stage searches for.
6. Results
Starting from 2 previously known Spring-specific memory shell families, the pipeline confirmed 7 new verified families, bringing total known coverage to 9, more than a fourfold increase, achieved specifically by replacing manual code reading with automated static filtering, runtime proof, automated reachability search, and constrained AI-assisted exploit generation.
7. What this means for defenders
- Assume the space of possible memory-shell injection points is much larger than the currently catalogued list. A systematic discovery pipeline like this one can find new families faster than manual research has managed in years; defenders should expect the known-family count to keep growing, not plateau.
- Prioritise resident-memory scanning over signature matching on known shell types. Because the underlying detection method (loaded classes with no corresponding disk file) is type-agnostic, it catches new families the researchers haven't found yet too, unlike RASP rules or signatures written against specific known injection mechanisms.
- Treat "public, non-final, held as an invoked field" components as an audit checklist for your own Spring-based applications and internal frameworks; that's exactly the structural profile this research shows attackers (and researchers) can search for automatically.
- Runtime instrumentation is a legitimate discovery tool for defenders too, not just for building the shell. The same IAST-style traffic-replay approach that proves a shell candidate fires can be turned around to continuously monitor production traffic for exactly that kind of unexpected component invocation.
Is this specific to Spring, or does the method generalise?
The specific 374-candidate shortlist and the 7 new families are Spring-specific, but the four-stage pipeline (static structural filtering → dynamic runtime proof → automated reachability search → constrained AI-assisted PoC generation) is a general approach applicable to other large frameworks with similar extension-point patterns.
Does an attacker need a fresh code-execution vulnerability for each new shell type?
Yes. A memory shell isn't itself the initial vulnerability, it's what an attacker does after they already have some code-execution primitive, turning a one-time exploit into a persistent, fileless backdoor.
Why is a resident-memory scan more future-proof than a bytecode-pattern rule?
A pattern-matching rule has to be written against a known shell's specific characteristics, so it only catches shell types someone has already discovered and written a rule for. Scanning for the type-agnostic signal, loaded classes with no corresponding disk file, catches any memory shell regardless of which framework component it hijacked.
What role does the LLM actually play here?
It's constrained to generating a proof-of-concept for an already-confirmed, already-located target, using the real decompiled source as grounding. It isn't used to discover new candidates or decide what's exploitable; that work happens in the SAST, instrumentation and memory-search stages first.
Takeaways
- JVM memory shells are fileless backdoors injected via reflection into a running Java framework's internals; known-family discovery had stalled for years due to the manual-audit bottleneck.
- A four-stage pipeline, SAST structural filtering, Java Agent runtime proof, automated JVM memory-graph search, and constrained AI-assisted PoC generation, replaces manual code reading with systematic, evidence-driven discovery.
- SAST narrowed the entire Spring framework surface to 374 structurally-plausible candidates; runtime instrumentation then separated real hits from noise.
- Result: 7 new verified Spring memory-shell families, more than tripling prior known coverage from 2 to 9.
- For defenders: type-agnostic resident-memory scanning beats signature-based detection against a shell family that hasn't been catalogued yet.
References & further reading
- Black Hat Asia 2026 — this talk's official page — abstract, speaker bio, and slides/video when posted.
- Official talk recording on YouTube — Black Hat channel.
- Black Hat Asia 2026 — full Briefings schedule — every talk in the conference.
- java-memshell-scanner (c0ny1) — the reference resident memory-shell detection tool cited in the talk.
- CodeQL — the static-analysis engine used for the SAST filtering stage.
- Spring Framework — the target ecosystem for the 374-candidate search and the 7 newly discovered families.