Feb 26, 2026·6 min read·29 visits
Vikunja < 2.0.0 contains a 'Zip Slip' vulnerability in its CLI restore command. Malicious backups can overwrite system files. Worse, the code wipes the database *before* validating the backup; if the backup is malformed, the app panics, crashes, and leaves you with an empty database.
A critical vulnerability in Vikunja's restore functionality allows for arbitrary file overwrites via Path Traversal (Zip Slip) and permanent data loss due to improper error handling. The application destructively wipes the existing database before validating the integrity of the backup archive, leading to potential Denial of Service (DoS) or Remote Code Execution (RCE).
Vikunja is the open-source darling for self-hosters who want to organize their lives without handing their data over to Big Tech. It is written in Go, sleek, and generally robust. However, every application has that one closet where they shove the messy code. In Vikunja's case, that closet was the CLI restore command.
The restore function is intended to take a backup ZIP file and reinstate your tasks, lists, and users. Ideally, a restore operation should be atomic: check the backup, prepare the transaction, and only then commit changes. Vikunja took a different approach. It was the digital equivalent of a mover who burns down your current house before checking if the furniture truck actually arrived.
This vulnerability (CVE-2026-27819) isn't just about reading files you shouldn't. It is about a catastrophic failure in logic that combines a classic directory traversal attack (Zip Slip) with a 'Kamikaze' execution flow that can leave a production instance permanently broken and empty.
The vulnerability stems from two distinct but compounding failures in vikunja/pkg/modules/dump/restore.go. The first is blind trust in user input. When processing a ZIP archive, the application iterates through files and checks if they start with database/. If they do, it strips that prefix and uses the rest of the string as the filename for the restore operation.
This is the textbook definition of Zip Slip (CWE-22). The application failed to sanitize the path for directory traversal characters (../). If I hand the application a ZIP file containing an entry named database/../../../../etc/shadow, Vikunja obliges and attempts to write to that location relative to its execution root.
The second flaw is Uncaught Exception (CWE-248) combined with Improper Sequencing. The code attempts to access a slice index (len(ms)-2) without verifying the slice actually has enough elements. If you feed it a malformed or empty backup, the Go runtime panics. Because this panic happens after the database wipe command, the application crashes, the process dies, and your data is gone forever.
Let's look at the vulnerable logic. It's a masterclass in 'check the wrong thing, then do the dangerous thing'.
// Vulnerable Code Pattern
func Restore(path string) error {
// 1. OPEN THE ZIP
z, _ := zip.OpenReader(path)
// 2. WIPE THE DATABASE (The Point of No Return)
// This runs before we even know if the zip is valid!
db.WipeEverything()
// 3. ITERATE FILES
for _, f := range z.File {
// The Zip Slip:
// Checks prefix, but doesn't sanitize directory traversal
if strings.HasPrefix(f.Name, "database/") {
fileName := strings.TrimPrefix(f.Name, "database/")
// ... writes to fileName ...
}
}
// 4. THE PANIC
// Accessing slice without bounds check
// If ms is empty, this crashes the app.
version := ms[len(ms)-2]
}The fix introduced in commit 1b3d8dc59cb5f2b759ab0ad2bc9915b993e3cb73 completely restructures this flow. It introduces a strict filename parser:
func parseDbFileName(fname string) (string, bool) {
// Explicitly forbids slashes and requires .json
if strings.Contains(fname, "/") || strings.Contains(fname, "\\") {
return "", false
}
if !strings.HasSuffix(fname, ".json") {
return "", false
}
return strings.TrimSuffix(fname, ".json"), true
}Critically, the patched version now reads the entire backup into memory (with a 500MB cap) and validates every single JSON file before calling db.WipeEverything(). This prevents the DoS scenario effectively.
Exploiting this requires creating a malicious ZIP file. Standard archiving tools usually prevent you from adding relative paths like ../, so we'd python-script this.
We want to overwrite config.yml to change the database host to a malicious server we control, or perhaps overwrite a binary if permissions allow.
config.yml (locally).database/../../config.yml.vikunja restore malicious.zip.database/ prefix, strips it, and writes the file to ../../config.yml relative to the CWD.This is for the chaotic evil attacker. We simply want to destroy the instance.
vikunja restore broken.zip.db.WipeEverything().index out of range panic.While this vulnerability requires CLI access (or an admin trigger), the impact is Critical.
Confidentiality & Integrity: The Zip Slip component allows an attacker to overwrite sensitive configuration files or inject code (e.g., via cron jobs or webhooks if they are stored as files). This is a direct path to Remote Code Execution (RCE) depending on the file system permissions of the user running Vikunja.
Availability: The destructive nature of the restore logic is arguably worse for the average user. A corrupted backup file shouldn't result in a total loss of production data. In a worst-case scenario, an automated backup/restore testing pipeline could inadvertently wipe a staging or production database simply because the backup artifact was incomplete.
The remediation strategy adopted by the Vikunja team is robust. By separating validation from execution, they moved from a 'Fail-Unsafe' to a 'Fail-Safe' architecture.
For Developers:
filepath.Base() or explicit allow-lists.len() before you leap.For Users:
Upgrade to v2.0.0 immediately. If you cannot upgrade, treat the restore command as a loaded gun—verify your ZIPs manually and take a snapshot of your VM/DB before running it.
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Vikunja Vikunja | < 2.0.0 | 2.0.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 (Path Traversal) & CWE-248 (Uncaught Exception) |
| Attack Vector | Local (CLI) / Network (if upload allowed) |
| CVSS | 9.1 (Critical) |
| Impact | Arbitrary File Overwrite / Permanent Data Loss |
| Fixed Version | v2.0.0 |
| Exploit Status | Proof of Concept (Internal) |
Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
An algorithmic complexity vulnerability in the python-sqlparse library allows remote, unauthenticated attackers to cause a Denial of Service (DoS) via resource exhaustion. By transmitting a carefully constructed SQL statement containing deeply nested structures, an attacker can trigger quadratic CPU consumption within the parsing engine. This behavior bypasses the built-in depth limits because the performance degradation occurs during the initial recursive tree construction, causing the application process to hang.
A critical vulnerability exists in the atomic-agents-stack package up to version 1.0.0. The HTTP Model Context Protocol (MCP) server-registry backend factory retrieves catalog metadata over cleartext HTTP by default. Because these catalogs define execution parameters ('command' and 'args') for local stdio subprocesses, a network-positioned attacker can intercept the cleartext traffic and inject arbitrary commands. This results in arbitrary remote code execution on the agent host system without requiring user interaction.
A high-severity vulnerability in the atomic-agents-stack framework allows complete bypass of cost-cap guardrails during parallel model execution when utilizing unlisted, local, or self-hosted models.
A security vulnerability in the Go library software.sslmate.com/src/go-pkcs12 allows remote attackers to bypass password-based integrity verification. By crafting a PKCS#12 file with an excessively short KeyLength parameter in the PBMAC1 configuration, the derived MAC key space collapses, allowing an attacker to forge arbitrary certificate structures and private keys that are incorrectly verified as valid.
A workspace boundary bypass vulnerability exists in the Chrome DevTools for Agents (chrome-devtools-mcp) Model Context Protocol (MCP) server from version 0.24.0 up to 1.1.0. The vulnerability allows an agent or malicious workspace containing symbolic links to read or modify arbitrary files outside the configured project workspace root directory. This occurs because the path validation function resolves paths lexically rather than physically.
A high-severity security vulnerability exists in 9Router, an AI router and token saver dashboard. When dashboard authentication features are disabled or left in default configurations, the application exposes administrative testing routines directly to the public internet. Unauthenticated network adversaries can exploit the OIDC configuration validation endpoint to initiate arbitrary HTTP requests, routing unauthorized traffic to local loops, adjacent container ports, and cloud resource metadata interfaces.