Contents
An attempt looks like a normal Server Function call. A hit leaves marks.
In requests
- POST requests carrying the
next-actionorrsc-action-idheaders, which AWS advises looking for. Normal Server Function calls carry them too, so treat them as a filter, not a signature.11 - Scanning traffic from many addresses with randomized user agents, as AWS observed.
On a server that was hit
- The Node.js process spawning shells,
curlorwget. - Unexpected files in
/tmp, and new systemd or cron entries for persistence. - Hands-on reconnaissance such as
whoami,idand reading/etc/passwd, which AWS saw one cluster do by hand for about an hour. - Cryptocurrency miners (XMRig), backdoors and tunnelers, reported by Google, Microsoft and Wiz.12
Beware look-alikes
A proof of concept (PoC) is code published to show a flaw is real. Many early public PoCs for this one did not work, and some were AI-generated. Davidson, Google's threat intelligence group and others warned that scanners built on them gave false positives and false negatives. Check your version numbers rather than trusting a scanner's verdict.10
One request walked out of the data and into JavaScript's own machinery.
React2Shell was a flaw in deserialization: the step where a server rebuilds live JavaScript objects from the text a browser sent. The browser's message could steer that rebuilding into JavaScript's own machinery, and from there into running code. Nothing in the chain needed a login, and none of it ran the app's own code.
Scroll, or focus the stage and use the arrow keys. The amber dot is the request; the camera moves to the part of the server each step is about. Field names are conceptual; there is no exploit here.
- Own propertyA property set on the object itself, not inherited.
hasOwnPropertytells the two apart. - CWE-502The catalogue entry for deserialization of untrusted data: unsafe when the data controls more than it should.
A Server Function call is just an HTTP POST.
React Server Components let part of a React app run on the server. A Server Function (also called a Server Action) is server code the browser can call over the network. Next.js sends that call as a POST request marked with a
Next-Actionheader.Nothing about the request proves it came from your own page. Anyone can send one.
- method
- POST
- header
- Next-Action: <action id>
- body
- multipart form data
- login needed
- no
The arguments travel in React's Flight format.
The Flight protocol is React's own wire format between browser and server. It can say more than JSON can. Each numbered form field is a chunk, and a value inside one chunk can be a reference: roughly “chunk 1, then property
a, then propertyb”.A value can also be marked as a promise, or point at an export of a server module.
- chunk 0
- the call's arguments
- chunk 1
- an object
- chunk 2
- a reference: 1 → a → b
- promises
- allowed
- module refs
- allowed
The decoder runs before any of your code.
To know which function to call and with what, the server first rebuilds live JavaScript objects from the chunks. That happens inside React's Flight reply decoder.
It runs before your Server Function and before any login check you wrote. Datadog and the discoverer both confirmed that a stock
create-next-appproduction build was exploitable.14- your functions
- none needed
- auth checks
- not run yet
- app code
- not run yet
- decoder
- ReactFlightReplyServer
A reference is resolved by walking names.
For “chunk 1 → a → b” the resolver starts at chunk 1 and steps through each name in turn:
value = value[name].In the vulnerable versions, nothing checked that each name was the object's own data. Any name the caller sent was followed. A second lookup, for server-module exports, had the same gap.6
- for each name
- value = value[name]
- own-property check
- none
- who picks names
- the caller
In JavaScript,
value[name]also finds inherited properties.Every JavaScript object has a prototype: a shared object it inherits behaviour from, which has its own prototype, and so on. That is the prototype chain. Ask an object for
__proto__,constructoror a promise'sthenand you get that shared machinery, not the object's data.Because the resolver accepted any name, a reference could walk out of the data and into the machinery. Datadog describes this as prototype-chain traversal.
- obj["a"]
- the object's own data
- obj["__proto__"]
- its prototype
- obj["constructor"]
- the function that built it
- promise["then"]
- how it is resumed
The decoder's own chunk is promise-like. That is the lever.
A thenable is any object with a
thenmethod; JavaScript automatically calls it when a promise resolves to that object. According to the discoverer's write-up, React's internal chunk type inherited fromPromise.prototypeand had athen.10Using references, an attacker could reach a chunk and plant a
then. Automatic promise unwrapping then re-entered the parser with a fake chunk built entirely from attacker data, internal fields included.- chunk prototype
- Promise.prototype
- then
- reachable by reference
- fake chunk fields
- attacker data
- unwrapping
- automatic
From
constructortoFunction, and the server runs code.Following
constructorproperties eventually reaches JavaScript'sFunctionconstructor, which turns a string into runnable code. The decoder calls it while it believes it is resolving an ordinary value.The code runs as the Node.js server process, with its environment variables, files and network. Attackers used it to read secrets and cloud credentials, then install crypto miners, backdoors and tunnels.13
- runs as
- the server process
- reach
- env vars, files, cloud metadata
- seen in the wild
- miners, backdoors, tunnelers
- login needed
- no
The fix: only an object's own properties count.
Sebastian Markbåge's PR #35277, merged 3 Dec 2025 at 15:41 UTC and released as 19.0.1, 19.1.2 and 19.2.1, steps into a name only when the value is an object and
hasOwnPropertysays the name is its own. Module-export lookups got the same guard.The chunk type was restructured, and the server's reply decoder was brought back in line with the client-side decoder it had drifted from.6
- step into name
- only if own property
- inherited names
- resolve to nothing
- module exports
- own exports only
- RCE
- closed; DoS follow-ups kept coming
The numbered markers on the diagram match the steps.
Next.js carried its own copy, so upgrading React changed nothing.
Next.js does not depend on React's server packages through npm. It ships its own bundled copy, a practice called vendoring. Upgrading react-server-dom-webpack in your package.json changes nothing for Next.js: you have to upgrade next. That is why Next.js published its own advisory, which “tracks the downstream impact”.3
@vitejs/plugin-rsc also vendors react-server-dom-webpack and got its own advisory. Frameworks that consume React's packages directly (Waku, React Router, RedwoodSDK) had no advisory of their own: the fix there is to upgrade the React packages.
If your app's React code does not use a server, your app is not affected.
- VendoringCopying a dependency's code into your own package instead of declaring it. The copy does not appear as its own entry in a lockfile.
- GHSAA GitHub Security Advisory ID. Each vendoring layer needed its own.
Six hours from fix to scanning. Two days to the government's list.
- 4days
from the private report (29 Nov) to the public fix (3 Dec).
- ~6h
from the CVE's publication to scanning, seen by Datadog around 22:00 UTC.
- 2days
until CISA added it to the Known Exploited Vulnerabilities catalog.
- 7
follow-up CVEs in the same decoder, through July 2026.
The headline fix held. The safe floor kept moving.
The remote code execution was closed on day one. But researchers looking hard at the same decoder kept finding ways to exhaust it: infinite loops, memory exhaustion and CPU burn. The first denial-of-service fix was itself incomplete. Seven follow-up CVEs landed over eight months.2
The lesson for anyone pinning versions: track the latest patch on your line, not “the version that fixed the headline CVE”.
The patch for React2Shell remains fully effective.
| Line | 55182 RCE · Dec 3 | 55183/4 Dec 11 | 67779 Dec 11 | 23864 Jan 26 | 23869 Apr 8 | 23870 May 6 | 44907 Jul 21 |
|---|---|---|---|---|---|---|---|
| 19.0 | 19.0.1 | 19.0.2 | 19.0.3 | 19.0.4 | 19.0.5 | 19.0.6 | 19.0.8 |
| 19.1 | 19.1.2 | 19.1.3 | 19.1.4 | 19.1.5 | 19.1.6 | 19.1.7 | 19.1.9 |
| 19.2 | 19.2.1 | 19.2.2 | 19.2.3 | 19.2.4 | 19.2.5 | 19.2.6 | 19.2.8 |
Next.js shipped matching releases. The 11 December 2025 fixes (14.2.35, 15.0.7, 15.1.11, 15.2.8, 15.3.8, 15.4.10, 15.5.9, 16.0.10) also reached Next.js 13.3 and later, so 13.x and 14.x users who were safe from the RCE still had to move to 14.2.35. Further releases followed on 28 January, 10 April and 11 May 2026. As of Next.js's 20 July 2026 security release, the newest patches are 15.5.21 and 16.2.11.4
- Denial of service (DoS)Making a service unavailable, here by sending input that makes the decoder loop, run out of memory or burn CPU.
Attackers arrived within hours, from many directions.
Each line is one organization's own report. Counts come from different vantage points and don't add up to a single total.
- AWS (4 Dec 2025): exploitation attempts within hours of disclosure from China-nexus groups it tracks as Earth Lamia and Jackpot Panda, chained with other recent CVEs.11
- Google Threat Intelligence (12 Dec): several China-nexus clusters deploying tunnelers, downloaders and backdoors; Iran-nexus actors; and financially motivated XMRig miners from 5 December.12
- Wiz: victims from 5 December, mainly internet-facing Next.js apps and Kubernetes containers; credential harvesting from environment variables, files and cloud metadata; at least six miner campaigns.13
- Datadog: more than 800 scanning IPs, with payloads moving from broken proofs of concept to working ones.14
- Palo Alto Unit 42: activity overlapping North Korean “Contagious Interview” tooling, and Linux backdoors.15
- Microsoft (15 Dec): “several hundred machines across a diverse set of organizations”, on Windows and Linux.16
- CISA: the KEV entry currently marks known ransomware campaign use. The feed does not say when that flag was set.9
Exposure estimates come from vendor telemetry: Wiz found vulnerable React or Next.js in 39% of the cloud environments it sees, and Unit 42 counted over 968,000 React and Next.js instances. Press figures such as “more than 60 organizations” could not be confirmed on the primary pages and are left out here.
Only upgrading stopped it. Everything else limited the damage.
Which controls would have stopped or limited this one. A bug in trusted code is a different animal from a malicious package: most supply-chain controls do not apply.
| Control | Effect here | Why |
|---|---|---|
| Upgrade speed | Stops it | The only fix. No workaround existed. Scanning started about six hours after publication, so a monthly patch cycle was far too slow. |
| Edge firewall rules | Stopgap | Vercel, AWS, Google Cloud Armor, Azure, Fastly and Akamai all shipped rules, and all said not to rely on them. Vercel paid bounties for bypasses of its own. |
| Secret rotation | Limits damage | Code execution in the server process exposes every environment variable it can read (step 7). |
| Lockfile scanning | Partly | Finds you only if it matches the right package. The copies inside next and @vitejs/plugin-rsc are invisible to a scan keyed on react-server-dom-*. |
| Minimum release age, provenance, 2FA, pinned actions | Doesn't apply | These guard against malicious publishes. Here the code was published honestly by its maintainers; the bug was in it. |
[Firewall rules] cannot guarantee protection against all possible variants.
Sources
Primary sources are the vendors, advisory databases and governments responsible for the facts. Secondary sources are researchers' own analyses.
Primary · vendors and maintainers
- 1React (Meta)Critical Security Vulnerability in React Server Components
Description, versions, report and fix timeline, framework upgrade steps, the firewall caveat.
- 2React (Meta)Denial of Service and Source Code Exposure in React Server Components
Follow-up CVEs, reporters and fixed versions.
- 3Next.js (Vercel)CVE-2025-66478
Next.js ranges and fixes, App Router only, secret-rotation guidance, fix-react2shell-next.
- 4Next.js (Vercel)Security update, 11 December 2025
Follow-up fixed versions; the RCE patch “remains fully effective”.
- 5VercelReact2Shell security bulletin
Firewall rules and their limits, bypass bounties, rotation.
- 6ReactFix PR #35277 and commit 7dc903cd
The own-property checks and the decoder rework described in step 8.
Primary · advisory databases and government
- 7CVE ProgramCVE-2025-55182
CVSS vector, CWE-502, publication time. The CVE-2025-66478 record shows the rejection.
- 8GitHub Advisory DBGHSA-fv66-9v8q-g76r (React) · GHSA-9qr9-h5gf-34mp (Next.js) · GHSA-fmh4-wr37-44fp (Vite plugin)
Affected ranges per package.
- 9CISAKnown Exploited Vulnerabilities catalog (feed)
KEV listing, due date, ransomware flag.
Secondary · discoverer and researchers
- 10Lachlan Davidsonreact2shell.com and the original write-up
The name, the report date, the vendoring point, the conceptual chain, warnings about fake PoCs.
- 11AWS Security
- 12Google Threat Intelligence
- 13Wiz
- 14Datadog Security Labs
- 15Palo Alto Unit 42
- 16Microsoft Security
Written from sources checked on 2026-09-30. Every fact above traces to one of them; claims found only in press coverage are left out. Pull quotes are verbatim from the linked primary pages; the bracketed words in Vercel's are ours.