---
id: "CVE-2026-96627"
kind: "vulnerability"
aliases: ["CVE-2026-96627", "GHSA-pw5q-rjx4-7f7p", "GO-2026-6807"]
title: "Arcane: Delegated users:update permission allows admin account takeover via password reset (no privilege-hierarchy check)"
severity: "high"
cvss: 8.800000190734863
epss: 0.002899999963119626
exploited: false
status: "open"
category: "package"
products: [{"name": "github.com/getarcaneapp/arcane/backend/v2", "fixed": ["2.6.0"], "affected": "< 2.6.0", "ecosystem": "go"}, {"name": "github.com/getarcaneapp/arcane/backend", "fixed": [], "affected": "all versions", "ecosystem": "go"}]
ecosystems: ["go"]
tags: ["arcane", "cwe-266", "cwe-269", "docker", "go", "package", "self-hosted"]
not_affected_if: ["default-config"]
published_at: "2026-10-16T21:15:03+00:00"
first_seen_at: "2026-10-23T19:11:32+00:00"
updated_at: "2026-10-23T19:51:10+00:00"
sources: [{"source": "ghsa", "url": "https://github.com/advisories/GHSA-pw5q-rjx4-7f7p", "first_seen_at": "2026-10-23T19:11:32+00:00"}, {"source": "govuln", "url": "https://pkg.go.dev/vuln/GO-2026-6807", "first_seen_at": "2026-10-23T19:13:08+00:00"}, {"source": "nvd", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-96627", "first_seen_at": "2026-10-23T21:57:14+00:00"}]
---

# Arcane: Delegated users:update permission allows admin account takeover via password reset (no privilege-hierarchy check)

Arcane's UpdateUser path lets any user holding the delegated users:update permission overwrite the global administrator's password hash, with no check that the caller outranks the target. An attacker who holds that permission can then log in as the admin and take full control of the Arcane instance.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- go:github.com/getarcaneapp/arcane/backend/v2 affected < 2.6.0 fixed 2.6.0
- go:github.com/getarcaneapp/arcane/backend affected all versions fixed ?

## How to check

Check the Arcane backend module version in go.mod: github.com/getarcaneapp/arcane/backend/v2 below 2.6.0 is affected.
Review custom roles for any role granted users:update, since built-in non-admin roles do not hold it.
Audit recent PUT /users/{id} requests in logs for password changes on admin accounts.

## Fix

Upgrade github.com/getarcaneapp/arcane/backend/v2 to 2.6.0 or later.
Until then, remove users:update from custom roles and rotate the admin password.

## Timeline

- 2026-10-23T19:11:32+00:00 ghsa https://github.com/advisories/GHSA-pw5q-rjx4-7f7p
- 2026-10-23T19:13:08+00:00 govuln https://pkg.go.dev/vuln/GO-2026-6807
- 2026-10-23T21:57:14+00:00 nvd https://nvd.nist.gov/vuln/detail/CVE-2026-96627

## From the sources

## Summary
A user holding the delegated `users:update` permission can reset the global administrator's password and take over the admin account. `UpdateUser` sets the target user's `PasswordHash` from the request body for ANY target with no comparison of caller-vs-target privilege. Because role assignment and role CRUD are deliberately gated by `RequireGlobalAdmin`, `users:update` is intended to sit BELOW admin (profile management); this finding defeats that separation.

## Root Cause
`UpdateUser` (handler `backend/api/handlers/users.go:317-323`) loads the target by ID and sets `userModel.PasswordHash` from the request body for any user, then `userService.UpdateUser` (`user_service.go:174`) simply `tx.Save`s — with NO actor-vs-target privilege comparison. The only `IsGlobalAdmin` guard (`user_service.go:345`) is a last-admin check inside `DeleteUser`, not the update path. Role assignment / role CRUD are `RequireGlobalAdmin` (`backend/pkg/authz/permissions.go:27-33`), proving `users:update` is intended below admin.

## Impact
Complete administrator account takeover (C:H/I:H/A:H) by any holder of the delegated `users:update` permission.

## Proof of Concept
1. Admin delegates a custom "User Manager" role with `users:update`.
2. `PUT /users/{adminId}` with `{"password":"attacker123"}`.
3. `POST /auth/login {"username":"arcane","password":"attacker123"}` → admin session.

## Attack Chain
1. Action: admin delegates custom role with `users:update`. Guard: role creation is `RequireGlobalAdmin`. Bypass: intended delegation (holder is meant to manage profiles, not seize admin).
2. Action: `PUT /users/{adminId}` `{"password":"attacker123"}`. Guard: `RequirePermission(users:update)` (`users.go:154`). Bypass: holder has it; no hierarchy check (`users.go:317-323`, `user_service.go:174` unconditional Save).
3. Action: `POST /auth/login` as admin → admin session.

## Bypass Evidence
- `users.go:317-323` sets `PasswordHash` for any target; `user_service.go:174-194` has no actor/target privilege comparison.
- Only `IsGlobalAdmin` hits are `DeleteUser`'s last-admin guard and DTO flags (grep).
- `users:update` is org-level, held by no built-in non-admin role (`permissions.go:24`).

## Affected Versions
`<= 2.5.0`.

Reported by **zx (Jace)** — GitHub: @manus-use

---

_Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai_
