threatintel
actor tracker
All briefs

Technical profile: VersaMem (Volt Typhoon, CVE-2024-39717)

Author: Thomas Malinowski Published: 2026-05-16 Status: OSINT synthesis from primary technical reporting Tags: Volt Typhoon, China, web shell, Java, Tomcat, Javassist, ISP, SD-WAN


Summary

VersaMem is a custom Java web shell observed by Lumen Technologies' Black Lotus Labs in August 2024, attached as a malicious agent to the Apache Tomcat process running on Versa Director — the centralised management plane for Versa Networks' SD-WAN appliances. The implant exploits a zero-day in Versa Director (CVE-2024-39717, CVSS 7.2) that allowed authenticated administrators to upload arbitrary files and reach remote code execution. Black Lotus Labs attributes the campaign with moderate confidence to the PRC state-sponsored cluster Microsoft tracks as Volt Typhoon, with the earliest known exploitation against a U.S. ISP victim dated 12 June 2024 — more than two months before the bug was assigned a CVE on 22 August 2024.

VersaMem is interesting for two reasons that make it worth a dedicated profile rather than a one-liner in a campaign roll-up:

  1. It is fully in-memory, manipulating already-loaded Java bytecode via the JVM's Instrumentation API and the Javassist library — a class of implant rarely seen outside red-team tooling.
  2. It targets the management plane of a service-provider appliance category (SD-WAN orchestration) that is, by design, trusted to read and write configuration on every downstream edge device. A successful VersaMem on a Versa Director instance gives the operator the same authority over the carrier's SD-WAN estate as the carrier's own NOC.

Both properties make VersaMem an unusually clean illustration of the Volt Typhoon thesis: do not bring tooling to the carrier; live on the equipment that is already there.


Exploitation chain

StepMechanism
1. Pre-authThe operator obtains valid Versa Director credentials — Black Lotus Labs assesses the access vector was separate, prior exploitation of internet-exposed SOHO routers and a long-credential-spray dwell, consistent with Volt Typhoon's documented operational style.
2. BugAuthenticated admin uses the file-upload feature exposed at the Versa Director GUI to drop a JAR payload that exists outside the application's expected file paths. Versa Director did not validate the file path or type, allowing the upload to be placed where the Tomcat container could be made to load it.
3. RCEThe uploaded JAR contains a Java agent (a JAR with Premain-Class and Agent-Class in its manifest) registered with the running JVM via the Instrumentation API. The JVM hands the agent a ClassFileTransformer callback that fires on every subsequent class load — and, more usefully here, can rewrite the bytecode of already-loaded classes.
4. PersistenceRe-attachment on Tomcat restart is implicit: Versa Director's startup scripts will pick up the agent if the operator drops it on a path the launcher reads (the Versa Director installer registers a small number of well-known classpath entries). VersaMem does not need a separate persistence stage.

In-memory architecture

The Lumen disassembly identifies two principal classes inside the JAR, both implementing javassist.ClassFileTransformer and both attached at agent-load time via Instrumentation.addTransformer:

CapturePassTransformer

Hooks Versa's setUserPassword method on the authentication service class. On every invocation — i.e. every time a user authenticates against Versa Director — the transformer intercepts the plaintext password argument before it is hashed by Versa's internal credential store. The captured credential is then:

  1. AES-encrypted with a key embedded in the JAR,
  2. Base64-encoded,
  3. written to a stable location on disk (Lumen reports a path under the Tomcat temp directory) for retrieval by the operator on a later session.

The on-disk file is the only persistent artifact VersaMem leaves behind — and even that artifact is encrypted and looks like arbitrary Base64 in a directory that legitimately contains hundreds of similar temporary files. There is no implant on disk to find with a static AV scan.

Catalina doFilter hook

Hooks the org.apache.catalina.ApplicationFilterChain.doFilter method — the central dispatch point through which every inbound HTTP request passes after Tomcat's connector accepts it. The hook inspects the request for an actor-defined POST parameter (a static "password" token shipped with the implant), and if present, expects a second parameter to carry a Base64-encoded Java module to load in-memory.

The module loader then:

  1. Base64-decodes the second parameter,
  2. defines the resulting bytes as a new class in a fresh ClassLoader,
  3. invokes a known entrypoint on the class,
  4. discards the class. Subsequent requests load fresh modules afresh.

This is functionally equivalent to a remote-eval for the operator: any capability that can be expressed as a single Java class — port scanner, credential dumper, configuration scraper, secondary-tooling fetcher — can be staged, executed, and discarded in a single HTTP round-trip with zero on-disk footprint beyond the AES-encrypted credential log.


Operator OPSEC and what it tells us

A few choices in the implant suggest a mature operator:

  • The credential log is encrypted on disk rather than written in plaintext. Many open-source web shells dump credentials in the clear to simplify follow-on tooling; VersaMem accepts the extra complexity of key management for the operator OPSEC benefit.
  • The Catalina-filter module loader is stateless — modules are loaded, run, and discarded. There is no "module table" left in memory that an investigator with a JVM heap dump could enumerate.
  • The implant uses a single static authentication parameter rather than a request-signed protocol. This is less sophisticated than (say) Cobalt Strike's BeaconExecutionStatus, but it shifts the OPSEC burden off the network — there is no recognizable challenge-response handshake in pcap.
  • The agent is delivered as a Maven-built JAR with a clean manifest. Build artefact metadata indicates an internal build pipeline (Apache Maven, build timestamp 2024-06-03 14:24 UTC) rather than a hand-compiled class file.

Net assessment: VersaMem looks like in-house operator tooling, written and built specifically for the Versa Director target, by a group that understands JVM internals well enough to live entirely inside the Java agent surface. It is not a re-skin of a public project.


Detection opportunities

VersaMem is hard to find on disk. It is not hard to find at runtime if you are looking at the right surface.

Detection surfaceSignal
JVM agent telemetryThe presence of any Instrumentation-registered agent on a Versa Director production JVM is itself anomalous. jcmd <pid> VM.command_line and jcmd <pid> VM.system_properties reveal -javaagent arguments and dynamically-attached agents respectively.
Tomcat access logCatalina doFilter hook fires on every request, but the operator's actual invocations are POSTs that carry the static "password" parameter. Hunt for POST requests with an unusual parameter set and zero body content type on otherwise-quiet management endpoints.
Disk huntThe AES-encrypted credential log lives at a stable path under Tomcat's temp directory. A YARA rule scanning for high-entropy Base64 blobs in ${CATALINA_BASE}/temp/ catches the artifact when the implant is active.
Egress trafficVersaMem itself does no outbound connections — exfil happens through the operator's HTTP fetch of the encrypted credential file. But the modules loaded via the Catalina hook will: any unexplained outbound from a Versa Director box is investigable.

This project ships a static-string YARA rule (mal_volt_typhoon_versamem.yar) targeting the unique class names and the Maven build manifest seen in the public Lumen sample. It is a research-quality starting point; production tuning should swap to a rule maintained against rotating Volt Typhoon samples post-disruption.


Strategic takeaways

  1. The management plane is the carrier perimeter. Versa Director is to a Versa-equipped carrier what a domain controller is to an enterprise. Patching, hardening, and isolating it should be a higher-priority programme than perimeter-firewall hardening at most service-provider organisations. The same lesson re-applies to every SD-WAN, SASE, and managed-firewall orchestration plane on the market.
  2. In-memory Java agents are a defensive blind spot. Endpoint protection on Linux JVMs typically does not enumerate dynamically-attached agents. Building that visibility — even at the level of an hourly jcmd VM.system_properties poll — is a cheap detection investment with a high marginal return against this entire class of implant.
  3. Two-month dwell on a zero-day before CVE assignment is the ceiling, not the floor. VersaMem was in the wild for ten weeks before a CVE existed, against a high-trust appliance category. Defenders should assume the same dwell window for every internet-facing appliance they operate, and design programmes that survive that window without depending on patch availability.

References