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.
- The operator ran mass exploitation and recorded obtaining code execution. High confidence in the campaign, moderate in the totals. Its own result files record 8,595 hosts swept, 958 narrowed to App Router candidates, and between 66 and 77 returning code execution per verification technique. Every one of those figures is the operator's own bookkeeping. We did not probe a single target to confirm it.
- This was hands-on post-exploitation, not just scanning. High confidence. Recovered scripts carry a hardcoded internal-network service map extracted from a compromised victim's Kubernetes environment, plus code to inject in-memory backdoors onto those internal services.
- The activity is China-nexus. Moderate confidence. Chinese-language source comments throughout, targeting driven by an account key for a Chinese attack-surface search service, a Hong Kong reseller ASN we have already tied to China-nexus command-and-control, and tradecraft matching the publicly reported wave. Every leg of that is weak alone, comment language least of all, since tooling is copied and reused. The judgment rests on the combination and stops at nexus. Not attributable to a named group on this evidence.
- The React2Shell activity on this infrastructure is unreported. High confidence. The host's address is listed in one public abuse feed as an AdaptixC2 server, so the box itself is not a secret. But neither it, its ASN, nor its React2Shell command-and-control endpoints appear in any public reporting on CVE-2025-55182 exploitation, which is extensively documented otherwise.
- The motivation reads as access acquisition. Moderate confidence. Broad opportunistic targeting biased toward web3, crypto and AI applications, an off-the-shelf post-exploitation framework, and reverse-shell staging all point away from narrow espionage and toward an initial-access broker or a financially motivated operator. The Kubernetes lateral movement shows it does follow through on high-value access.
- The workspace has stayed exposed for more than two months after the campaign and was still live during collection, which is the only reason any of this is knowable.
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.
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.
| Group | Contents | What 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.
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
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.
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;
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.
| Stage | Recorded by the operator |
|---|---|
| Hosts swept | 8,595 |
| App Router candidates after triage (patch state unknown) | 958 |
| Returned code execution, echo mode | 72 |
| Returned code execution, out-of-band mode | 66 |
| Returned code execution, no-echo mode | 77 |
| Returned code execution, memshell mode | 67 |
| Memshells recorded as live, cross-referenced in two operator files | 2 |
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.
/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.
| Endpoint | Direction | Purpose |
|---|---|---|
/rce | victim to operator | Out-of-band execution proof, carrying hostname, user and working directory as query parameters |
/msok | victim to operator | Memshell-alive ping, carrying the victim hostname |
/slw | operator to victim | Serves the reverse-shell script that is fetched and piped straight to a shell |
/api/health?t= | operator to victim | The 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.
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 title | Targets |
|---|---|
| Latin | 746 (remainder) |
| Chinese | 104 |
| Cyrillic | 42 |
| Korean | 28 |
| Thai | 21 |
| Japanese | 9 |
| Arabic | 8 |
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 SaaS | 139 |
| Admin and ERP back-ends | 55 |
| Finance, web3 and crypto | 38 |
| Government and public services | 25 |
| Education | 23 |
| E-commerce | 21 |
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.
| Port | Service | Role | Seen by |
|---|---|---|---|
| 8889 | HTTP directory (Python http.server) | Operator workspace, left exposed. The evidence source. | Shodan |
| 8084 | HTTP command-and-control | Callbacks on /rce and /msok, reverse-shell staging on /slw | Malware code |
| 4221 | AdaptixC2 | Post-exploitation command-and-control | ThreatFox, first seen 2026-06-23 |
| 443 | HTTPS, Cloudflare Origin Certificate, uvicorn | Origin behind the Cloudflare-fronted public domain | Shodan |
| 8081 | Management panel | Operator panel, not examined | Shodan |
| 1080 | SOCKS5 proxy | Operator pivot and anonymization | Shodan |
| 9999 | HTTP, untitled | Matches the exploit-box port pattern seen elsewhere on this ASN | Shodan |
| 22 | OpenSSH | Management. 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.
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.
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.
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.
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.
| Signal | Scope |
|---|---|
A Next-Action header on a POST whose body carries $1:__proto__:then, $1:constructor:constructor and resolved_model | Generic, the whole CVE-2025-55182 class |
X-Nextjs-Request-Id: b5dce965 | Specific to this operator |
The fixed multipart boundary ----WebKitFormBoundaryx8jO2oVc6SWP3Sad | Specific to this operator |
A User-Agent of Assetnote/1.0.0 paired with the above | Specific, 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:
| Phase | Techniques |
|---|---|
| Targeting and triage | T1595, T1595.002, T1596 |
| Exploitation | T1190, T1203 |
| Execution and delivery | T1059.004, T1059.007, T1105, T1132 |
| Verification channel | T1071.001 |
| Persistence and evasion | T1505.003, T1620 |
| Discovery and lateral movement | T1046, 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.
| Host | 156.239.47[.]147 |
| Workspace (evidence source) | 156.239.47[.]147:8889 |
| React2Shell command-and-control | 156.239.47[.]147:8084 |
| AdaptixC2 (ThreatFox-flagged) | 156.239.47[.]147:4221 |
| HTTPS origin, uvicorn | 156.239.47[.]147:443 |
| Management panel | 156.239.47[.]147:8081 |
| SOCKS5 pivot proxy | 156.239.47[.]147:1080 |
| HTTP, untitled | 156.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 | goaipro[.]org, registered 2026-05-04 through Cloudflare, resolving through Cloudflare |
| Origin AS | AS400619 (SHANGXING TECH LIMITED / Fastmos / AROSSCLOUD / Zun Cloud, Hong Kong) |
| 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> |
| Fixed request header | X-Nextjs-Request-Id: b5dce965 |
| Fixed multipart boundary | ----WebKitFormBoundaryx8jO2oVc6SWP3Sad |
| Masquerading user-agent | Assetnote/1.0.0 |
| Flight prototype-pollution markers | $1:__proto__:then · $1:constructor:constructor · resolved_model |
| Memshell trigger header | x-health-token: ping |
| Exploit path spray | / · /app · /apps · /api · /dashboard · /admin · /login |
| Operator FOFA account key (partial, redacted) | 458e349e…d72f44 |
gen_targets.py | 19f2b48dcfc7dbe907d76af7b52a7597b29196969e81b91365043ccf85f9f398 |
targets_gen.py | 286c9c31c79ba45a2ce0cd55863685e3f2f39e300cbb5f47b78d075e2765c6fe |
cve_poc.py | 78e1689f822f178797237eb0c769965ea57c1475a966089d80a2aeb8cff6ae47 |
cve_poc_v2.py | 9ee38a9c3d6ebcf784f7ff30e972a07308da618362cdae9e2394f7e8ed9cf818 |
cve_poc_v3.py | 2881aa30fd424d6984fcbd74c4b4be47e0ade1eda5f4c00f5ba5f6300f4192df |
exploit.py | 1bf13fc88c0454d34e4c885894ac276940dd4d56cdf590eb28cdef9c9acf98b2 |
internal_exploit.js | 1103e9c905787f7b2f47109488c025bec20d3fe30631efe7e7acd89249e04c01 |
internal_exploit_v2.js | 940faeeed57844695964f450e31c325983510dea7de93512debfe0c60c7e6324 |
k8s_scan.js | d9823f611647efcd0485cc9852fdb90a9fac202a24257f510ef50a5dcbcc0bb8 |
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.
| Victim indicators | The 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
- Discovery: a Shodan port-pattern sweep of AS400619, following prior work on that ASN.
- Primary collection: urlscan.io-mediated capture of 21 files. Unlisted scans were submitted via API and urlscan's scanners fetched the content; analyst infrastructure never contacted the operator host.9
- Secondary collection: 18
.logfiles fetched directly from a disposable, non-attributable collection host behind a commercial VPN exit, because the host serves them asapplication/octet-streamand urlscan does not store that. Disclosed as a distinct method with a distinct tip-off risk. - Handling: evidence held write-protected at mode 0444, scripts and data only, nothing executed. Static analysis performed offline in a container with networking disabled.
- Integrity: SHA-256 for every file in both raw and decoded form, with per-file urlscan scan UUIDs recorded.
- Enrichment: passive host records and ThreatFox for the C2 attestation; BGP for network attribution rather than IP-geolocation org strings.
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
- National Vulnerability Database. CVE-2025-55182 (React Server Components, unsafe deserialization, CVSS 10.0, disclosed 2025-12-03). nvd.nist.gov. ↑
- 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. ↑ ↑
- 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. ↑ ↑ ↑
- 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. ↑
- 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. ↑
- 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. ↑
- CrimsonVector internal tracking. Unreported Gshell command-and-control cluster on AS400619, July 2026. Held by CrimsonVector; not published. ↑
- 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. ↑
- urlscan.io, used here as an origin-shielding collection intermediary. urlscan.io. Technique identifiers throughout follow MITRE ATT&CK, attack.mitre.org. ↑
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.