On the evening of July 7, 2026, someone sat down at a Hong Kong-hosted box and started working. Inside about an hour they built a target list of 8,595 internet-facing Next.js applications, swept it for the React Server Components deserialization flaw disclosed seven months earlier, narrowed it to 958 running the vulnerable framework, and logged between 66 and 77 of them returning code execution. Fileless backdoors went onto a subset. At some point the same operator was also inside a victim's private Kubernetes network, doing the same thing again to its internal services.

Then they left the workspace on a public HTTP directory and walked away. The scripts, the target lists, the result files, the run logs, all of it, sitting on port 8889 with directory listing enabled. It has been there ever since. We found it two months later, read all 39 files without contacting the host from anything attributable to us, and reconstructed the campaign end to end from the operator's own records.

The vulnerability itself is not news. CVE-2025-55182, informally "React2Shell," is a CVSS 10.0 unauthenticated remote code execution flaw that was weaponized by multiple China-nexus groups within hours of its December 3, 2025 disclosure, and it has been covered exhaustively by Amazon, Google, Microsoft, Unit 42, Cloudflare, Rapid7 and Recorded Future.23 What is new here is the operator. Its React2Shell tooling, its campaign and its victims appear nowhere in that reporting, and the host itself is publicly visible only as a bare entry in one abuse feed, unconnected to this vulnerability. This is an unnamed operator inside a well-documented wave, caught with its notebook open.

Key findings

The vulnerability, and the wave it created

CVE-2025-55182 is an unsafe-deserialization remote code execution flaw in React Server Components, disclosed on December 3, 2025 and rated CVSS 10.0.1 It affects the react-server-dom-webpack, react-server-dom-parcel and react-server-dom-turbopack packages at versions 19.0, 19.1.0, 19.1.1 and 19.2.0, and by extension the Next.js App Router built on them. The fixed releases are 19.0.1, 19.1.2 and 19.2.1.3

The mechanism is what makes it dangerous. A server handler for the React "Flight" wire protocol deserializes attacker-supplied data arriving in a Server Function request. The exploit abuses Flight's self-reference syntax to walk the object prototype chain, reaches the global Function constructor, and from there executes arbitrary code inside the Node.js process. No authentication, no user interaction, and no developer-written server code is required. One crafted HTTP request is the whole attack.4

Exploitation began almost immediately. Amazon's threat intelligence teams observed attempts from infrastructure linked to the China-nexus groups Earth Lamia and Jackpot Panda within hours of disclosure.2 Google Threat Intelligence Group went on to document five separate China-nexus clusters deploying distinct tooling, alongside Iran-nexus activity and a broad layer of opportunistic cryptomining.3 The flaw reached CISA's Known Exploited Vulnerabilities catalog within days.5

That is the wave. What follows is one operator inside it that nobody has written up.

How this was collected

The starting point was a passive host record. A Shodan sweep for the port pattern we already associate with AS400619 surfaced a host running a Python http.server on port 8889 with directory listing on, alongside a SOCKS5 proxy, a management panel, and an HTTPS service fronting a registered domain. The directory index was the operator's working folder.

Reading someone else's exploitation workspace is a provenance problem before it is an analysis problem. Two rules governed collection: never touch the host from attributable infrastructure, and never execute anything recovered from it. Two distinct methods were used, and they are recorded separately in the chain of custody because they carry different risk.

How the 39 files were collected

21 files, mediated entirely by urlscan.io. Unlisted scans were submitted through the urlscan API for each file URL, and urlscan's own scanners fetched the content. CrimsonVector infrastructure never contacted the operator host for any of these. urlscan returned each body gzip-compressed; both the raw and decoded forms are hashed.

18 .log files by direct fetch. urlscan could not retrieve these, because the host serves .log as application/octet-stream, which urlscan does not store. They were pulled directly from a disposable, non-attributable collection host behind a commercial VPN exit. The operator would have seen a VPN exit address, not CrimsonVector. That is a real, if small, tip-off risk.

The decoded evidence set is held write-protected at mode 0444. It is scripts and data files only, with no binaries. Nothing was executed at any point, and static analysis was done offline in a container with networking disabled entirely. Every file carries a SHA-256 hash in both raw and decoded form, with the originating urlscan scan UUID recorded per file. With the two collections combined, all 39 directory entries are accounted for. reverse_shells.log was empty at the source.

This is third-party OSINT capture of an exposed directory, not a forensic image of the operator host. File timestamps and authorship are as-served and are not independently verified. The result files are the operator's own claims of success, which makes them strong evidence of intent and of the operator's self-assessment, and nothing more.

No victim host was probed by us, then or since. Where this report says a target was vulnerable, exploited, or backdoored, it means the operator's own files say so. Confirming any of it would have meant sending a request to a victim, so the claims are reported at the confidence the evidence supports rather than dressed up as verified.

The workspace

Port 8889 exposes a working directory, not a staging server or a payload drop, and it has the mess that implies: four successive versions of the same exploit script, half a dozen redundant run logs, and result files named the way a person names files at one in the morning.

GroupContentsWhat it establishes
Targeting gen_targets.py, targets_gen.py, targets.txt How the 8,595-host target list was built, and from which service
Exploitation cve_poc.py, cve_poc_v2.py, cve_poc_v3.py, exploit.py Four iterations of the React2Shell payload and its delivery
Lateral movement k8s_scan.js, internal_exploit.js, internal_exploit_v2.js Internal service enumeration and re-exploitation inside a victim environment
Results exploit_echo.json, exploit_noecho.json, exploit_oob.json, exploit_memshell.json, vuln_targets.json, vuln_urls.txt, memshell_verify.json, memshell_verified.json The operator's own scorecard, per verification technique
Run logs 18 .log files covering check, echo, no-echo, memshell and generation passes The minute-by-minute timeline of the session

The run logs are the part that turns a pile of scripts into a narrative. They are the operator's own console output, timestamped, and they show the order of operations and the dead ends. You can watch the technique change as verification modes fail and get replaced.

One overnight session

The infrastructure predates the campaign by two months, and the campaign itself is startlingly short. The public front domain was registered in early May 2026. An AdaptixC2 listener was flagged on the host in late June. Then nothing until the night of July 7, when the whole React2Shell operation runs start to finish before breakfast.

Infrastructure staging, the overnight campaign, and two months of exposure
timeline
    title From front-domain registration to a workspace still open in September
    section Staging
        04 May 2026 : Front domain registered through Cloudflare; earliest certificate issued
        23 Jun 2026 : AdaptixC2 first observed on port 4221 (ThreatFox)
    section The campaign, one session
        07 Jul 2026, 21#58;39 GMT : First workspace file written; target generation from FOFA
        07 Jul 2026, 21#58;56 GMT : Target triage begins; App Router confirmation sweep
        07 Jul 2026, 22#58;00 to 23#58;02 GMT : Echo, no-echo, memshell and out-of-band verification passes
        08 Jul 2026, 06#58;51 GMT : Last workspace file written; session ends
    section Left open
        03 Sep 2026 : Directory independently indexed by Shodan, still exposed
        10 Sep 2026 : Confirmed still live during CrimsonVector collection
        14 Sep 2026 : Re-check finds the host live and the AdaptixC2 listener still reporting to ThreatFox
          
Fig 1. Directory file timestamps span 21:39 on July 7 to 06:51 on July 8. The run logs place the active exploitation in a tighter window, roughly 21:56 to 23:02, progressing from target triage through echo and no-echo verification to memshell deployment and confirmation. Timestamps are as-served by the operator host and are not independently verified.

Roughly an hour of concentrated exploitation, inside a session whose file timestamps span nine hours, against a target list built the same evening. What filled the remaining eight hours is not in the evidence, and neither is the Kubernetes work. The scripts carrying the victim's internal service map were written during the session, but the map itself had to be collected before they could be written, which could mean earlier that night or a separate, earlier intrusion. We cannot place it on this timeline and do not try to.

Two things about the timing do hold up. The cadence is that of a person working through a job list, not a self-propagating botnet and not patient espionage tasking. And the date is late. The React2Shell rush was December 2025, within hours of disclosure. This campaign ran in July 2026, seven months after the fixed releases shipped. That is not the behavior of an actor racing a fresh vulnerability. It is the behavior of one harvesting whatever is still unpatched long after the industry stopped paying attention, which fits the access-acquisition read better than it fits espionage.

The attack chain

The recovered tooling reconstructs the full sequence, from the query that produced the target list to the backdoor left running inside a victim's private network.

Target-to-backdoor chain, reconstructed from the operator toolkit
flowchart TD
    F["FOFA query
Next.js fingerprint"] T["targets.txt
8,595 hosts"] TR["Triage
App Router match"] V["958 candidates
patch state unknown"] X["React2Shell request
Next-Action multipart"] P["Function constructor
to child_process"] C["/rce callback
66 to 77 recorded"] R["Reverse shell via /slw
log empty"] M["Node memshell
hidden as /api/health"] K["k8s_scan.js
10.0.0.0/8 service map"] I["internal_exploit.js
internal services"] F --> T --> TR --> V --> X --> P P --> C P --> R P --> M M --> K --> I --> M classDef recon fill:#FBEFC8,stroke:#B8901A,color:#3A2F05; classDef exploit fill:#F8DFE0,stroke:#C0272D,color:#5A1216; classDef persist fill:#DFE0F5,stroke:#4B4BA8,color:#1E1E4A; class F,T,TR,V recon; class X,P,C,R exploit; class M,K,I persist;
Fig 2. Reconnaissance and triage in gold, exploitation and verification in crimson, persistence and lateral movement in blue. The FOFA query is enumerated across 20 countries and 11 ports with web3, crypto and AI keywords appended; triage matches the App Router chunk path only, which is a framework check and not a patch check; the exploit body carries the Flight prototype-pollution markers and reaches a base64 shell command. The loop at the bottom is the important part: the in-memory backdoor on an internet-facing app becomes the vantage point for enumerating a private Kubernetes network, and the same exploit is then replayed against internal services that were never meant to be reachable.

Targeting. gen_targets.py queries FOFA with an embedded operator account key for the fingerprint combining the Next.js application signature with the App Router static chunk path. The query is enumerated across 20 countries and 11 ports, with an appended keyword set covering web3, crypto and AI applications. Output is the 8,595-line target list. The keyword bias is the first real signal about motive.

Triage. Before firing anything, the exploit scripts fetch each target's root and confirm the App Router chunk path is present. It is a cheap filter that drops Pages Router apps, which cannot be vulnerable, and it is what reduces 8,595 hosts to 958.

Be careful with that second number, including where you see it quoted elsewhere. It counts applications running the right framework, not applications confirmed vulnerable. An App Router app on a patched React release matches exactly the same fingerprint and cannot be exploited. The vulnerability is only actually demonstrated at the next step, on the 66 to 77 hosts that returned code execution.

Exploitation. A crafted Next-Action multipart request carries the Flight prototype-pollution payload, with the telltale $1:__proto__:then and $1:constructor:constructor markers and a resolved_model status. In the exploit.py variant, the payload's _prefix field reaches Node's child-process module through process.mainModule.require, decodes a base64 blob, and runs it as a shell command synchronously. The scripts spray a fixed path set: root, /app, /apps, /api, /dashboard, /admin and /login.

Verification. Four separate modes confirm execution, each calling back to the command-and-control host with the victim hostname, user and working directory. The four exist because the operator iterated: echo mode depends on output being reflected in the response, which many targets will not do, so out-of-band and no-echo variants were written to catch the blind cases.

StageRecorded by the operator
Hosts swept8,595
App Router candidates after triage (patch state unknown)958
Returned code execution, echo mode72
Returned code execution, out-of-band mode66
Returned code execution, no-echo mode77
Returned code execution, memshell mode67
Memshells recorded as live, cross-referenced in two operator files2

Two cautions on that table. The per-technique counts overlap heavily and must not be summed; they are four passes over the same 958 candidates, and the spread between 66 and 77 is mostly a measure of how many targets each verification method can see, not of four separate victim sets. And every figure in it, without exception, is the operator's own bookkeeping.

Payloads. Three delivery options sit behind successful execution: a bare out-of-band probe, a reverse shell that pipes a script fetched from the command-and-control host into an interpreter, and the in-memory backdoor.

The payloads up close

The exploit request. The multipart body sets three form fields, of which field zero is the malicious Flight JSON. What makes this operator's traffic trivially separable from the rest of the React2Shell wave is that the requests are not randomized. Every script version we examined carries a fixed X-Nextjs-Request-Id of b5dce965 and a hardcoded multipart boundary, and the later cve_poc_v3.py adds a User-Agent masquerading as the scanner sold by Assetnote, a legitimate attack-surface vendor with no connection to this activity. The hope is presumably that defenders read it as authorized security testing and move on. Those three strings are arbitrary, stable, and make excellent detection content.

One header on the same requests looks like detection content and is not. The requests carry Accept: text/x-component, which is simply the standard content type for React Flight traffic. Next.js sends it legitimately on every Server Component request. Alert on it and you will bury the analyst who has to triage the result.

The in-memory backdoor. The Node.js memshell reassigns the HTTP server's request-emit handler so that every inbound request is inspected before normal handling. A request to /api/health carrying the header x-health-token: ping executes an attacker-supplied command and returns the output. Everything else passes through untouched, and the application keeps behaving normally. Nothing is written to disk. A separate confirmation build calls home to announce that the implant is alive.

Why the health-check disguise works

/api/health is the single most boring path in a modern deployment. Load balancers hit it, orchestrators hit it, uptime monitors hit it, and nobody reads those logs. A backdoor that answers there generates traffic indistinguishable from the noise it is hiding in, lives only in the memory of a process that is supposed to be running, and disappears cleanly on restart with no artifact left for a disk-based forensic pass to find. The detection signal is the header, not the path, plus a request-emit handler that no longer matches its known-good baseline.

The command-and-control protocol. Four endpoints carry the whole operation, three on the operator host and one on the victim.

EndpointDirectionPurpose
/rcevictim to operatorOut-of-band execution proof, carrying hostname, user and working directory as query parameters
/msokvictim to operatorMemshell-alive ping, carrying the victim hostname
/slwoperator to victimServes the reverse-shell script that is fetched and piped straight to a shell
/api/health?t=operator to victimThe memshell command channel, on the victim itself

It is a small protocol, and a legible one. There is no encryption, no domain fronting, and no jitter. For an operator running an off-the-shelf C2 framework on a neighboring port, the React2Shell tooling itself is conspicuously homemade and conspicuously noisy.

One leg of it also appears never to have worked. reverse_shells.log was present in the directory but empty at the source, while the memshell logs were not. The straightforward reading is that the reverse-shell path was built, staged and tried, and never landed a session, leaving the in-memory backdoor as the only persistence that actually took. That is a negative finding drawn from an empty file, so hold it loosely, but it is consistent with the rest: no interactive shell appears anywhere in the run logs.

Into the cluster

Everything above is mass exploitation, which is to say it is a numbers game that could in principle be run by a script with nobody watching. Three files in the directory are not that.

k8s_scan.js carries a hardcoded list of internal services on 10.0.0.0/8 addresses, with source comments stating in plain terms that the list was extracted from a compromised victim's Kubernetes environment. This is not a scanner that discovers internal services. It is a scanner pre-loaded with the answers, written after somebody had already been inside and looked around.

internal_exploit.js and its second version then run the same React2Shell payload against those internal services over plain HTTP, and inject the in-memory backdoor onto them. The internal services were never exposed to the internet, so they were never in anyone's patching urgency queue, and they speak unencrypted HTTP to each other because they are supposed to be on a trusted network. The initial compromise of one internet-facing application turned all of that into an attack surface.

The internal service names share a common prefix and describe a wealth or portfolio-management platform: a dashboard, an identity service, a security-administration service, and a portfolio-administration service. We are not naming the platform, and we are not publishing the service map. What matters analytically is the depth: this victim was taken from external RCE to internal enumeration to internal backdoor deployment across a set of services that include identity and security administration.

Key finding

The Kubernetes lateral movement is the single most important thing in the evidence set, because it is what separates this operator from the commodity-botnet layer of the React2Shell wave. Mass scanning is automatable. Reading a compromised cluster, hardcoding its internal service map into a purpose-written script, and replaying the exploit inward is a human decision about a target judged worth the effort.

Who was hit

The operator's candidate list runs to 958 targets. Application titles in that list give a rough read on who was in scope, with the caveat that title text is a weak proxy for both geography and sector. The script an application titles itself in says something about its audience and very little about where it is hosted or who owns it.

Script of application titleTargets
Latin746 (remainder)
Chinese104
Cyrillic42
Korean28
Thai21
Japanese9
Arabic8

That spread is consistent with the 20-country sweep in the targeting script rather than with any regional focus. The sector read is more interesting, because it tracks the keyword list the operator chose.

Sector, by title keyword (non-exclusive)Targets
AI and SaaS139
Admin and ERP back-ends55
Finance, web3 and crypto38
Government and public services25
Education23
E-commerce21

Those buckets are non-exclusive and together cover 301 of the 958 candidates. The other 657 titles matched no keyword, so treat the sector read as a shape, not a census.

Two entries stand apart from the rest of the result set. The operator's memshell verification files record two hosts, one in Microsoft Azure address space and one in Tencent Cloud, where the in-memory backdoor answered and returned command output. Those two are cross-referenced across two separate operator files rather than appearing once, which makes them the best-corroborated claims anywhere in the evidence, but the corroboration is still internal to the operator's own records.

We did not probe either host. That means we cannot say whether those backdoors were still live in September, only that the operator recorded them as live in July. Both hosts, and the wealth-management platform described in the previous section, are excluded from this publication entirely and are handled through notification instead.

The host, and the network it sits on

The operator host is a self-contained target-to-shell platform. Everything needed to run the campaign is on one box, and most of it is visible from outside. Ports marked as seen by Shodan were in the passive host record on 2026-09-09. The two command-and-control ports are attested by the malware code and by ThreatFox but were not exposed to Shodan's scanners at observation time, which is normal for on-demand or scanner-firewalled C2.

PortServiceRoleSeen by
8889HTTP directory (Python http.server)Operator workspace, left exposed. The evidence source.Shodan
8084HTTP command-and-controlCallbacks on /rce and /msok, reverse-shell staging on /slwMalware code
4221AdaptixC2Post-exploitation command-and-controlThreatFox, first seen 2026-06-23
443HTTPS, Cloudflare Origin Certificate, uvicornOrigin behind the Cloudflare-fronted public domainShodan
8081Management panelOperator panel, not examinedShodan
1080SOCKS5 proxyOperator pivot and anonymizationShodan
9999HTTP, untitledMatches the exploit-box port pattern seen elsewhere on this ASNShodan
22OpenSSHManagement. The ECDSA host key is unique to this host.Shodan

AdaptixC2 on 4221 deserves a note. It is a legitimate open-source post-exploitation framework released in 2024 for red teaming, which has since been adopted broadly by criminal operators including ransomware affiliates.6 Its presence here fits an operator buying capability off the shelf rather than building it, a weak signal about both resourcing and motive.

That listener is also the one place this host is already public. The address has sat on ThreatFox as an AdaptixC2 server since June, so anyone who looks it up will find it, and the claim in this report is narrower than "nobody has seen this box." What has not been reported is everything else: the React2Shell tooling on 8889, the command-and-control on 8084, the campaign, the tradecraft, and the victims. The abuse-feed entry is a bare address with a malware family label attached, and nothing in it connects the host to CVE-2025-55182.

The host sits on AS400619, a Hong Kong reseller network announced under several trading names including Fastmos, AROSSCLOUD and Zun Cloud. CrimsonVector separately tied that ASN to an unreported Gshell command-and-control cluster in July 2026, and now tracks it as a suspected bulletproof or operator-friendly network.7 That prior work is unpublished, so treat it as an assertion you cannot check and weight it accordingly; the ASN's registration, its announced trading names, and its routing are public and can be checked. The front domain was registered through Cloudflare on 2026-05-04 and resolves through Cloudflare, with this host as the origin.

The pivots dead-ended. The host's SSH host key is unique to it, with no key-sharing sibling hosts anywhere in Shodan. Its TLS fingerprint on 443 is the generic Cloudflare-origin value and therefore not discriminating. The front domain is Cloudflare-registered with privacy protection. No shared certificate, SSH key or favicon links this host to the July Gshell cluster or to any other operator infrastructure. The durable links are the ASN and the operator's own tooling fingerprints, and nothing else. Based on that, the operator is either well-compartmentalized or working from throwaway single-instance infrastructure.

Attribution, and what we do not claim

Four hypotheses were weighed against the evidence.

H1 A China-nexus operator inside the React2Shell wave

Most consistent with the evidence. Chinese-language source comments throughout the toolkit, targeting keyed to an operator account on a Chinese attack-surface service, a Hong Kong reseller ASN already tied to China-nexus command-and-control in separate work, and tradecraft matching the publicly reported wave. Each leg is weak alone. Comment language is not nationality, exploit code gets copied wholesale, and the search service sells to anyone with a card. The judgment rests on the combination, and it stops at nexus.

H2 A specific named group

Not supported. Public reporting names Earth Lamia, Jackpot Panda and five GTIG-tracked clusters for the wave broadly, but nothing in this evidence ties this host or its command-and-control to any named group's known infrastructure or tooling. Set aside barring corroboration.

H3 Red-team or authorized scanner activity

Contradicted. Reverse-shell staging, a fileless backdoor, implants the operator logged as live on third-party cloud hosts, and lateral movement into a victim's internal Kubernetes network are not consistent with authorized testing. The Assetnote user-agent in the third exploit version is a masquerade, not a disclosure, and no authorized tester hides behind another vendor's name.

H4 A commodity botnet

Partly consistent, but incomplete. The opportunistic breadth fits, and much of the React2Shell wave really is cryptomining noise. But the hands-on Kubernetes lateral movement is not something a self-propagating script does. We deliberately do not lean on the AdaptixC2 listener here, since we cannot establish whether it is this operator's or a co-tenant's, and evidence that uncertain should not carry a hypothesis.

The assessment: a China-nexus operator, motivated by access or money, operating within but distinct from the publicly reported React2Shell exploitation wave, with the specific group unresolved. We are not naming a group, and we are not going to, because the evidence does not support it.

The gaps: we do not know which named cluster, if any, this operator maps to. We do not know the full scope of the deep-compromise victim. We do not know what followed the backdoors, whether data theft, ransomware staging, or resale of access. We do not know whether the AdaptixC2 listener on 4221 belongs to this operator or to a co-tenant on the same box. And the exposed directory is a workspace, not the operator's whole infrastructure, so its management and egress paths remain unmapped.

Detection and defense

Patch first. Upgrade React Server Components and Next.js to the fixed releases for CVE-2025-55182. It works against default configurations with no user interaction, so filtering on the request markers below only buys time; the upgrade is the fix.

Detect the exploitation attempt. Two layers are useful here: a generic rule for the vulnerability class, and a specific one for this operator's fixed request artifacts.

SignalScope
A Next-Action header on a POST whose body carries $1:__proto__:then, $1:constructor:constructor and resolved_modelGeneric, the whole CVE-2025-55182 class
X-Nextjs-Request-Id: b5dce965Specific to this operator
The fixed multipart boundary ----WebKitFormBoundaryx8jO2oVc6SWP3SadSpecific to this operator
A User-Agent of Assetnote/1.0.0 paired with the aboveSpecific, and a masquerade

Detect the backdoor. On the network, look for requests to /api/health carrying the header x-health-token: ping, and for any /api/health?t= whose query value is shaped like a shell command. On the host, the signal is a Node process whose HTTP request-emit handler has been reassigned at runtime. The concrete check is whether the server's emit handler still reports itself as native code; on a clean process the source inspection returns a native-code stub, and on a patched one it returns readable JavaScript:

// run in-process, or via an inspector session against a suspect worker
const http = require('node:http');
console.log(http.Server.prototype.emit.toString().includes('[native code]')
  ? 'clean'
  : 'PATCHED - inspect the replacement handler');

There will be no dropped file to find alongside it, because there is no dropped file. Restarting the process removes the implant and destroys the evidence at the same time, so capture the process memory before you bounce anything.

Watch egress. Outbound HTTP from a web tier to the operator host on either command-and-control port, or any shell-piped fetch of the reverse-shell path, is a high-fidelity compromise signal on its own. A Node application server making outbound requests it has no business making is the cheapest detection in this whole report.

The rules themselves. There is no reason to hold these back. The operator knows its own tooling, so publishing costs nothing operationally and saves every defender reading this the work of rebuilding them. Three YARA rules cover the exploit payload, the command-and-control endpoints, and the Node memshell:

rule react2shell_cve_2025_55182_exploit {
  meta:
    description = "CVE-2025-55182 React2Shell exploit payload (Flight prototype-pollution RCE)"
    author = "CrimsonVector"
  strings:
    $pp1 = "$1:__proto__:then"
    $pp2 = "$1:constructor:constructor"
    $rm  = "resolved_model"
    $hdr = "X-Nextjs-Request-Id"
    $bnd = "WebKitFormBoundaryx8jO2oVc6SWP3Sad"
    $na  = "Next-Action"
  condition:
    2 of ($pp1,$pp2,$rm) and 1 of ($hdr,$bnd,$na)
}

rule fastmos_shangxing_c2 {
  meta:
    description = "Fastmos/SHANGXING (AS400619) React2Shell operator C2 endpoints"
    author = "CrimsonVector"
  strings:
    $c2 = "156.239.47.147:8084"
    $e1 = "/slw"
    $e2 = "/msok"
    $e3 = "/rce?h=$(hostname)"
    // $fofa = the operator's targeting-service key, redacted here; see note below
  condition:
    $c2 or 2 of ($e1,$e2,$e3)
}

rule fastmos_nodejs_memshell {
  meta:
    description = "Node.js in-memory shell hooking http.Server.prototype.emit, disguised as /api/health"
    author = "CrimsonVector"
  strings:
    $hook  = "http.Server.prototype.emit"
    $hook2 = "Server.prototype.emit"
    $trig  = "x-health-token"
    $path  = "/api/health"
    $imp   = "node:child_process"
  condition:
    ($hook or $hook2) and 1 of ($trig,$path,$imp)
}

The second rule ships with one string commented out. The operator's targeting-service account key is a strong discriminator and would work well as a match, but it is a live credential on a third-party service and is redacted throughout this report. Defenders and CERTs who want the full string can ask. The rule matches on the command-and-control host and endpoints without it.

And the network rules, in Suricata syntax. The first is generic to the vulnerability class; the rest are specific to this operator. Note that these blocks carry the operator address undefanged, because a rule has to be copy-pasteable to be worth publishing. Tune the signature IDs, thresholds and flow direction before deploying any of them.

alert http any any -> $HOME_NET any (msg:"React2Shell CVE-2025-55182 Server Function payload";
  flow:to_server,established; http.method; content:"POST";
  http.header; content:"Next-Action"; nocase;
  http.request_body; content:"$1:__proto__:then"; content:"resolved_model"; distance:0;
  content:"$1:constructor:constructor";
  classtype:web-application-attack; sid:9055182; rev:1;)

alert http any any -> $HOME_NET any (msg:"React2Shell SHANGXING/Fastmos exploit request";
  flow:to_server,established; http.method; content:"POST";
  http.header; content:"X-Nextjs-Request-Id|3a 20|b5dce965";
  http.request_body; content:"----WebKitFormBoundaryx8jO2oVc6SWP3Sad";
  classtype:web-application-attack; sid:9055183; rev:1;)

alert http any any -> $HOME_NET any (msg:"Fastmos Node memshell trigger /api/health x-health-token";
  flow:to_server,established; http.uri; content:"/api/health";
  http.header; content:"x-health-token|3a 20|ping"; nocase;
  classtype:trojan-activity; sid:9055184; rev:1;)

alert http $HOME_NET any -> [156.239.47.147] any (msg:"Fastmos React2Shell C2 callback";
  flow:to_server,established; http.uri; pcre:"/^\/(rce|msok|slw)(\?|$)/";
  classtype:command-and-control; sid:9055185; rev:1;)

alert ip $HOME_NET any -> [156.239.47.147] any (msg:"Traffic to Fastmos/SHANGXING operator host";
  classtype:command-and-control; sid:9055186; rev:1;)

The MITRE ATT&CK mapping for the chain, for teams working detection coverage rather than indicators:

PhaseTechniques
Targeting and triageT1595, T1595.002, T1596
ExploitationT1190, T1203
Execution and deliveryT1059.004, T1059.007, T1105, T1132
Verification channelT1071.001
Persistence and evasionT1505.003, T1620
Discovery and lateral movementT1046, T1613, T1210

Notification

Victim notification is underway, and it is the reason parts of this report are redacted rather than published in full.

This is incident notification, not coordinated vulnerability disclosure. The CVE is public and patched, so there is no vendor bug to report and no embargo to negotiate. The deliverable is getting compromised and exposed parties to remediate, without harming anyone and without touching a victim system to confirm anything. Every notification rests on evidence already collected.

The work is tiered. The urgent tier is the two hosts the operator recorded as backdoored and the wealth-management platform whose internal cluster was entered, routed to the relevant cloud provider security teams and, for the platform, through a national CERT or a direct security contact. The scaled tier is the 958 App Router candidates, packaged for the Shadowserver Foundation, which notifies network owners and national CERTs at a scale no independent researcher can match.8 That package is framed as exposure and operator interest, not as confirmed compromise, because that is what the evidence supports. Handing a notification partner 958 hosts labeled vulnerable, when what we have is 958 hosts with a matching framework fingerprint, would be wrong on the facts and would burn credibility we need for the next case. The last tier is the operator infrastructure itself, reported to the hosting provider's abuse contact with modest expectations, and shared with trusted intelligence communities.

Notification will probably trigger cleanup and tip the operator off. That tradeoff was made deliberately rather than by default: protecting victims outranks continued observation, two backdoors were live, and the workspace had already been sitting open for two months. Publishing this piece has the same effect, which is why it follows the notification rather than leading it.

Conclusion

The React2Shell wave has been well covered at the top. Amazon, Google, Microsoft and the major vendors documented the named China-nexus groups, the malware families, and the cryptomining layer underneath. What that coverage does not reach is the middle: the individual operators who picked up a public exploit, pointed a commercial attack-surface search engine at it, and worked through a target list overnight on rented Hong Kong infrastructure. There are more of them than there are named groups, and they are harder to see precisely because nothing about them is distinctive.

This one is visible only because of an operational mistake. A Python http.server with directory listing enabled, left running on the same box as the command-and-control, turned an anonymous participant in a global exploitation wave into a documented case with a timeline, a toolkit, a victim list and a set of detection signatures. The tradecraft that mattered was not sophisticated, on either side. The operator forgot to close a door. We found it and read what was inside without knocking.

Appendix A · Indicators

All indicators are defanged. Treat the host and endpoints below as hostile. Victim indicators are withheld while notification runs and are not published here in any form.

Operator host (SHANGXING TECH LIMITED / Fastmos, AS400619, HK)
Host156.239.47[.]147
Workspace (evidence source)156.239.47[.]147:8889
React2Shell command-and-control156.239.47[.]147:8084
AdaptixC2 (ThreatFox-flagged)156.239.47[.]147:4221
HTTPS origin, uvicorn156.239.47[.]147:443
Management panel156.239.47[.]147:8081
SOCKS5 pivot proxy156.239.47[.]147:1080
HTTP, untitled156.239.47[.]147:9999
SSH host key (ECDSA, unique to this host)71:64:21:4d:58:cd:f0:d5:83:ca:15:b4:67:53:97:70
Front domain and network
Front domaingoaipro[.]org, registered 2026-05-04 through Cloudflare, resolving through Cloudflare
Origin ASAS400619 (SHANGXING TECH LIMITED / Fastmos / AROSSCLOUD / Zun Cloud, Hong Kong)
Command-and-control endpoints
Execution proof/rce?h=<hostname>&u=<user>&d=<pwd>
Memshell alive ping/msok
Reverse-shell staging/slw
Memshell command channel (on the victim)/api/health?t=<cmd>
Request and payload fingerprints
Fixed request headerX-Nextjs-Request-Id: b5dce965
Fixed multipart boundary----WebKitFormBoundaryx8jO2oVc6SWP3Sad
Masquerading user-agentAssetnote/1.0.0
Flight prototype-pollution markers$1:__proto__:then · $1:constructor:constructor · resolved_model
Memshell trigger headerx-health-token: ping
Exploit path spray/ · /app · /apps · /api · /dashboard · /admin · /login
Operator FOFA account key (partial, redacted)458e349e…d72f44
Operator tooling, decoded SHA-256
gen_targets.py19f2b48dcfc7dbe907d76af7b52a7597b29196969e81b91365043ccf85f9f398
targets_gen.py286c9c31c79ba45a2ce0cd55863685e3f2f39e300cbb5f47b78d075e2765c6fe
cve_poc.py78e1689f822f178797237eb0c769965ea57c1475a966089d80a2aeb8cff6ae47
cve_poc_v2.py9ee38a9c3d6ebcf784f7ff30e972a07308da618362cdae9e2394f7e8ed9cf818
cve_poc_v3.py2881aa30fd424d6984fcbd74c4b4be47e0ade1eda5f4c00f5ba5f6300f4192df
exploit.py1bf13fc88c0454d34e4c885894ac276940dd4d56cdf590eb28cdef9c9acf98b2
internal_exploit.js1103e9c905787f7b2f47109488c025bec20d3fe30631efe7e7acd89249e04c01
internal_exploit_v2.js940faeeed57844695964f450e31c325983510dea7de93512debfe0c60c7e6324
k8s_scan.jsd9823f611647efcd0485cc9852fdb90a9fac202a24257f510ef50a5dcbcc0bb8

Those nine are the operator's scripts. The remaining captured files are result sets and target lists containing victim data and are neither published nor hashed here. Full hashes for the complete set, raw and decoded, with per-file urlscan provenance, are held with the investigation.

Withheld
Victim indicatorsThe two hosts the operator recorded as backdoored, the deep-compromise platform, its internal service map, and the 958-entry candidate list are not published. They are held with the investigation and routed through notification channels.

Appendix B · Method and limitations

On limitations: this is third-party OSINT capture of an exposed directory, not a forensic image of the operator host, so file timestamps and authorship are as-served and are not independently verified. The result files are the operator's own records: they evidence intent and self-assessment reliably, and nothing in them is confirmed by us, because confirming any of it would have meant probing a victim. The two memshell entries are corroborated across two operator files, which is internal corroboration, not independent verification.

Further: the 958 figure counts App Router framework matches, not confirmed vulnerabilities, and should never be quoted as the latter. The per-technique execution counts overlap and must not be summed. The Kubernetes lateral movement cannot be dated to the July session. The management panel on 8081 was not examined. Ownership of the AdaptixC2 listener is unresolved. Attribution stops at China-nexus and does not reach a named group. And the whole picture is a workspace snapshot, not the operator's full infrastructure.

Sources

  1. National Vulnerability Database. CVE-2025-55182 (React Server Components, unsafe deserialization, CVSS 10.0, disclosed 2025-12-03). nvd.nist.gov. ↑
  2. CJ Moses. "China-nexus cyber threat groups rapidly exploit React2Shell vulnerability (CVE-2025-55182)," AWS Security Blog, December 4, 2025 (Earth Lamia and Jackpot Panda observed within hours of disclosure). aws.amazon.com. ↑ ↑
  3. Google Threat Intelligence Group. "Multiple Threat Actors Exploit React2Shell (CVE-2025-55182)," Google Cloud Blog, December 12, 2025 (affected and fixed package versions; five China-nexus clusters; Iran-nexus activity; XMRIG deployment from December 5). cloud.google.com. ↑ ↑ ↑
  4. OffSec. "CVE-2025-55182: React Server Components RCE via Flight Payload Deserialization" (technical breakdown of the self-reference and prototype-chain path to the Function constructor). offsec.com. See also Rapid7, "React2Shell, critical unauthenticated RCE affecting React Server Components." rapid7.com. ↑
  5. CISA. Known Exploited Vulnerabilities Catalog (CVE-2025-55182 added December 2025). cisa.gov. Broader wave coverage: Microsoft Security Blog, microsoft.com; Unit 42, unit42.paloaltonetworks.com; Cloudflare, blog.cloudflare.com; Recorded Future, recordedfuture.com. ↑
  6. Unit 42. "AdaptixC2: A New Open-Source Framework Leveraged in Real-World Attacks" (open-source red-team C2 released 2024, since adopted by criminal operators including ransomware affiliates). unit42.paloaltonetworks.com. ↑
  7. CrimsonVector internal tracking. Unreported Gshell command-and-control cluster on AS400619, July 2026. Held by CrimsonVector; not published. ↑
  8. The Shadowserver Foundation, scaled victim notification to network owners and national CERTs. shadowserver.org. Command-and-control attestation on port 4221 from abuse.ch ThreatFox, threatfox.abuse.ch. ↑
  9. urlscan.io, used here as an origin-shielding collection intermediary. urlscan.io. Technique identifiers throughout follow MITRE ATT&CK, attack.mitre.org. ↑
infrastructure threat-intelligence vulnerability-exploitation bulletproof-hosting china-nexus kubernetes malware-analysis OSINT

CrimsonVector, investigative research by Diego Parra into criminal infrastructure, threat-actor attribution, and security research. Defanged URLs and IPs are intentionally bracketed per responsible disclosure conventions.