```html ``` A CVSS 10.0 Exploit for Cal.com Landed on GitHub Last Night. The Bug Is Not Cal.com's — It Is the Next.js They Shipped With.
top of page

A CVSS 10.0 Exploit for Cal.com Landed on GitHub Last Night. The Bug Is Not Cal.com's — It Is the Next.js They Shipped With.

  • Writer: Patrick Duggan
    Patrick Duggan
  • 13 minutes ago
  • 5 min read

At 18:01 UTC on July 28 our exploit harvester logged a new repository on GitHub: a working Python exploit for CVE-2025-71389. The target is Cal.com, the open-source scheduling platform, in every version up to and including 5.9.8. The score is 10.0 on both CVSS 3.1 and 4.0. It is unauthenticated, it is a single request, and it is not on CISA's Known Exploited Vulnerabilities catalog, which means nothing in your patch process is going to raise its hand about it.


We are not claiming a scoop. The CVE record was published July 23 and the fix is in 5.9.9. What our harvester caught is the moment between disclosure and weaponization closing — the part of the timeline that vendor advisories never show you, because by the time anyone writes the retrospective the exploit has been public for months and nobody remembers what week it appeared.


The reason we are writing about this one specifically is that it is the sequel to a post we published on July 14, and the sequel is worse than the original.


What we said on July 14



React2Shell — CVE-2025-55182, the React Server Components deserialization bug — broke at Christmas. Every major vendor covered it in December: Microsoft, Google, Wiz, Cloudflare, Rapid7, Zscaler, JFrog, Qualys, Sysdig. A wall of coverage for one week, and then the news cycle moved on the way it always does.


Our harvester did not move on, because it does not read the news. It watches GitHub. Two weeks ago we published what it had quietly recorded in the seven months since: two dozen separate exploit repositories for the same bug, still arriving, long after the industry had filed it under handled. The argument was that a 10.0 does not get weaponized once. It gets weaponized continuously, by different people, for as long as unpatched instances exist.


That argument was about the same bug being re-tooled. This is the next move, and it is a different shape.


Cal.com did not write this bug



Read the affected-versions data carefully. CVE-2025-71389 is filed against Cal.com. The vulnerable code is not in Cal.com.


Cal.com bundles Next.js 15.x. That Next.js bundles the React Server Components request handling that deserializes attacker-controlled input. The root flaws are CVE-2025-55182 in React and CVE-2025-66478 in Next.js. Cal.com's contribution to this vulnerability was a line in a dependency manifest.


So there are now two ways to be exposed to React2Shell. The first is the one everyone understood in December: you run React, you patch React. The second is the one almost nobody is tracking: you run a product that runs React, and whether you are exploitable is a fact about that product's build, not about anything you installed or chose. You cannot patch it. You can only wait for the vendor to bump their dependency and then update the whole product.


This is soft-surface bleed at the dependency tier. The hard perimeter held — Cal.com's own code is not the problem. The bleed came through the trusted middle, the framework everyone assumed was somebody else's responsibility.


Why Cal.com is a bad host for this particular bug



Self-hosted Cal.com is not a marketing site. It is a scheduling service, which means by design it holds live OAuth tokens for Google Workspace and Microsoft 365 calendars, the meeting history of everyone who books through it, and frequently the video-conferencing integrations attached to both. It is a small, unglamorous internal app that quietly accumulated the credentials to read an organization's calendar.


An unauthenticated remote code execution on that box is not a defacement. It is a token vault with a front door.


And self-hosted is the operative word. Cal.com's cloud instances are the vendor's problem and are presumably already on a fixed build. The exposed population is the organizations who chose to self-host precisely because they did not want their calendar data sitting on somebody else's infrastructure — which is to say, the ones who were being careful. That is the recurring cruelty of this class of bug. Prudence puts you in the blast radius.


What the exploit actually does



We pulled the repository and read it. We are describing this at the level already public in the author's own documentation, because you cannot hunt for something you cannot recognize.


The request is an unauthenticated POST to the site root carrying a Next-Action header and a crafted multipart Flight payload. The payload uses prototype pollution — the classic proto-then and constructor-constructor pairing — to walk from deserialization into Node's child process module and reach a synchronous command execution. The output of the command comes back out through the error digest field in the React Server Components response, which is a genuinely elegant piece of work: the exfiltration channel is the framework's own error reporting.


One request. No authentication. No user interaction. No second stage required.


What to do tonight



If you run self-hosted Cal.com, check your version. Anything at or below 5.9.8 is vulnerable. The fix is 5.9.9. There is no configuration change that mitigates this — the vulnerable path is reachable on the default deployment, which is the whole reason the score is a 10.


If you cannot update tonight, put the instance behind something that will not pass an unauthenticated POST to the site root with a Next-Action header. That is a crude control and it will break legitimate server actions, but a broken scheduling widget is a better Tuesday than a compromised calendar.


Then go hunting in whatever logs you have. You are looking for POST requests to the root path carrying a Next-Action header from sources that should not be issuing them, multipart request bodies containing proto or constructor tokens, and unusual error digest values in responses. If your reverse proxy does not log request headers, this is the week to find out.


The wider check, which is the actual point



Cal.com is the instance we caught. It is not the population.


Go look at every self-hosted product in your estate that ships a bundled JavaScript framework — the internal wikis, the status pages, the admin dashboards, the analytics front ends, the low-code tools somebody stood up in a sprint and nobody has thought about since. For each one, the question is not "have I patched React." It is "what React did the vendor build this with, and have they shipped a version since December."


Most of them will not have a CVE filed against them yet. CVE-2025-71389 exists because somebody did the work to file it against Cal.com specifically. There is no reason to believe Cal.com was the only product bundling a vulnerable Next.js 15.x, and every reason to believe it was among the first to be named rather than the only one to be affected.


That gap — between the products that are vulnerable and the products that have been named — is where the next six months of this bug live.


The receipt



Our exploit harvester logged this repository at 18:01 UTC on July 28, 2026, and it is in our indicator feed under source exploit-harvester. It logged the earlier React2Shell weaponizations on July 17 and July 24, and roughly two dozen before those going back to December. The harvester does not know what is important. It watches a specific behavior — public exploit code appearing for a known CVE — and it files what it sees, every six hours, whether or not anyone is reading.


We publish the interesting ones. This one was interesting because the bug moved house.


As always, we cap our confidence at 95%. The exploit is public and we read it; the affected-version data is from the CVE record; the population estimate for downstream bundled-framework exposure is an argument, not a measurement, and we have flagged it as one.





Her name was Renee Nicole Good.


His name was Alex Jeffery Pretti.

 
 
 
bottom of page