Normal view

Received — 23 April 2026 Imperva Cyber Security Blog

React2DoS (CVE-2026-23869): When the Flight Protocol Crashes at Takeoff

9 April 2026 at 16:54

Executive Summary

In this article, we disclose a new high severity unauthenticated remote denial‑of‑service vulnerability we identified and reported in React Server Components that we’ve dubbed “React2DoS”.  In this blog, we’ll analyze its impact and place it in the broader context of recently found Flight protocol vulnerabilities, especially CVE‑2026‑23864.

Introduction

We are in a phase of the web where performance and developer experience are no longer trade-offs, they’re expectations. Modern frameworks compete to ship less JavaScript, reduce client-side complexity, and move logic back to the server.

React, as one of the dominant forces in frontend development, has been at the forefront of this evolution. With the introduction of React Server Components (RSC), the ecosystem embraced a new model: components that execute exclusively on the server, access databases and secrets directly, and stream a serialized UI representation to the client.

This architecture promises smaller bundles, cleaner separation of concerns, and more efficient rendering. Instead of hydrating everything on the client, Server Components emit a structured stream that the browser reconstructs locally.

At the heart of this mechanism lies a custom streaming protocol known as Flight. Through Flight, React can serialize complex structures, like arrays, maps, object references, even promises and async boundaries, allowing the server to describe rich UI trees in a compact format.

This is powerful.

But history has shown that when we introduce custom serialization formats and complex parsers, we also introduce risk. The server must deserialize and reconstruct object graphs from client-controlled input. And complex parsing logic has long been fertile ground for vulnerabilities.

In our research we discovered a denial-of-service vulnerability that allows an attacker to impose disproportionate computation to the remote server.

React2Shell and subsequent DoS vulnerabilities

Earlier this year, the disclosure of React2Shell caught much of the community off guard, triggering emergency patches and intense scrutiny of the React Server Components architecture, amplified by waves of low-quality AI-generated analysis that blurred the line between verified facts and speculation. This episode also prompted deeper investigations into and led to new discoveries related to the security of the Flight protocol and related parsing mechanisms.

CVE‑2026‑23864 (CVSS 3.1 of 7.5), stood out as a notable example and serves as a useful reference for understanding the mechanics behind the issue we explore in this research.

Among other vectors, this vulnerability concerned the BigInt deserialization path in Flight:

  • $n markers denote BigInt values
  • No limit was enforced on digit length

Therefore, sending a million‑digit BigInt could cause a significant computation cost, and CPU exhaustion. An example payload could look like this:

0:"$n9999999999...[repeated 1 million times]"

In our setup, a single query like this could delay the server’s execution by several seconds if the inbound payload reaches the maximum allowed size (1MB with Node.js runtime, 10MB with Edge runtime).

This was the starting point of our research, and we tried to find payload that would trigger a similar, or superior cost to the server. This is exactly what we found, actually more computationally-intensive  by several orders of magnitude.

React2DoS

React relies on a mechanism known as the React Flight Protocol to serialize values that are sent to Server Functions.

On the client side, data is transmitted to the server as small pieces (or “chunks”), for example through form submissions:

payload = {
  "0": (None, '["$1"]'),
  "1": (None, '{"category":"vehicle","model":"$2:modelName"}'),
  "2": (None, '{"modelName":"tesla"}'),
}

As illustrated above, these chunks can reference one another.

After deserialization on the server, the reconstructed object looks like this:

{ "category": "vehicle", "model": "tesla" }

At first, we tried to measure the cost of execution of every type of reference supported by the Flight protocol. Among them, we looked at two promising ones: $Q and $W, respectively instantiating new Maps and Sets from the client request payload.

The first observation we made was that it was possible to reference the root element in the root element itself (!), which paved the way to recursive expressions:

“0” : [“$Q0”]

This, would cause the execution of the following JavaScript expression:

New Map([null])

Which makes perfect sense, because at the time of resolution of $Q0, $0 is not known yet.

However, what surprised us, was the fact that the following expression:

“0” : [“$Q0”, “$Q0” ..., “$Q0”] (x n)

did trigger the execution of the Map constructor n times!

Indeed, the ReactFlightReplyServer uses a `consumed` attribute to prevent multiple computations of the same reference and prevent abuse. But this mechanism only enters in action when the reference is successfully resolved (see Fig 1).

Screenshot 2026 04 09 at 7.46.50 AM

Fig. 1: Exception doesn’t prevent recomputation of the same faulty Map 

Because the `new Map` expression failed (new Map([null]) is not a valid JavaScript expression), this outcome was not stored anywhere. But surprisingly, the deserialization is not interrupted by this exception! 

The execution of the expression `new Map ([null])` is pretty cheap, it takes our server around 0.03ms. Virtually instant. But this is neglecting the fact that  a threat actor can insert more than 100,000 instances in a 1MB payload, leading to the cost of several seconds, comparable to the CPU exhaustion issue behind CVE‑2026‑23864 and described above. 

Considering this, we submitted a first report to Meta, sharing this POC and demonstrating the impact. 

But soon after, we realized there was a way more impactful payload we could generate by exploiting our original idea.  

Instead of sending a series of “$Q0” that would immediately trigger the exception, we decided to introduce a series of valid map entries at the start of the root entry, to force the Map constructor to iterate over them before triggering the expected exception (see Fig. 2). 

Screenshot 2026 04 09 at 7.47.57 AM

Fig. 2: Internal recursive resolution of “$0” 

By doing so, we achieved a quadratic complexity, and a much more expensive payload ! The optimal number setting is n/2 valid maps and n/2 map references to the 0 object (“$Q0”). 

CVE‑2026‑23864 (CPU exhaustion) vs React2DoS (CVE-2026-23869) 

With our new attack vector, the computation could easily last several minutes. Therefore, with only small payloads of tens of kilobytes, it was possible to initiate impactful DoS attacks. 

To give ourselves an idea of the impact of this attack vector, we computed a chart showing the comparison between CVE‑2026‑23864 (CPU exhaustion) and React2DoS. The result showed that after only a few kilobytes, React2DoS starts to stand out, and when the payload size reaches hundreds of kilobytes, it is already more powerful by several orders of magnitude (see Fig. 3). 

Screenshot 2026 04 09 at 7.49.09 AM

Fig. 3: Comparison React2DoS – CVE‑2026‑23864 

Therefore, with a single request, a threat actor can trigger a computation that will take minutes to handle. By repeating this, complete denial of service can be achieved. 

Mitigation 

The React team fixed this issue via setting the consumed flag before any map/set constructor was called.

The issue affects React Server Components version 19.2.4 and below. We recommend that you update to the latest available version that patches this vulnerability as soon as possible.  

If your application already sits behind an Imperva proxy, it is automatically protected against this attack. 

Conclusion 

This case highlights an important reality: the path to innovation inevitably introduces complexity, and therefore risk. As ecosystems evolve rapidly, staying up to date and remaining aware of newly discovered security issues is essential. 

In a more personal way, it was a pleasure for me to delve into one of the most used framework in the world and discover a finding with meaningful impact. This wouldn’t have been possible if researchers before didn’t pave the way with their investigations and their recent findings (React2Shell,  CVE‑2026‑23864…).  

Disclosure Timeline 

Feb 3 2026 – Report including first payload 

Feb 5 2026 – Second payload reported 

April 8 2029 – Vulnerability fixed in 19.2.5 (patch backported to versions 19.0.5, 19.1.6)

The post React2DoS (CVE-2026-23869): When the Flight Protocol Crashes at Takeoff appeared first on Blog.

Received — 12 March 2026 Imperva Cyber Security Blog

N8N: Shared Credentials and Account Takeover

3 March 2026 at 23:41

Executive Summary

We identified a security weakness in n8n’s credential management layer that could have completely compromised the application’s security. This finding highlights the core risks of centralized authentication in workflow automation platforms.

As n8n serves as the central hub connecting critical systems and orchestrating business processes across teams, any gap in credential handling can potentially cascade across connected systems, disrupting operations, compromising data flows, and credentials.

While this issue was fixed in v2.6.4, it reminds us about the unique security challenges of AI automation platforms.

Introduction

We are in a moment where AI and automation platforms are rapidly becoming embedded in everyday operations, allowing teams to connect models, APIs, SaaS tools, and internal systems with minimal friction.

Platforms like n8n promise powerful automation through visual workflows and reusable credentials, lowering the barrier to orchestrating complex tasks across services. But this convenience comes with structural risk: these tools centralize highly sensitive tokens, OAuth flows, and API keys, effectively concentrating trust in a single automation layer.

When that layer fails to enforce basic security controls, the impact is not limited to one workflow, it can extend across every connected system. In this research, we examine how a Stored XSS vulnerability in n8n’s OAuth credential handling can lead to account takeover and broader instance compromise.

The Vulnerability

The vulnerability lies in how n8n handles the “Authorization URL” within the OAuth credential setup. OAuth (Open Authorization) is an authorization framework that allows an application to access a user’s data on another service without exposing the user’s password.

In a standard workflow, users configure OAuth credentials to authenticate n8n with an external provider. When a user clicks “Connect my account,” n8n opens a popup window pointing to the service’s authorization page.

However, we discovered that the frontend function responsible for opening this window did not validate the protocol of the provided URL (see below). This allowed an attacker to bypass the expected scheme and inject JavaScript code.

The Attack Flow

Because n8n allows credentials to be shared between users in the same instance (collaborative features), a threat actor can weaponize this weakness, see Fig 1.

Screenshot 2026 03 03 at 11.23.08 AM

Fig. 1: High level view of the attack flow

The steps are the following:

  1. Preparation: The attacker creates a new credential using the “OAuth2 API” type.
  2. Injection: In the “Authorization URL” field, instead of a valid URL, the attacker inserts a malicious JavaScript payload.
  3. Trap: The attacker shares this credential with the victim (e.g., an administrator or a user with higher privileges).
  4. Execution: The victim, seeing a shared credential, opens it and clicks “Connect my account.” The browser immediately executes the injected JavaScript in the context of the victim’s session instead of navigating to the remote authorization URL.

Demonstration Video

The following video demonstrates the exploitation chain: sharing the malicious credential with a victim account and triggering the XSS payload.

Root Cause

During the OAuth flow, the browser initiates a top-level navigation to the authorization URL in the oAuthCredentialAuthorize function of the credential service. However, this segment of the program missed sanitation of the Authorization URL.

Screenshot 2026 03 03 at 12.05.56 PM

Fig. 2: Vulnerable source code

Impact: Application Compromise

This is a stored XSS, meaning the payload is saved permanently in the database and served to any user who interacts with the credential. The impact of executing arbitrary JavaScript in the context of an n8n session is significant:

  1. Account Takeover: The attacker can impersonate the victim’s in his session and force actions on their behalf, effectively taking over the account.
  2. Credential Exfiltration: The attacker can then use the XSS to query the internal n8n API and retrieve other credentials stored in the instance.
  3. Instance Control: With admin access gained via the XSS, the attacker can access more credentials, escalate privileges, and gain full control of the n8n instance.

Conclusion

Workflow automation tools like n8n are becoming the backbone of modern IT infrastructure. While they offer immense power and speed, they also centralize trust. A vulnerability in this layer can often be more damaging than a vulnerability in a single isolated application.

We recommend organizations treat their automation platforms as Tier-0 assets, enforce strict access controls, and ensure they are patched promptly.

Timeline

  • Jan 29 : Disclosure of the issue
  • Feb 6 : Issue fixed in v2.6.4

The post N8N: Shared Credentials and Account Takeover appeared first on Blog.

Received — 11 January 2026 Imperva Cyber Security Blog

Code Execution in Jupyter Notebook Exports

16 December 2025 at 20:43

After our research on Cursor, in the context of developer-ecosystem security, we turn our attention to the Jupyter ecosystem. We expose security risks we identified in the notebook’s export functionality, in the default Windows environment, to help organizations better protect their assets and networks.

Executive Summary

We identified a new way external Jupyter notebooks could be exploited by threat actors to lure unsuspecting users and compromise their workstation.

Companies are recommended to use a centralized Jupyter server, stay up to date and strictly restrict external files susceptible to processing with Jupyter software.

Introduction

Jupyter notebook is quite an institution in the development of AI projects. Back in 2015, around 200,000 notebooks were publicly available on GitHub—by early 2021 that number had surged to nearly 10 million. Used by more than 80 % of data scientists and AI engineers worldwide, Jupyter is deeply embedded in every stage of AI workflows, from exploratory analysis and visualization to model prototyping and collaboration.

When investigating this ecosystem, our approach was to try to imagine where a threat actor could find his way through, and leverage functionalities to exploit victims’ environments. The first direction came surprisingly easily: the configuration files.

Configuration files are often considered innocuous. However, they may include obscure parameters that most users aren’t aware of. Ignoring them would be a critical mistake.

Config files have led to vulnerabilities in many other instances. For example, in VSCode’s IDE, the .vscode/settings.json config file was also a key component in multiple high severity vulnerabilities discovered (CVE‑2021‑34529 , CVE‑2025‑53773 or CVE-2025-54130).

One specificity of the Jupyter ecosystem that makes this attack vector even more interesting is the fact that configuration files are also perfectly valid Python executables- making them easier to exploit.

Jupyter Configuration Files

The most common configuration file is jupyter_notebook_config.py, typically found in the user-specific configuration directory (~/.jupyter/). It’s responsible for defining core Notebook server settings such as network bindings, authentication options, file system paths, and various security-related parameters. However, other config files may also be used depending on the component, such as jupyter_nbconvert_config.py for export settings, or jupyter_server_config.py for Jupyter Server.

Configuration files can actually exist in any directory, allowing for layered overrides. Available options cover a wide range of functionality, from UI behavior and authentication to kernel management, export formats, logging, and more. This approach gives users fine-grained control over the entire Jupyter ecosystem.

For example:

c = get_config()
c.NotebookApp.port = 8888
c.FileContentsManager.save_script = True

However, acknowledging a high severity impact, Jupyter decided in October 2022 to remove CWD from the config paths, reducing the risk presented significantly.

This was the starting point of our research. We started searching for a similar or stronger way to exploit the same idea: having a file whose name is not constrained adjacent to a jupyter notebook, assuming an unsuspecting user would trigger an innocuous operation on a perfectly legit Jupyter notebook on the official Jupyter software and inadvertently allow full system compromise.

And this is exactly what we found by investigating the official export tool of Jupyter, nbconvert.

The Vulnerability

The vulnerability we discovered allows arbitrary code execution on Windows machines when exporting a notebook to PDF. By placing a properly named, malicious script in the notebook folder location, an attacker could hijack the conversion process and execute code with the privileges of the user.

When a Jupyter notebook containing SVG output is exported via nbconvert, the svg2pdf.py preprocessor is triggered to convert SVG images via the Inkscape tool. During this process, the path to Inkscape executable is resolved using Python’s shutil.which() via the following expression:

inkscape_path = which("inkscape")

without including inkscape anywhere as a mandatory nbconvert dependency. This opened the door to unintended code execution as the following figure shows:

Screenshot 2025 12 15 at 7.22.07 AM

Fig. 1: High level flow of exploitation of the security issue

shutil.which behavior is controlled internally by the Windows API function NeedCurrentDirectoryForExePathW, which returns TRUE (include CWD) when the NoDefaultCurrentDirectoryInExePath environment variable is not set, which is the default configuration on standard Windows installations.

In Python versions earlier than 3.12, `shutil.which()` ignores the `NoDefaultCurrentDirectoryInExePath` environment variable entirely, making it impossible to prevent this unsafe search behavior through configuration.

Python 3.12 and later versions properly respect this environment variable when set, but the variable remains unset by default on Windows systems, leaving many vulnerable.

Since nbconvert officially supports Python versions starting from 3.9, it includes versions that are affected by this issue both ways.

CVE-2025-53000

This unsafe lookup behavior aligns with CWE-427: Uncontrolled Search Path Element. Therefore, we recommended disabling the searching of inkscape software from CWD and relying on fixed safe search places.

Upon receiving our report, the Jupyter team reproduced the issue, acknowledged the associated risk, and requested a CVE (see below). A discussion was then initiated regarding how to fix the issue. However, the Jupyter team eventually stopped responding to our messages and has not addressed the issue to date.

CVE-2025-53000 has been assigned to this vulnerability. At the time of publication, the Github advisory has not yet been released by the maintainers.

Because export functionality is commonly used and generally trusted, it presents an attractive target for attackers, and especially in environments where notebooks are frequently shared—such as academic research groups, data science teams, or educational institutions—the potential for exploitation increases substantially.

Eventually, following our 90-day policy, we decided to publish this advisory to help protect the community.

Demonstration Video

The following demonstration video was recorded on a Windows 10 Enterprise x64 machine with default settings, using miniconda3 and Python 3.13.9, using the latest available Jupyter software versions, including:

Jupyter Core 5.9.1, nbconvert 7.16.6, and Notebook 7.5.0

Post Exploitation

Once successfully triggered, this vulnerability gives the attacker arbitrary code-execution in the context of the user. This immediately impacts confidentiality, integrity, and availability, as the attacker can access, modify, or disrupt the user’s data and workflows. On typical Windows data-science workstations, victim accounts almost always have:

  • Direct access to sensitive notebooks and datasets.
  • Cached cloud credentials (AWS CLI, Azure CLI, gcloud, Databricks etc.)
  • Locally installed package managers (conda, pip, winget) and DevOps pipelines that will happily run additional code.

This potentially amplifies the radius of compromise, allowing its effects to spread beyond the initial workstation.

Recommendations

Companies are recommended to rely on a centralized Jupyter server, ensure that all Jupyter-related software remains up to date, and enforce strict restrictions on external files that may be processed through Jupyter tools.

It is also recommended to enable the NoDefaultCurrentDirectoryInExePath environment variable to reduce the risk of unintentionally executing files from untrusted locations.

Conclusion

This vulnerability shows how the invisible glue of our workflows can become points of failure when not properly scrutinized.

We expect more vulnerabilities to surface in this fast-growing AI ecosystem as workflows become more automated, composable, and cloud-integrated, and we hope this report encourages teams to take a closer look at the quiet dependencies holding their environments together.

Timeline

  • June 8: Disclosure report submitted.
  • June 12: Issue reproduced.
  • June 25: CVE reservation by Jupyter team.

The post Code Execution in Jupyter Notebook Exports appeared first on Blog.

❌