An in-depth account of how researchers discovered and disclosed CVE-2025-55182, a critical RCE vulnerability in React Server Components, and the aftermath that followed.
On December 3rd, 2025, Meta disclosed CVE-2025-55182, a vulnerability that researchers would later dub 'react2shell' - an unauthenticated Remote Code Execution flaw in React Server Components. The technical community quickly recognized its significance, as it exploited a fundamental weakness in the Flight protocol's type validation system, allowing the construction of arbitrary chunks and access to object prototypes. While detailed technical breakdowns emerged from researchers like Lachlan Davidson, the story of how this vulnerability was discovered, the ethical considerations surrounding its disclosure, and the subsequent cat-and-mouse game with web application firewalls reveals much about contemporary vulnerability research in the JavaScript ecosystem.
The discovery began in late November 2025 when Lachlan Davidson, a researcher with whom I had connected through robotics back in 2019, noticed something peculiar in a shared group chat. What started as a casual exploration of potential JavaScript jail challenges quickly evolved into something far more significant. The initial primitives available through Flight included basic types like String, Number, Array, Map, Date, BigInt, Object, and Function. With these, we could construct arbitrary function objects using techniques like Object.constructor.constructor("console.log('meow')"), but remained unable to execute them due to access restrictions.

The geographical distance between us—GMT-7 for me and GMT+13 for Lachlan, creating a 20-hour time zone difference—became an unexpected advantage in our collaborative research. While one of us slept, the other continued investigating, creating a near-constant cycle of discovery and refinement. Our initial discussions were filled with a mix of skepticism and excitement, with frequent comments like "lol wouldn't it be crazy if we found an RCE in React" reflecting our disbelief that such a fundamental flaw could exist in one of the web's most widely used libraries.
As we delved deeper, we shifted our discussion from the public group chat to direct messages, recognizing that we might be dealing with something of extraordinary significance. We focused our attention on NextJS, selecting it among various React-based frameworks due to its widespread adoption and the practical value of having a working exploit against it. The challenge wasn't just in identifying the vulnerability but in understanding the precise mechanics of React's Server Components implementation and how the Flight protocol processed data between client and server.
The breakthrough came after several days of incremental progress. Lachlan discovered the ability to access certain imports and bind arguments to them, though Webpack's restrictions limited the usefulness of available imports. My own exploration led me to examine Node.js source code and documentation, particularly focusing on the module system. After a Thanksgiving dinner party spent surreptitiously reading Node documentation on my phone, I returned to my desk and made a critical realization: executing JavaScript from disk was trivial if only we could get the code there.

Around 5am my time, Lachlan messaged that he had achieved Remote Code Execution. By this point, I had invested approximately 36 hours of active research, while Lachlan had devoted well over a hundred hours to the problem. The immediate reaction wasn't celebration but a sober recognition of what we had discovered. Neither of us being naive about the implications, we both recognized we were essentially in possession of a nuclear weapon capable of compromising a substantial portion of the modern web.
Despite our long-standing friendship and professional respect, the gravity of the situation created a natural tension. We both began moving defensively, uncertain of the other's intentions. The solution we arrived at was both practical and symbolic: a signed, on-paper agreement establishing that Lachlan would handle the disclosure to Meta, we would share any proof-of-concepts with each other, and neither of us would disclose information that would help others reproduce the exploit without mutual consent. We also archived and then purged all messages from our initial discussion server, preserving only what was necessary for documentation.

The disclosure process to Meta was methodical and professional. Following their acknowledgment, we prepared for what would undoubtedly be an intense period of public disclosure. A vulnerability of this magnitude would be exploitable across numerous third-party bug bounty programs, but we understood the necessity of allowing time for patches before submitting any reports. In a moment of characteristic irreverence, I posted a hash of our proof-of-concept on Twitter, creating a breadcrumb trail for those who would eventually piece together the full exploit.
My focus shifted to reconnaissance, with two primary objectives: identifying relevant bug bounty programs and assessing the scale of vulnerable systems on the internet. I developed a backend system using Go and MariaDB to track scanned domains and their vulnerability status. The initial scanning of 4 million domains revealed that approximately 2% were running some form of NextJS, though only versions 15 and higher using the app router were actually vulnerable. This statistic, while seemingly high, masked the reality that many organizations had not updated their installations, inadvertently protecting themselves through inertia.

The reconnaissance process wasn't without its complications. My scanning activity attracted the attention of Hetzner, whose datacenter infrastructure I had initially used for large-scale scanning. After a brief apology, I continued my work from my home IP. The scanning methodology evolved from simple HTTP response inspection to a more sophisticated approach using headless browsers, evaluating next.version in the console after full page load. This provided more accurate version information and helped bypass basic anti-bot measures that have proliferated in response to mass scraping driven by large language models.
As the disclosure date approached, I built a simple frontend for my database of targets, enlisting Claude to create an admin interface using NextJS. The tool allowed me to manage bounty programs, define domain scope, and dispatch both passive subdomain scans and active vulnerability assessments. The amusing detail that Claude selected version 14.2.15 of NextJS—too old to be vulnerable—served as a reminder of the unpredictable nature of AI-assisted development.
When the advisory for CVE-2025-55182 was finally published at 7am Pacific on Wednesday, I revealed the Twitter hash I had posted days earlier. Lachlan and I immediately began testing our exploit against identified targets, achieving quick successes against non-Cloudflare, non-Vercel platforms. However, we soon encountered significant resistance from web application firewalls, particularly Cloudflare's systems which were identifying and blocking our proof-of-concept.
The challenge of bypassing these protections became the next frontier of our research. Cloudflare's regex appeared robust, specifically blocking the ":constructor" syntax essential to accessing the function constructor. While frustrating, this wasn't unexpected, as Meta and Vercel had worked with WAF providers prior to disclosure precisely to anticipate such challenges. Our initial attempts focused on alternative methods for accessing the Function constructor, consuming approximately ten hours with limited success.
Complicating matters further, a fake proof-of-concept gained significant traction on Twitter, causing confusion in the security community. Several reputable sites reported it as authentic without verification, and scanner tools emerged incorrectly claiming sites were safe. This misinformation campaign diverted attention from the actual vulnerability and demonstrated how quickly false information can spread in security circles.
My breakthrough came from an unexpected direction. Instead of treating the problem as a JavaScript jail challenge, I approached it from a traditional WAF bypass perspective. I recalled discussions about how many web application firewalls stop scanning after a certain body size. NextJS limits request bodies to 1MB by default to prevent DoS attacks, and I hypothesized that firewalls might have lower thresholds.
After experimenting with various approaches, I developed a payload that inserted approximately 500KB of junk data within the Flight object structure before the offending text. This technique exploited the difference between NextJS's 1MB limit and the smaller thresholds used by firewall systems. The breakthrough was validated when it successfully bypassed Cloudflare's protections on a test site I had established.

The first public proof-of-concept emerged from researcher maple3142 approximately 30 hours after initial disclosure, ending our brief period of exclusive advantage. According to multiple sources, Amazon's published WAF rules significantly accelerated this timeline by providing hints about the exploit's mechanics. Around the same time, I discovered that Vercel's claimed platform protections were largely ineffective, as demonstrated by successful exploitation against a Vercel-hosted site I had not initially identified as such.
Vercel's response became a significant chapter in this saga. Despite CEO claims that their firewalls completely protected vulnerable sites, evidence suggested otherwise. More importantly, Vercel demonstrated commendable commitment to security by offering $50,000 per unique bypass to their WAF through HackerOne. While an administrative oversight delayed our initial submission (they had forgotten to include the flag in the environment), we eventually developed five unique bypasses:
- A parsing bug where the firewall regex cared about something JavaScript didn't
- String concatenation by importing path.join
- Recursive JSON decoding
- Recursive JSON decoding at two levels deeper
- Abusing Error.toString to get free concatenation with : as a separator
The Vercel WAF challenge lasted approximately one week, with researchers submitting numerous bypass attempts. We suspect Vercel somewhat overestimated the difficulty of finding bypasses while underestimating the number of capable researchers willing to devote significant effort when substantial rewards are available. The company ultimately paid out for 23 unique bypasses, demonstrating their commitment to responsible disclosure and security.
Several near-misses during our research highlighted the delicate balance of JavaScript's type system and its implications for security. We explored String.normalize using NFC by default (which would have worked if constructor had a K), String.join's default argument of ,, and BigInt.toString's default radix of 10. These edge cases revealed how seemingly minor implementation details in JavaScript can have significant security implications.
The busboy library, which claims to support base64-encoded multipart form bodies but actually encodes the body as base64 before returning it (without decoding), provided an amusing distraction during our research. While not directly relevant to our bypasses, this example illustrated how even well-established libraries can contain fundamental misunderstandings of basic functionality.
This entire experience, spanning from initial discovery through disclosure and the subsequent WAF challenges, represented some of the most exhilarating work of my career. The technical complexity, ethical considerations, and sheer scale of affected systems created a perfect storm of challenges and learning opportunities. I remain deeply grateful to Lachlan for including me in this discovery and to all who supported us throughout the process.
The react2shell vulnerability serves as a reminder that even the most mature and widely-used software libraries can contain fundamental flaws that escape detection for years. The discovery process demonstrates the value of collaborative research across time zones and the importance of approaching security problems from multiple perspectives. As the JavaScript ecosystem continues to evolve with increasingly complex frameworks and abstractions, the need for rigorous security review and transparent vulnerability disclosure becomes ever more critical.

Comments
Please log in or register to join the discussion