Vikunja is an open-source self-hosted task management platform. Starting in version 0.21.0 and prior to version 2.2.0, the Vikunja Desktop Electron wrapper enables nodeIntegration in the main BrowserWindow and does not restrict same-window navigations. An attacker who can place a link in user-generated content (task descriptions, comments, project descriptions) can cause the BrowserWindow to navigate to an attacker-controlled origin, where JavaScript executes with full Node.js access, resulting in arbitrary code execution on the victim's machine. Version 2.2.0 patches the issue.
<h2>Root cause</h2>
Two misconfigurations combine to create this vulnerability:
-
nodeIntegration: true is set in BrowserWindow web preferences (desktop/main.js:14-16), giving any page loaded in the renderer full access to Node.js APIs (require, child_process, fs, etc.).
-
No will-navigate or will-redirect handler is registered on the webContents. The existing setWindowOpenHandler (desktop/main.js:19-23) only intercepts window.open() calls (new-window requests). It does not intercept same-window navigations triggered by:
<a href="https://..."> links (without target="_blank")
window.location assignments
- HTTP redirects
<meta http-equiv="refresh"> tags
<h2>Attack scenario</h2>
- The attacker is a normal user on the same Vikunja instance (e.g., a member of a shared project).
- The attacker creates or edits a project description or task description containing a standard HTML link, e.g.:
<a href="https://evil.example/exploit">Click here for the updated design spec</a>
- The Vikunja frontend renders this link. DOMPurify sanitization correctly allows it -- it is a legitimate anchor tag, not a script injection. Render path example:
frontend/src/views/project/ProjectInfo.vue uses v-html with DOMPurify-sanitized output.
- The victim uses Vikunja Desktop and clicks the link.
- Because no
will-navigate handler exists, the BrowserWindow navigates to https://evil.example/exploit in the same renderer process.
- The attacker's page now executes in a context with
nodeIntegration: true and runs: require('child_process').exec('id > /tmp/pwned');
- Arbitrary commands execute as the victim's OS user.
<h2>Impact</h2>
Full remote code execution on the victim's desktop. The attacker can read/write arbitrary files, execute arbitrary commands, install malware or backdoors, and exfiltrate credentials and sensitive data. No XSS vulnerability is required -- a normal, sanitizer-approved hyperlink is sufficient.
<h2>Proof of concept</h2>
- Set up a Vikunja instance with two users sharing a project.
- As the attacker user, edit a project description to include:
<a href="https://attacker.example/poc.html">Meeting notes</a>
- Host poc.html with:
<script>require('child_process').exec('calc.exe')</script>
- As the victim, open the project in Vikunja Desktop and click the link.
- calc.exe (or any other command) executes on the victim's machine.
<h2>Credits</h2>
This vulnerability was found using GitHub Security Lab Taskflows.