# PhoenixStorybook: Unauthenticated remote code execution via HEEx template injection in phoenix_storybook playground

Unauthenticated WebSocket psb-assign values are interpolated unescaped into a HEEx template compiled and evaluated server-side, giving pre-auth RCE in phoenix_storybook playground.

- Payout: Research
- Severity: critical
- Program: PhoenixStorybook
- Found by (vendor credit): Nick Mykhailyshyn, Cenk Kücük, Jonatan Männchen / EEF
- Found by: ndelphit
- Disclosed: 2026-06-09
- CVE: CVE-2026-8467
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-55hg-8qxv-qj4p
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-55hg-8qxv-qj4p
- Tags: area:web domain:web impact:internal-access impact:rce kind:research platform:ghsa sev:critical tech:websocket technique:source-review vuln:rce vuln:ssti

**Target:** Phoenix LiveView WebSocket playground (psb-assign event) in phoenix_storybook
**Root cause:** ComponentRenderer.attributes_markup/1 interpolates binary attribute values into a HEEx template string without escaping quotes or braces, and the result is compiled with EEx.compile_string/2 and evaluated via Code.eval_quoted_with_env/3 with full Kernel access and no sandbox.
**Steps:**
1. Identify a story URL with a Playground tab, e.g. `/storybook/core_components/button`.
2. Connect to the Phoenix LiveView WebSocket without authentication.
3. Join the story's LiveView channel and send a `psb-assign` event with an attribute value that escapes the HEEx attribute context, e.g. `foo" injected={EXPR} bar="`.
4. Server compiles/evaluates the injected expression (e.g. `System.cmd/2`) and returns output in the rendered response.
**Exploitation:** Unauthenticated attacker connects to the LiveView socket, joins the story channel, and sends psb-assign with a crafted attribute value breaking out of the attribute string and injecting an Elixir expression; the server evaluates it with full Kernel access, executing arbitrary OS commands as the server process and returning output in the rendered response.
**Insight:** LiveView playgrounds that rebuild HEEx markup from user-supplied attribute values are template-injection sinks: any binary interpolated into `name="#{val}"` without escaping `"` and `{` lets you break into an inline HEEx expression, and EEx.compile_string + Code.eval_quoted_with_env gives full Kernel, so treat every psb-* event as attacker-controlled code input.

## Proof of concept (copied exactly from the original report)

**Unescaped attribute interpolation in HEEx template**

```
{name, val} when is_binary(val) ->
  ~s|#{name}="#{val}"|
No escaping of `"` or `{` is performed. A value such as `foo" injected={EXPR} bar="` breaks out
```

