DEV Community

Cover image for How a Serialization Flaw in React 19 Server Components Led to Remote Code Execution
Shibly Noman
Shibly Noman

Posted on

How a Serialization Flaw in React 19 Server Components Led to Remote Code Execution

A critical security incident in early December 2025 drew widespread attention when the React ecosystem was found to contain a maximum-severity remote code execution vulnerability, tracked as CVE-2025-55182 and colloquially dubbed “React2Shell.” This flaw, which affects React Server Components (RSC) and frameworks built on top of them, could allow an unauthenticated attacker to execute arbitrary code on vulnerable servers with a single crafted HTTP request.

What Was the Vulnerability?

To understand the vulnerability, it is necessary to first examine how React 19 Server Components exchange data between the server and the client.

React Server Components do not transmit plain JSON. Instead, they rely on a specialized serialization format known as the Flight protocol, which is capable of encoding complex JavaScript constructs such as references, deferred values, and asynchronous boundaries. Within this protocol, a serialized payload can represent unresolved asynchronous data effectively indicating that a particular value is a Promise whose resolution will be provided at a later stage.

This bidirectional communication model allows the client to send references back to the server to complete the rendering process. While this design enables efficient streaming and incremental rendering, it also introduces significant complexity and expands the attack surface.

At the core of the vulnerability is the server-side logic responsible for parsing and reconstructing these serialized values. Specifically, the method responsible for parsing model strings within React Server Components defines how incoming Flight payloads are deserialized into executable JavaScript structures on the server. Insufficient validation during this reconstruction process ultimately enabled the exploitation of the serialization pipeline.

function parseModelString(
    response: Response,
    obj: Object,
    key: string,
    value: string,
    reference: void | string,
) : any {
    switch (value[1]) {
        ...
            case 'B': {
                // Blob
                const id = parseInt(value.slice(2), 16);
                const prefix = response._prefix;
                const blobKey = prefix + id;
                // We should have this backingEntry in the store already because we emitted
                // it before referencing it. It should be a Blob.
                const backingEntry: Blob = (response._formData.get(blobKey): any);
                return backingEntry;
            }
    }
}
Enter fullscreen mode Exit fullscreen mode

How the Serialization Vulnerability Works

The vulnerability stems from how React Server Components reconstruct complex JavaScript objects from serialized Flight payloads. During deserialization, the server failed to enforce strict validation on property access paths, allowing crafted payloads to reference inherited and magic properties such as constructor, __proto__, or prototype. By injecting a malicious then property, an attacker could create a thenable object, which the JavaScript runtime automatically attempts to resolve as a Promise. Through chained references (for example, resolving then via the prototype chain), the deserialization logic could be coerced into invoking the JavaScript Function constructor, resulting in arbitrary code execution. This exploit was possible because the server implicitly trusted the payload structure and did not differentiate between own properties and inherited properties during model reconstruction.

vulnerability_analysis_react_19_ssr

Example of a Crafted Flight Payload (Simplified):

const _payload = {
...
    1 : {
        status: “resolve_model”,
        reason: 0,
        _response: {
            _prefix: “console.log('Hello World!')”,
            _fromData: {
                 get: “$1: then:constructor”,
                }
        },
        then: “$1:then”,
        value:’{”then”: “$B”}’
    },
    2: “$@1”
};
Enter fullscreen mode Exit fullscreen mode

Why this works (at a high level):

  • The payload introduces a fake then property, making the object appear promise-like.
  • The de-serializer resolves references without restricting prototype traversal.
  • Reference chaining allows access to inherited constructors.
  • Implicit Promise resolution triggers execution during model reconstruction.

Mitigation & Fix Overview

Following the disclosure of CVE-2025-55182, the React core team, in coordination with platform and infrastructure providers, implemented a series of layered mitigations to neutralize the immediate exploitation vector and to strengthen the overall security posture of React Server Components.

These remediation efforts focus on robust deserialization safeguards, reduced protocol expressiveness, and the removal of implicit execution semantics that previously enabled unsafe behavior.

The key mitigation measures include:

  • Strict Property Validation During Deserialization

    Enforcement of explicit property allow-lists and ownership checks to prevent access to inherited or magic properties during model reconstruction.

  • Prototype-less Object Reconstruction

    Use of objects without prototype chains to eliminate unintended access to JavaScript runtime primitives and inherited behavior.

  • Explicit Promise Handling (Elimination of Implicit Thenables)

    Removal of reliance on JavaScript’s automatic thenable resolution in favor of explicit and controlled async handling.

  • Constrained Flight Protocol Grammar

    Reduction of permissible reference paths, indirection depth, and token combinations to limit the protocol’s attack surface.

  • Defense-in-Depth Runtime Guards

    Introduction of runtime assertions, depth limits, and fail-fast mechanisms to prevent unsafe execution even in the presence of malformed payloads.

Collectively, these measures eliminate the known remote code execution vector and significantly raise the barrier for similar classes of deserialization-based vulnerabilities in future releases.

Guidance for Application Developers

While the vulnerability was in React itself, developers should still adopt the following best practices:

  • Keep React and framework dependencies fully up to date
  • Avoid exposing Server Component endpoints to untrusted clients
  • Apply authentication and CSRF protections to RSC routes
  • Monitor for abnormal payload sizes or resolution patterns
  • Treat framework serialization as untrusted input

Top comments (0)