{"id":"CVE-2026-96627","kind":"vulnerability","aliases":["CVE-2026-96627","GHSA-pw5q-rjx4-7f7p","GO-2026-6807"],"related":[],"title":"Arcane: Delegated users:update permission allows admin account takeover via password reset (no privilege-hierarchy check)","description":"## Summary\nA 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.\n\n## Root Cause\n`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.\n\n## Impact\nComplete administrator account takeover (C:H/I:H/A:H) by any holder of the delegated `users:update` permission.\n\n## Proof of Concept\n1. Admin delegates a custom \"User Manager\" role with `users:update`.\n2. `PUT /users/{adminId}` with `{\"password\":\"attacker123\"}`.\n3. `POST /auth/login {\"username\":\"arcane\",\"password\":\"attacker123\"}` → admin session.\n\n## Attack Chain\n1. 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).\n2. 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).\n3. Action: `POST /auth/login` as admin → admin session.\n\n## Bypass Evidence\n- `users.go:317-323` sets `PasswordHash` for any target; `user_service.go:174-194` has no actor/target privilege comparison.\n- Only `IsGlobalAdmin` hits are `DeleteUser`'s last-admin guard and DTO flags (grep).\n- `users:update` is org-level, held by no built-in non-admin role (`permissions.go:24`).\n\n## Affected Versions\n`<= 2.5.0`.\n\nReported by **zx (Jace)** — GitHub: @manus-use","summary":"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.","severity":"high","cvss":8.800000190734863,"epss":0.002899999963119626,"exploited":false,"exploited_at":null,"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"],"how_to_check":"Check the Arcane backend module version in go.mod: github.com/getarcaneapp/arcane/backend/v2 below 2.6.0 is affected.\nReview custom roles for any role granted users:update, since built-in non-admin roles do not hold it.\nAudit 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.\nUntil then, remove users:update from custom roles and rotate the admin password.","status":"open","published_at":"2026-10-16T21:15:03+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","triaged_at":"2026-10-23T19:51:10+00:00","updated_at":"2026-10-23T19:51:10+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-10-23T19:51:10Z","model":"claude-haiku-5-5","output":{"fix":"Upgrade github.com/getarcaneapp/arcane/backend/v2 to 2.6.0 or later.\nUntil then, remove users:update from custom roles and rotate the admin password.","summary":"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.","category":"package","who_cares":["go","arcane","self-hosted","docker"],"confidence":0.8,"analyst_note":"","how_to_check":"Check the Arcane backend module version in go.mod: github.com/getarcaneapp/arcane/backend/v2 below 2.6.0 is affected.\nReview custom roles for any role granted users:update, since built-in non-admin roles do not hold it.\nAudit recent PUT /users/{id} requests in logs for password changes on admin accounts.","needs_analyst":false,"not_affected_if":["default-config"],"severity_if_unknown":"high"},"cost_usd":0.000483,"input_tokens":2992,"output_tokens":368,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-194':357C '-2026':19A,27A,30A '-266':40B '-269':42B '-323':176C,335C,350C '-33':231C '-6807':28A '-96627':20A,31A '/auth/login':280C,342C '/getarcaneapp/arcane/backend':37A '/getarcaneapp/arcane/backend/v2':34A '/users':273C,318C '1':260C,289C '2':271C,315C '2.5.0':396C '27':230C '3':278C,339C '317':175C '7f7p':25A 'account':8A,120C,242C 'action':290C,316C,340C 'actor':201C 'actor-vs-target':200C 'actor/target':360C 'admin':7A,90B,119C,162C,215C,238C,261C,285C,291C,314C,344C,345C,371C,391C 'adminid':274C,319C 'administr':65B,112C,241C 'affect':394C 'allow':6A 'arcan':1A,38B,49B,97B,282C 'assign':146C,224C 'attack':79B,287C 'attacker123':277C,284C,321C 'backend/api/handlers/users.go':174C 'backend/pkg/authz/permissions.go':229C 'bodi':131C,188C 'built':387C 'built-in':386C 'bypass':303C,327C,347C 'c':244C 'caller':74B,140C 'caller-vs-target':139C 'caus':171C 'chain':288C 'check':17A,71B,216C,333C 'comparison':137C,205C,362C 'complet':240C 'concept':259C 'control':94B 'creation':300C 'crud':149C,226C 'custom':264C,293C 'cve':18A,29A 'cwe':39B,41B 'defeat':167C 'deleg':2A,58B,104C,253C,262C,292C,305C 'deleteus':218C,367C 'deliber':151C 'docker':43B 'dto':374C 'evid':348C 'find':166C 'flag':375C 'full':93B 'gate':152C 'ghsa':22A 'ghsa-pw5q-rjx4-7f7p':21A 'github':401C 'github.com':33A,36A 'github.com/getarcaneapp/arcane/backend':35A 'github.com/getarcaneapp/arcane/backend/v2':32A 'global':64B,111C 'go':26A,44B 'grep':376C 'guard':209C,298C,322C,372C 'h':247C 'h/a':246C 'h/i':245C 'handler':173C 'hash':68B 'held':383C 'hierarchi':16A,332C 'hit':365C 'hold':56B,81B,102C 'holder':250C,306C,328C 'host':48B 'id':181C 'impact':239C 'insid':217C 'instanc':98B 'intend':158C,236C,304C 'isglobaladmin':208C,364C 'jace':400C 'last':214C,370C 'last-admin':213C,369C 'let':53B 'level':382C 'load':177C 'log':86B 'manag':164C,266C,310C 'manus':403C 'manus-us':402C 'meant':308C 'non':390C 'non-admin':389C 'org':381C 'org-level':380C 'outrank':75B 'overwrit':62B 'packag':45B 'password':11A,67B,114C,276C,283C,320C 'passwordhash':127C,352C 'path':52B,222C 'permiss':5A,61B,83B,107C,256C 'permissions.go:24':393C 'post':279C,341C 'privileg':15A,143C,204C,361C 'privilege-hierarchi':14A 'profil':163C,311C 'proof':257C 'prove':232C 'put':272C,317C 'pw5q':23A 'report':397C 'request':130C,187C 'requireglobaladmin':154C,228C,302C 'requirepermiss':323C 'reset':12A,109C 'rjx4':24A 'role':145C,148C,223C,225C,267C,294C,299C,392C 'root':170C 'save':338C 'seiz':313C 'self':47B 'self-host':46B 'separ':169C 'session':286C,346C 'set':122C,183C,351C 'simpli':195C 'sit':160C 'summari':99C 'take':92B,116C 'takeov':9A,243C 'target':77B,124C,134C,142C,179C,203C,355C 'tx.save':196C 'uncondit':337C 'updat':4A,60B,106C,156C,221C,234C,255C,270C,297C,325C,378C 'updateus':51B,121C,172C 'use':404C 'user':3A,55B,59B,101C,105C,125C,155C,191C,233C,254C,265C,269C,296C,324C,377C 'user_service.go:174':194C,336C,356C 'user_service.go:345':210C 'usermodel.passwordhash':184C 'usernam':281C 'users.go:154':326C 'users.go:317':334C,349C 'userservice.updateuser':193C 'version':395C 'via':10A 'vs':141C,202C 'zx':399C","nicknames":[],"mitigations":[],"who_affected":null,"tier":null,"verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-16T21:15:03+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-pw5q-rjx4-7f7p","url":"https://github.com/advisories/GHSA-pw5q-rjx4-7f7p","severity":"high","published_at":"2026-10-16T21:15:03+00:00","modified_at":"2026-10-16T21:15:06+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","fetched_at":"2026-10-23T19:11:32+00:00","note":null},{"source":"govuln","external_id":"GO-2026-6807","url":"https://pkg.go.dev/vuln/GO-2026-6807","severity":null,"published_at":"2026-10-19T21:08:35+00:00","modified_at":"2026-10-19T21:08:35+00:00","first_seen_at":"2026-10-23T19:13:08+00:00","fetched_at":"2026-10-23T19:13:08+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-96627","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-96627","severity":null,"published_at":"2026-10-16T22:18:58+00:00","modified_at":"2026-10-19T15:17:09+00:00","first_seen_at":"2026-10-23T21:57:14+00:00","fetched_at":"2026-10-23T22:28:55+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}