How I Detected and Stopped a Real-World RCE Attack on My Next.js App (CVE-2025-55182 / React2Shell)

Last week I experienced one of the most eye-opening security incidents in my time building web apps.
My production Next.js application was actively targeted by attackers attempting remote code execution (RCE), and the root cause was a newly disclosed vulnerability in React Server Components.

This is a write-up of what happened, how I diagnosed the issue, how I patched it, and what steps I took to harden my infrastructure.

1) The First Signs: Suspicious Logs in My Container

Everything began when I noticed strange logs while checking my Docker containers:

/bin/sh: curl: not found
/base64 -d | bash
ping: bad address
cat: can't open '.env.production'

Attackers were:

  • Attempting to download malicious scripts
  • Trying to exfiltrate environment variables
  • Dropping files into /tmp
  • Running reconnaissance commands
  • Injecting base64-encoded payloads

At this stage, nothing persisted on the filesystem, and no malicious binaries survived container restarts, which was a good sign.

Still, something was definitely trying to execute shell commands inside my app.

2) Investigating the Root Cause

My first instinct was to examine my own server-side code:

  • I reviewed all route handlers
  • Audited every Server Action
  • Scanned the codebase for any child_process.exec, spawn, or CLI wrappers.

None of my code executed shell commands, nothing even close.
This led to the next logical suspicion: a framework-level vulnerability, and that’s exactly what it was.

The Culprit: CVE-2025-55182 (React2Shell)

It turns out my app was running a Next.js version that used a vulnerable build of React Server Components, specifically the versions affected by CVE-2025-55182 – React2Shell.
A critical, pre-authentication RCE caused by unsafe deserialization in React’s RSC/Flight protocol.

With a single crafted HTTP request, an attacker could trigger arbitrary code execution inside the Node.js server, even with no custom API endpoints.
I tested the publicly available PoC against my running app…
And it worked.

Suddenly everything made sense.

3) Fixing the Vulnerability

The React team released patches, and Next.js provided an official tool to upgrade vulnerable packages.
To fix the issue, I ran:

npx fix-react2shell-next

This updated:

  • React RSC internals
  • Next.js internals depending on the vulnerable code path

I rebuilt and redeployed my app from clean Docker images.
Then I tested the PoC again: Exploit failed. Completely.
This was the moment I knew the issue was resolved.

Successful patch

Running npx fix-react2shell-next on an affected Next.js app – confirming active vulnerabilities and applying the official patch.

4) Hardening My Infrastructure (This Saved Me)

Even though the vulnerability existed, the attacker never gained persistence or escalated to the host. Why?
Because I had already hardened my Docker environment:

  • Read-only root filesystem
  • /tmp mounted with noexec
  • All Linux capabilities dropped
  • no-new-privileges enabled
  • No bash, curl installed inside the container
  • Isolated writable directories
  • Nginx rate limiting and reverse-proxy filtering

These security layers prevented: file execution, script downloads, persistence installation, cron modifications, privilege escalation, writing outside isolated volumes.
In the end: the vulnerability allowed RCE, but the environment stopped it from becoming a full compromise.

5) Post-Incident Actions

Even after patching, I also:

  • Rotated all environment secrets
  • Re-deployed everything from clean images
  • Validated no malicious files remained
  • Reviewed logs to confirm attack attempts were blocked

Because as we often say: Security is not just about fixing one issue, it’s an ongoing process.

6) Key Takeaways for Developers

Here’s what I learned and what you should be doing too:
a. Patch fast
React2Shell was actively exploited in the wild. If your app uses Next.js + RSC, you must update.
b. Least privilege saves lives
A read-only root filesystem alone can break 80% of real-world attack chains.
c. Don’t rely on your own code being perfect
Framework vulnerabilities happen and attackers move fast.
d. Use multiple layers of defense
No single measure stopped this attack, together, they did.
e. Monitor everything
Logs revealed the attack before it was too late.

7) The Bottom Line

This experience was intense but deeply educational. I’m sharing this story only a few days after the attack, but incidents like this can happen to anyone, and every victim has their own story behind it.

It reinforced how important it is to stay up to date, harden your runtime environment, assume that vulnerabilities will happen, and prepare your infrastructure to handle them safely.

If even one person reading this patches their application or improves their security setup, then writing this was absolutely worth it.

References:

Leave a Reply