← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2025-55182 · CWE-502 · Disclosed 2025-12-03

A pre-authentication remote code execution vulnerability exists in React Server Components versions 19

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

This is not a crack in the paint, it is a hidden service door that opens straight into your Node.js process

CVE-2025-55182 is an unsafe deserialization bug in React Server Components handling for react-server-dom-webpack, react-server-dom-parcel, and react-server-dom-turbopack. Affected versions are 19.0.0, 19.1.0, 19.1.1, and 19.2.0; fixed versions are 19.0.1, 19.1.2, and 19.2.1. The bug is triggered over HTTP against server-side React deployments that support React Server Components, and React states apps can be vulnerable even when they do not explicitly implement Server Function endpoints.

The vendor's CRITICAL 10.0 is basically fair in enterprise reality. The one real downward pressure is exposure population: this does not hit pure client-side React SPAs, and it requires an RSC-capable server-side stack. But once that condition is met, the rest of the chain is brutally simple: unauthenticated, remote, no user interaction, full server-side code execution, public PoCs, active exploitation, and CISA KEV status. That keeps it squarely in CRITICAL.

"KEV-listed pre-auth RCE on internet-facing app servers is an emergency, even if only RSC-enabled React stacks are exposed."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Find an RSC-capable target with Nuclei, Censys, or Shodan-like enumeration

Attackers first identify internet-facing applications likely to be running React Server Components or downstream frameworks such as Next.js App Router. Cloudflare and Censys both observed large-scale scanning immediately after disclosure, and Cloudflare explicitly noted Nuclei usage in the early wave. This step is low skill because the ecosystem, headers, routes, and framework fingerprints are easy to harvest at scale.
Conditions required:
  • Target application is reachable over the internet or from attacker-controlled network position
  • Application uses server-side React with RSC-capable components or a downstream framework that embeds the vulnerable packages
Where this breaks in practice:
  • Pure client-side React deployments are unaffected
  • Internal-only apps or apps behind VPN/private ingress shrink reachable population
  • Some hosting/WAF providers added detection quickly, reducing blind internet-wide success
Detection/coverage: External attack-surface management and web fingerprinting can spot likely exposed frameworks, but version certainty usually requires SBOM/SCA or package inspection.
STEP 02

Deliver a malicious Flight/Server Action payload with a public PoC

The exploit sends a crafted HTTP request that abuses React's decoding of payloads destined for server-side RSC handling. Public research repos such as ejpir/CVE-2025-55182-research document working chains and payload construction, so attackers do not need to reverse the bug from scratch. This is the weaponization step that converts a framework fingerprint into an actual code path hit.
Conditions required:
  • Application processes React Server Components traffic on the vulnerable package versions
  • Attacker can send HTTP requests to the relevant application endpoint
Where this breaks in practice:
  • Apps fronted by managed WAFs may block commodity payloads
  • Custom routing, body limits, or nonstandard deployment patterns can break copy-paste exploit reliability
Detection/coverage: WAFs from Cloudflare and AWS shipped coverage, and Nuclei/scanner signatures exist, but coverage is imperfect because the protocol and payload can be mutated.
STEP 03

Abuse object traversal and fake chunk handling to reach dangerous runtime behavior

The technical chain documented in public research abuses reference resolution and fake chunk injection so attacker-controlled data is revived as trusted model state. The unsafe traversal lets the payload reach powerful JavaScript primitives, and the deserialization logic crosses the boundary from data parsing into executable behavior. At this point the vulnerability stops being 'just parsing' and becomes process compromise.
Conditions required:
  • Target is running one of the vulnerable react-server-dom-* packages
  • The crafted payload is accepted and decoded by the server-side RSC implementation
Where this breaks in practice:
  • Exploit reliability can vary by framework wrapper and deployment hardening
  • Some edge controls block obvious forms of the payload before it reaches Node.js
Detection/coverage: SCA will reliably identify vulnerable package versions; runtime detection is harder and usually shows up as suspicious request bodies, deserialization anomalies, or downstream process execution.
STEP 04

Execute arbitrary code in the Node.js app context and pivot

Successful exploitation yields arbitrary JavaScript execution inside the application server process. From there the attacker can read secrets, steal environment variables, access cloud credentials, tamper with builds or content, and laterally move using whatever the app identity already has. In real estates this often means CI tokens, database credentials, object-store keys, and internal API access—not just one crashed web pod.
Conditions required:
  • Application runtime has access to secrets, internal services, or cloud metadata worth stealing
  • Post-exploitation controls do not immediately kill the process or isolate the workload
Where this breaks in practice:
  • Strong workload isolation, minimal IAM roles, read-only containers, and good EDR on hosts can reduce blast radius
  • Short-lived containers can limit persistence if secrets and identities are also tightly scoped
Detection/coverage: EDR, container runtime telemetry, cloud audit trails, and app logs may catch follow-on behaviors, but they do not prevent the initial RCE.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusYes. CISA KEV-listed, AWS observed exploitation within hours of disclosure, and GreyNoise/Cloudflare both reported opportunistic exploitation and mass scanning.
KEVListed on CISA KEV on 2025-12-05 with 2025-12-12 due date for federal remediation.
Proof-of-concept availabilityPublic PoCs exist. ejpir/CVE-2025-55182-research documents a working RCE chain; Cloudflare also observed attackers using commodity tooling including Nuclei.
EPSS0.82011 supplied in the prompt, which is an extremely high exploitation-likelihood signal and consistent with the observed abuse pattern.
CVSS interpretationCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H means unauthenticated network reachability, no user clicks, and full CIA impact with scope change. Technically that is maximum-severity territory.
Affected versionsreact-server-dom-webpack, react-server-dom-parcel, and react-server-dom-turbopack versions 19.0.0, 19.1.0, 19.1.1, and 19.2.0.
Fixed versionsUpstream React fixes are 19.0.1, 19.1.2, and 19.2.1. Censys also notes downstream Next.js fixes at 15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7, and 16.0.7; no distro-style backports were identified because this is primarily an npm package issue.
Exposure dataCensys reported ~2.15 million internet-facing services that *may* be affected, while Cloudflare saw 582.10M React2Shell WAF hits from 2025-12-03 through 2025-12-11 17:00 UTC.
DisclosurePublicly disclosed on 2025-12-03; React says the report came from Lachlan Davidson on 2025-11-29.
Reporting researcher / orgLachlan Davidson reported it to the React team; public threat reporting came quickly from React, AWS, Cloudflare, GreyNoise, and Censys.
04 · The Call

noisgate verdict.

Final Verdict
= UNCHANGED to CRITICAL (9.6/10)

The decisive factor is active exploitation against internet-facing app servers with no authentication requirement. The main friction is that only server-side React/RSC deployments are in scope, but that narrowing is not enough to drag a KEV-listed pre-auth RCE out of the CRITICAL bucket.

HIGH Affected version ranges and fixed versions
HIGH Active exploitation and KEV status
MEDIUM Precise exposure population in a typical enterprise estate

Why this verdict

  • Vendor baseline holds: 10.0 is not inflated for environments actually running vulnerable RSC packages because the bug is unauthenticated remote RCE with full server impact.
  • First friction adjustment down: this is not all React. Pure client-side SPAs are unaffected, so the reachable population is narrower than the product brand name suggests.
  • Second friction adjustment stops there: requiring an RSC-capable server stack is a real filter, but it does not imply prior compromise, internal access, authentication, or user interaction.
  • Amplifier overwhelms the narrowing: CISA KEV listing and observed exploitation by AWS/GreyNoise/Cloudflare mean defenders are racing active operators, not hypothetical researchers.
  • Blast radius is server-side, not cosmetic: compromise lands in the Node.js application trust zone where secrets, database access, cloud roles, and CI/CD tokens commonly live.

Why not higher?

There is no honest room to score this at a perfect theoretical 10.0 in real-world prioritization without acknowledging deployment friction. The most important limiter is exposure population: only server-side React estates using vulnerable RSC-capable components are affected, and many enterprises still run pure SPA React front ends that are completely out of scope.

Why not lower?

A lower severity would ignore the facts that matter most operationally: pre-auth remote code execution, public PoCs, active exploitation, and KEV status. This is not a post-auth admin bug or an internal-only chain; if the vulnerable app is reachable, the attacker can be outside your perimeter and still get code execution.

05 · Compensating Control

What to do — in priority order.

  1. Patch exposed RSC stacks now — Upgrade affected React packages to 19.0.1, 19.1.2, or 19.2.1, and upgrade downstream frameworks like Next.js to fixed builds where applicable. Because this is KEV-listed and actively exploited, do this immediately, within hours, not on the normal CRITICAL queue.
  2. Turn on managed WAF coverage — Enable vendor-managed rules at the edge for React2Shell coverage, including Cloudflare or AWS WAF protections if those platforms front the app. This is a same-day emergency brake within hours that buys time while patching rolls through.
  3. Block Server Action traffic if unused — If the application does not need Server Actions or RSC write paths, block the relevant request patterns and headers at the reverse proxy or WAF. This is a within-hours containment move that meaningfully reduces exposed attack surface.
  4. Reduce public exposure — Put admin, preview, staging, and low-business-value RSC apps behind VPN, access proxy, or IP allowlists. For a KEV-listed pre-auth RCE, shrinking the exposed population within hours is worth more than arguing about exact exploit syntax.
  5. Hunt for app-server secret abuse — Review environment variables, cloud-role use, outbound connections, child-process execution, and unusual Next.js/Node.js activity on exposed app servers. Run this immediately and continue during patch rollout, because successful exploitation often turns into credential theft and cloud pivoting.
What doesn't work
  • MFA does not help because the exploit is unauthenticated and hits the application server before any user login flow matters.
  • Regex-only custom WAF signatures are unreliable because public research already discusses evasions and protocol-compliant payload mutation.
  • SAST on your own app code misses the point if the vulnerable behavior lives in transitive framework packages already shipped to production.
  • Patching only react and react-dom` is insufficient if the vulnerable react-server-dom-*` package remains present.
06 · Verification

Crowdsourced verification payload.

Run this on the application repo checkout, build workspace, or target host filesystem where package-lock.json and/or node_modules exist. Invoke it as python3 verify_cve_2025_55182.py /path/to/app; no admin rights are required for read access. It checks package-lock.json, npm-shrinkwrap.json, and installed node_modules for vulnerable react-server-dom-* package versions and prints VULNERABLE, PATCHED, or UNKNOWN.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
# verify_cve_2025_55182.py
# Detect vulnerable React Server Components package versions for CVE-2025-55182.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN / error

import json
import os
import sys
from typing import Dict, List, Optional, Tuple

TARGETS = {
    'react-server-dom-webpack': {
        'vuln': [('19.0.0', '19.0.0'), ('19.1.0', '19.1.1'), ('19.2.0', '19.2.0')],
        'fixed': ['19.0.1', '19.1.2', '19.2.1']
    },
    'react-server-dom-parcel': {
        'vuln': [('19.0.0', '19.0.0'), ('19.1.0', '19.1.1'), ('19.2.0', '19.2.0')],
        'fixed': ['19.0.1', '19.1.2', '19.2.1']
    },
    'react-server-dom-turbopack': {
        'vuln': [('19.0.0', '19.0.0'), ('19.1.0', '19.1.1'), ('19.2.0', '19.2.0')],
        'fixed': ['19.0.1', '19.1.2', '19.2.1']
    }
}


def norm(v: str) -> Optional[Tuple[int, int, int]]:
    if not v or not isinstance(v, str):
        return None
    v = v.strip()
    if v.startswith('v'):
        v = v[1:]
    v = v.split('-')[0].split('+')[0]
    parts = v.split('.')
    if len(parts) < 3:
        return None
    try:
        return tuple(int(x) for x in parts[:3])
    except ValueError:
        return None


def cmp_ver(a: str, b: str) -> Optional[int]:
    na, nb = norm(a), norm(b)
    if na is None or nb is None:
        return None
    return (na > nb) - (na < nb)


def in_range(v: str, start: str, end: str) -> bool:
    c1 = cmp_ver(v, start)
    c2 = cmp_ver(v, end)
    return c1 is not None and c2 is not None and c1 >= 0 and c2 <= 0


def is_vulnerable(pkg: str, version: str) -> bool:
    if pkg not in TARGETS:
        return False
    for start, end in TARGETS[pkg]['vuln']:
        if in_range(version, start, end):
            return True
    return False


def flatten_lock_deps(obj, found: Dict[str, List[str]]):
    if isinstance(obj, dict):
        if 'name' in obj and 'version' in obj:
            name = obj.get('name')
            ver = obj.get('version')
            if name in TARGETS and isinstance(ver, str):
                found.setdefault(name, []).append(ver)
        if 'packages' in obj and isinstance(obj['packages'], dict):
            for path, meta in obj['packages'].items():
                if not isinstance(meta, dict):
                    continue
                name = meta.get('name')
                ver = meta.get('version')
                if not name and isinstance(path, str) and 'node_modules/' in path:
                    name = path.split('node_modules/')[-1]
                if name in TARGETS and isinstance(ver, str):
                    found.setdefault(name, []).append(ver)
        if 'dependencies' in obj and isinstance(obj['dependencies'], dict):
            for name, meta in obj['dependencies'].items():
                if name in TARGETS and isinstance(meta, dict) and isinstance(meta.get('version'), str):
                    found.setdefault(name, []).append(meta['version'])
                flatten_lock_deps(meta, found)
        for v in obj.values():
            flatten_lock_deps(v, found)
    elif isinstance(obj, list):
        for item in obj:
            flatten_lock_deps(item, found)


def inspect_lockfile(path: str, found: Dict[str, List[str]]):
    try:
        with open(path, 'r', encoding='utf-8') as f:
            data = json.load(f)
        flatten_lock_deps(data, found)
    except Exception:
        pass


def inspect_node_modules(root: str, found: Dict[str, List[str]]):
    nm = os.path.join(root, 'node_modules')
    if not os.path.isdir(nm):
        return
    for pkg in TARGETS:
        pkg_json = os.path.join(nm, pkg, 'package.json')
        if os.path.isfile(pkg_json):
            try:
                with open(pkg_json, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                ver = data.get('version')
                if isinstance(ver, str):
                    found.setdefault(pkg, []).append(ver)
            except Exception:
                pass


def uniq_versions(found: Dict[str, List[str]]) -> Dict[str, List[str]]:
    out = {}
    for k, vals in found.items():
        dedup = sorted(set(vals), key=lambda x: norm(x) or (999, 999, 999))
        out[k] = dedup
    return out


def main():
    root = sys.argv[1] if len(sys.argv) > 1 else os.getcwd()
    if not os.path.isdir(root):
        print('UNKNOWN - path does not exist or is not a directory: {}'.format(root))
        sys.exit(2)

    found: Dict[str, List[str]] = {}
    for lf in ['package-lock.json', 'npm-shrinkwrap.json']:
        p = os.path.join(root, lf)
        if os.path.isfile(p):
            inspect_lockfile(p, found)
    inspect_node_modules(root, found)

    found = uniq_versions(found)

    if not found:
        print('UNKNOWN - no target packages found in package lockfiles or node_modules under {}'.format(root))
        sys.exit(2)

    vulnerable = []
    patched = []
    unknown = []

    for pkg, versions in found.items():
        for ver in versions:
            n = norm(ver)
            if n is None:
                unknown.append((pkg, ver))
            elif is_vulnerable(pkg, ver):
                vulnerable.append((pkg, ver))
            else:
                patched.append((pkg, ver))

    if vulnerable:
        details = ', '.join(['{}@{}'.format(p, v) for p, v in vulnerable])
        print('VULNERABLE - {}'.format(details))
        sys.exit(1)

    if patched and not vulnerable:
        details = ', '.join(['{}@{}'.format(p, v) for p, v in patched])
        print('PATCHED - {}'.format(details))
        sys.exit(0)

    details = ', '.join(['{}@{}'.format(p, v) for p, v in unknown]) if unknown else 'unable to classify versions'
    print('UNKNOWN - {}'.format(details))
    sys.exit(2)


if __name__ == '__main__':
    main()
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning, treat every internet-facing server-side React estate using RSC-capable packages as an incident-level triage item: identify exposed apps, turn on edge protections, and isolate or block unused Server Action/RSC paths immediately, within hours because KEV and active exploitation override the normal CRITICAL timing. Under the noisgate mitigation SLA, mitigation is immediate in this case; under the noisgate remediation SLA, the actual vendor patch must land within 90 days, but for public-facing production apps the practical expectation is same-day or next-change-window patching, not leisurely quarter-end cleanup.

Sources

  1. React advisory
  2. GitHub security advisory GHSA-fv66-9v8q-g76r
  3. CVE record
  4. CISA KEV catalog
  5. AWS threat intelligence blog
  6. Cloudflare WAF protection advisory
  7. GreyNoise exploitation analysis
  8. Censys advisory
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.