CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-27148

Storybook Ending: Dev Server RCE via WebSocket Hijacking

Alon Barad
Alon Barad
Software Engineer

Feb 26, 2026·6 min read·42 visits

Executive Summary (TL;DR)

Storybook's dev server left its WebSocket door wide open. If a developer visits a malicious site while Storybook is running, the site can hijack the connection (CSWSH), write malicious files to the local disk, and trigger RCE. Patch immediately to versions 7.6.23+, 8.6.17+, 9.1.19+, or 10.2.10+.

A critical flaw in the Storybook development server allows attackers to hijack the WebSocket connection from a malicious website via Cross-Site WebSocket Hijacking (CSWSH). Because the server failed to validate the `Origin` header or require authentication, a drive-by attack can silently connect to a developer's local instance, overwrite files, and achieve Remote Code Execution (RCE) on the developer's machine.

The Hook: Your Localhost is Not a Castle

We often treat localhost like a sanctuary. It's the safe space where code is born, broken, and fixed before it faces the cruel judgment of production. But here's the dirty secret of modern web development: your local dev server is often more exposed than your production environment. Why? Because developers prize convenience over security. "It's just running locally, who can touch it?" turns out to be a very expensive assumption.

Enter Storybook, the industry standard for UI component development. Under the hood, the Storybook Dev Server runs a WebSocket channel (/storybook-server-channel) to synchronize the Manager (the UI you click) with the Preview (the iframe rendering your components). This channel is the nervous system of the application. It handles hot module reloading, event emission, and—crucially—file system operations like creating and saving stories.

Now, imagine if that nervous system had no immune system. No antibodies. No skin. Just raw nerves exposed to the open internet. That is exactly what CVE-2026-27148 is. It’s a mechanism that allows an attacker to reach into your localhost from their website and tell your Storybook server to start rewriting your project files.

The Flaw: The 'Origin' Myth

The vulnerability here is a classic Cross-Site WebSocket Hijacking (CSWSH) issue. To understand it, you have to understand how browsers handle WebSockets. When a browser initiates a WebSocket handshake, it sends an HTTP GET request with an Upgrade: websocket header. Crucially, it also sends an Origin header, telling the server which site is asking for the connection. For standard HTTP requests (fetch/XHR), the browser enforces the Same-Origin Policy (SOP). But for WebSockets? The browser relies on the server to check that Origin header and say "No."

Storybook didn't say no. In fact, it didn't even look. The ServerChannelTransport implementation blindly accepted any incoming connection to /storybook-server-channel. It didn't care if the request came from http://localhost:6006 (the legit UI) or https://evil-hacker-blog.com.

This creates a terrifyingly simple attack vector. I don't need to be on your network. I don't need to phish your credentials. I just need you to visit my website while you have Storybook running in a background tab. My JavaScript runs in your browser, sees localhost (which your browser can reach), and opens a socket. Your browser attaches cookies (if there were any, though Storybook didn't use them here) and the connection is established. Game on.

The Code: The Smoking Gun

Let's look at the anatomy of the failure. The vulnerability resided in how the server handled the upgrade event. In the vulnerable versions, the code looked effectively like this:

// VULNERABLE CODE (Conceptual)
server.on('upgrade', (request, socket, head) => {
  if (request.url === '/storybook-server-channel') {
    // "Come on in, the water's fine!"
    wss.handleUpgrade(request, socket, head, (ws) => {
      wss.emit('connection', ws, request);
    });
  }
});

See the lack of logic? If the URL matches, the connection is granted. There is no check for the Origin header, and no token verification.

Now, let's look at the fix introduced in commit 54689a8add18ea75d628c540f4bc677592a1e685. The maintainers introduced a token-based authentication system. When the dev server starts, it generates a UUID. The frontend must present this UUID to connect.

// PATCHED CODE (Simplified)
const SERVER_CHANNEL_TOKEN = randomUUID(); // Generated at startup
 
server.on('upgrade', (request, socket, head) => {
  const url = new URL(request.url, 'http://localhost');
  if (url.pathname === '/storybook-server-channel') {
    // The check that saves the day
    const requestToken = url.searchParams.get('token');
    
    if (!isValidToken(requestToken, SERVER_CHANNEL_TOKEN)) {
      // "You shall not pass!"
      socket.write('HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n');
      socket.destroy();
      return;
    }
    
    wss.handleUpgrade(request, socket, head, (ws) => {
      wss.emit('connection', ws, request);
    });
  }
});

This simple check neutralizes the attack. An external website cannot guess the random UUID generated when you started your server.

The Exploit: From Hijack to RCE

So I have a socket. What can I do with it? This is where it gets dark. The Storybook channel isn't just for chatting; it's an RPC mechanism. It supports event handlers for CREATE_STORY and SAVE_STORY. These handlers take a file path and content, and write it to the disk.

Here is the attack chain:

  1. Recon: The malicious script attempts to connect to default ports (6006, 6007, etc.) to find a live Storybook instance.
  2. Connection: The socket opens. The attacker listens for the initial handshake.
  3. Payload Delivery: The attacker sends a JSON payload masquerading as a SAVE_STORY event.
{
  "type": "SAVE_STORY",
  "args": [
    {
      "importPath": "./stories/Button.stories.ts",
      "content": "import { exec } from 'child_process'; exec('calc.exe'); // ... rest of story code"
    }
  ]
}
  1. Execution: Storybook receives the event. It assumes the request is coming from its own UI (because why would it check?). It writes the malicious content to Button.stories.ts.
  2. Trigger: The Dev Server usually runs a file watcher (like Webpack or Vite). It sees the file change, recompiles, and hot-reloads the module. The moment that file is processed or rendered in the Preview iframe, the attacker's JavaScript executes within the Node.js context of the build process or the browser context of the preview.

This is effectively RCE. If I can write to your filesystem and force a recompile, I own your shell.

The Fix: Closing the Loop

The mitigation is straightforward: Update. The Storybook team has released patches for all supported major versions.

  • Storybook 7: Upgrade to 7.6.23
  • Storybook 8: Upgrade to 8.6.17
  • Storybook 9: Upgrade to 9.1.19
  • Storybook 10: Upgrade to 10.2.10

If you are stuck on an older version and cannot upgrade immediately (we've all been there), you have limited options. You could try to run Storybook behind a reverse proxy that strips the Origin header or enforces authentication, but honestly, that's more work than just upgrading package.json.

> [!WARNING] > Do not use ngrok or similar tunneling services to share your local Storybook unless you are absolutely certain you have patched. Exposing a vulnerable dev server to the public internet turns a "Drive-By" attack into a "Public Invitation" for anyone scanning for open WebSockets.

Official Patches

StorybookGitHub Security Advisory and Patch Notes

Fix Analysis (1)

Technical Appendix

CVSS Score
8.9/ 10
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:A/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
EPSS Probability
0.36%
Top 42% most exploited

Affected Systems

Storybook Dev Server (< 7.6.23)Storybook Dev Server (8.1.0 - < 8.6.17)Storybook Dev Server (9.0.0 - < 9.1.19)Storybook Dev Server (10.0.0 - < 10.2.10)

Affected Versions Detail

Product
Affected Versions
Fixed Version
Storybook
Storybook.js
< 7.6.237.6.23
Storybook
Storybook.js
8.1.0 - < 8.6.178.6.17
Storybook
Storybook.js
9.0.0 - < 9.1.199.1.19
Storybook
Storybook.js
10.0.0 - < 10.2.1010.2.10
AttributeDetail
Attack VectorNetwork (Drive-By)
CVSS v4.08.9 (High)
CWECWE-74 / CWE-79
ImpactRCE / Persistent XSS
Exploit StatusPoC Available
ComponentServerChannelTransport

MITRE ATT&CK Mapping

T1189Drive-by Compromise
Initial Access
T1566Phishing
Initial Access
T1204User Execution
Execution
CWE-74
Injection

Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')

Known Exploits & Detection

GitHub Security AdvisoryOfficial advisory detailing the CSWSH vector.

Vulnerability Timeline

Patches merged into Storybook repository
2026-02-18
Public disclosure and GHSA published
2026-02-25
CVE-2026-27148 assigned
2026-02-25

References & Sources

  • [1]GHSA-mjf5-7g4m-gx5w
  • [2]MITRE ATT&CK: Drive-by Compromise

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•23 minutes ago•CVE-2026-45491
6.2

CVE-2026-45491: Directory Traversal via Improper Link Resolution in .NET System.Formats.Tar

A directory traversal vulnerability exists in the Microsoft .NET System.Formats.Tar library during archive extraction. When extracting a TAR archive using the TarFile.ExtractToDirectory API, the extraction engine improperly resolves symbolic links prior to file creation, allowing local unauthorized attackers to write or overwrite arbitrary files outside the target directory. This can lead to local tampering, privilege escalation, or arbitrary code execution.

Amit Schendel
Amit Schendel
1 views•6 min read
•about 1 hour ago•GHSA-GJ48-438W-JH9V
6.1

GHSA-GJ48-438W-JH9V: Client-Side HTML Sanitization Bypass in Bleach

A client-side HTML sanitization bypass vulnerability exists in the Bleach library where the formaction attribute is not recognized as a URI. This allows attackers to inject javascript: URIs when formaction is on the allowed list, resulting in Cross-Site Scripting (XSS).

Alon Barad
Alon Barad
3 views•6 min read
•about 1 hour ago•CVE-2026-53722
5.4

CVE-2026-53722: Reflected DOM-based Cross-Site Scripting (XSS) in Nuxt <NuxtLink>

A reflected DOM-based Cross-Site Scripting (XSS) vulnerability was identified in Nuxt's core <NuxtLink> component. Prior to the patched versions, the component failed to validate or sanitize the target URI schemes before directly rendering them into the 'href' attribute of native HTML anchor elements. An attacker who controls the input bound to the 'to' or 'href' properties can inject executable URI schemes, such as 'javascript:' or 'data:', leading to arbitrary script execution in the context of the user's browser session.

Amit Schendel
Amit Schendel
2 views•6 min read
•about 14 hours ago•GHSA-PW6J-QG29-8W7F
5.9

GHSA-pw6j-qg29-8w7f: State Persistence and Sensitive Credential Leakage in Tornado CurlAsyncHTTPClient

A state persistence vulnerability exists in Tornado's CurlAsyncHTTPClient component where pooled pycurl.Curl handles are reused across asynchronous requests without a complete state reset. Consequently, sensitive per-request configurations, such as client TLS certificates or proxy basic authentication credentials, persist on the shared handle. This behavior leads to subsequent requests leaking these credentials to unauthorized remote servers.

Amit Schendel
Amit Schendel
7 views•7 min read
•about 15 hours ago•CVE-2026-48748
7.5

CVE-2026-48748: Netty HTTP/3 QPACK Blocked Streams Memory Exhaustion

CVE-2026-48748 is a denial-of-service vulnerability in Netty's HTTP/3 codec (netty-codec-http3) occurring when QPACK dynamic tables are enabled but the blocked streams limit is not explicitly configured. A bug in limit checking and a memory leak in stream tracking allow unauthenticated remote attackers to exhaust the JVM heap memory and crash the server.

Amit Schendel
Amit Schendel
8 views•6 min read
•about 15 hours ago•CVE-2026-50009
4.8

CVE-2026-50009: Stateless Reset Token Exposure in Netty QUIC

CVE-2026-50009 is a cryptographic design vulnerability in the Netty network application framework. Prior to version 4.2.15.Final, the framework's QUIC protocol implementation fails to cryptographically segregate the generated Connection IDs and the associated Stateless Reset Tokens. An on-path network attacker who sniffs traffic during a Connection ID rotation can extract secret token material from cleartext headers, enabling them to inject spoofed reset packets and terminate active connections.

Alon Barad
Alon Barad
7 views•6 min read