Описание
Vikunja has an incomplete fix for CVE-2026-35595: Write-only user can detach shared project from parent hierarchy via parent_project_id=0
Summary
The fix for CVE-2026-35595 (project re-parenting privilege escalation) only gates reparent operations when parent_project_id > 0. A user with Write (but not Admin) permission on a shared child project can detach it from its parent by sending parent_project_id: 0, bypassing the Admin requirement. This severs the recursive CTE permission inheritance chain, potentially disrupting the project hierarchy and affecting inherited access for other collaborators.
Affected component
- Package: go-vikunja/vikunja
- Affected versions: v2.3.0 and later (including latest unstable v2.3.0-246-9852aff4). The fix for CVE-2026-35595 was introduced in v2.3.0 but left the detach-to-root case unpatched. Fixed in 2.4.0.
- Tested on: Vikunja v2.3.0 (Docker image vikunja/vikunja:2.3.0) AND latest unstable (vikunja/vikunja:unstable, v2.3.0-246-9852aff4 built 2026-04-27)
Technical detail
Vulnerable code
File: pkg/models/project.go (lines 1009-1041)
File: pkg/models/project_permissions.go (line 145)
File: pkg/models/project.go (line 1065)
Why it's exploitable
- The generic web handler (
pkg/web/handler/update.go:37) creates a fresh emptyProject{}struct --ParentProjectIDdefaults to Go's zero value (0). - When JSON body contains
"parent_project_id": 0, the struct hasParentProjectID == 0. CanUpdateat line 145:ParentProjectID != 0is false -- reparent check skipped -- falls through toCanWritewhich succeeds (attacker has Write).UpdateProjectat line 1018:ParentProjectID > 0is false -- Admin gate skipped entirely.- xorm writes
parent_project_id = 0because"parent_project_id"is always incolsToUpdatewithCols(). - The project is detached from its parent hierarchy.
Precondition checklist
- Attacker has authenticated account
- Attacker has Write permission on a child project (via direct share or team membership)
- The target project has a non-zero parent_project_id (it's a child of another project)
- Default Vikunja configuration (no special setup needed)
Reproduction
Prerequisites: Two users (victim = project owner, attacker = Write-only collaborator), a parent project, and a child project shared with the attacker at Write permission.
- Authenticate as attacker:
- Verify attacker does NOT have Admin (delete should return 403):
- Exploit -- detach project from parent:
- Verify detachment:
Evidence (3 independent runs)
| Run | parent_project_id BEFORE | DELETE attempt (proves no Admin) | parent_project_id AFTER | Result |
|---|---|---|---|---|
| 1 | 3 (child of project 3) | HTTP 403 Forbidden | null (detached to root) | CONFIRMED |
| 2 | 3 (child of project 3) | HTTP 403 Forbidden | null (detached to root) | CONFIRMED |
| 3 | 3 (child of project 3) | HTTP 403 Forbidden | null (detached to root) | CONFIRMED |
Note: "null (detached to root)" means parent_project_id was set to 0 in the database, making the project a root-level project with no parent.
Impact
- Unauthorized hierarchy modification: A user with only Write permission can detach a child project from its parent, which should require Admin permission (as established by the CVE-2026-35595 fix for non-zero reparents).
- Permission inheritance disruption: The recursive CTE permission model traverses
parent_project_idupward. Detaching a project severs this chain, potentially causing other collaborators who inherited access through the parent to lose their permissions on the detached project. - Organizational disruption: The project moves from a structured hierarchy to a root-level project, breaking the owner's intended organizational structure.
Suggested fix
Use a pointer field *int64 for ParentProjectID to distinguish between "field omitted" (nil) and "explicitly set to 0" (detach). The fix commit itself acknowledges this at project.go:1017: "a proper fix needs a pointer field."
Alternatively, add a dedicated detach boolean field or a separate API endpoint for detaching projects, with its own Admin permission check.
Пакеты
code.vikunja.io/api
>= 2.3.0, < 2.4.0
2.4.0
Связанные уязвимости
Vikunja is an open-source self-hosted task management platform. From 2.3.0 until 2.4.0, a user with Write but not Admin permission on a shared child project can detach it from its parent hierarchy by submitting parent_project_id equal to 0 to POST /api/v1/projects/{project}. The Project.CanUpdate authorization check in pkg/models/project_permissions.go and UpdateProject logic in pkg/models/project.go only gate nonzero parent values, while UpdateProject always persists parent_project_id, so the explicit zero value bypasses the Admin requirement introduced for CVE-2026-35595. Detachment severs the recursive permission-inheritance chain and can disrupt the owner’s hierarchy and inherited collaborator access. This issue is fixed in version 2.4.0.