{"id":"GHSA-g95c-wqvr-8c3j","kind":"vulnerability","aliases":["GHSA-g95c-wqvr-8c3j"],"related":[],"title":"better-auth: Admin roles with user:update can change another user's phone number","description":"## Am I affected?\n\nYou are affected if **all** of these are true:\n\n- You use the admin plugin and the phone number plugin from `better-auth`.\n- A role other than the full admin role holds the `user:update` permission.\n\nYou are not affected if only full administrators can update users.\n\n## Summary\n\nThe admin `update-user` endpoint requires a separate permission to change a user's email address, but not to change their phone number. A role allowed to edit user profiles could set another user's phone number and mark it as verified.\n\n## Impact\n\nA user with a limited role that holds `user:update` can put their own phone number on another user's account and then sign in to that account with a phone OTP. This gives them the other user's session, including accounts with more privileges than their own.\n\n## Patches\n\nUpgrade `better-auth` to 1.7.16, or to 1.8.0-beta.5 on the beta line.\n\nChanging `phoneNumber` or `phoneNumberVerified` through `update-user` now needs the new `user:set-phone` permission. The default admin role has it. If you use custom access control, add `set-phone` to your statements and grant it to the roles that should change phone numbers.\n\n## Workarounds\n\nRemove `user:update` from roles that should not change sign-in details, or reject `phoneNumber` and `phoneNumberVerified` in requests to `/admin/update-user` before they reach Better Auth.\n\n## Technical details\n\n`/admin/update-user` checked `user:set-email` before changing the email address, but passed phone fields through with only `user:update`. It now checks `user:set-phone` when either phone field is present.\n\n## Credit\n\nDiscovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-938BEJ5J.","summary":"A role that holds the user:update permission in the better-auth admin plugin can set another user's phone number and mark it verified. The attacker can then sign in to that account with a phone OTP and take over its session, including accounts with more privileges.","severity":"medium","cvss":6.8,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"better-auth","fixed":["1.7.16"],"affected":"< 1.7.16","ecosystem":"npm"},{"name":"better-auth","fixed":["1.8.0-beta.5"],"affected":">= 1.8.0-beta.0, < 1.8.0-beta.5","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["auth","better-auth","cwe-863","node","package","self-hosted"],"not_affected_if":["admin-plugin-disabled","only-full-admin-can-update-users","phone-plugin-disabled"],"how_to_check":"- Check the installed version with `npm ls better-auth`.\n- Check whether the admin plugin and the phone number plugin are both enabled in your better-auth configuration.\n- Check whether any custom role other than the full admin role holds `user:update`.","fix":"- Upgrade `better-auth` to `1.7.16` on the 1.7 line.\n- Upgrade `better-auth` to `1.8.0-beta.5` on the beta line.","status":"open","published_at":"2026-11-03T22:07:18+00:00","first_seen_at":"2026-11-09T22:53:20+00:00","triaged_at":"2026-11-09T22:54:37+00:00","updated_at":"2026-11-09T22:54:37+00:00","category":"package","confidence":0.85,"triage":{"at":"2026-11-09T22:54:37Z","model":"claude-haiku-5-5","output":{"fix":"- Upgrade `better-auth` to `1.7.16` on the 1.7 line.\n- Upgrade `better-auth` to `1.8.0-beta.5` on the beta line.","reach":"internet","title":"better-auth: limited admin roles can change another user's phone number","impact":"high","summary":"A role that holds the user:update permission in the better-auth admin plugin can set another user's phone number and mark it verified. The attacker can then sign in to that account with a phone OTP and take over its session, including accounts with more privileges.","audience":"developers","category":"package","nicknames":[],"who_cares":["better-auth","node","self-hosted","auth"],"confidence":0.85,"fix_status":"available","needs_auth":"yes","worst_case":"Attacker takes over another user's account, including more privileged ones.","analyst_note":"","how_to_check":"- Check the installed version with `npm ls better-auth`.\n- Check whether the admin plugin and the phone number plugin are both enabled in your better-auth configuration.\n- Check whether any custom role other than the full admin role holds `user:update`.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["admin-plugin-disabled","phone-plugin-disabled","only-full-admin-can-update-users"],"who_is_affected":"You are affected only if you use both the admin plugin and the phone number plugin from better-auth, and a role other than the full admin role holds the user:update permission. If only full administrators can update users, you are not affected. Custom access control setups that grant user:update to limited roles are the ones to review.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"unknown"},"cost_usd":0.0008,"input_tokens":5120,"output_tokens":575,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-2026':370C '-863':37B '-938':371C '/admin/update-user':319C,327C '1.7.16':241C '1.8.0':244C '8c3j':20A,25A 'access':277C 'account':77B,88B,207C,214C,228C 'add':279C 'address':159C,337C 'admin':4A,56B,107C,124C,144C,269C 'administr':138C 'affect':94C,97C,134C 'allow':169C 'anoth':11A,60B,176C,204C 'ant':369C 'anthrop':363C 'attack':70B 'auth':3A,28A,31A,32B,35B,55B,117C,239C,324C 'bej5j':372C 'beta':248C 'beta.5':245C 'better':2A,27A,30A,34B,54B,116C,238C,323C 'better-auth':1A,26A,29A,33B,53B,115C,237C 'chang':10A,154C,163C,250C,294C,306C,334C 'check':328C,349C 'control':278C 'could':174C 'credit':360C 'custom':276C 'cwe':36B 'default':268C 'detail':310C,326C 'discov':361C 'edit':171C 'either':355C 'email':158C,332C,336C 'endpoint':148C 'field':341C,357C 'full':123C,137C 'g95c':18A,23A 'ghsa':17A,22A 'ghsa-g95c-wqvr-8c3j':16A,21A 'give':220C 'grant':287C 'hold':46B,126C,194C 'host':42B 'impact':186C 'includ':87B,227C 'limit':191C 'line':249C 'mark':66B,182C 'need':259C 'new':261C 'node':38B 'number':15A,64B,112C,166C,180C,202C,296C 'oss':365C 'otp':81B,218C 'packag':39B 'pass':339C 'patch':235C 'permiss':50B,130C,152C,266C 'phone':14A,63B,80B,111C,165C,179C,201C,217C,265C,282C,295C,340C,353C,356C 'phonenumb':251C,313C 'phonenumberverifi':253C,315C 'plugin':57B,108C,113C 'present':359C 'privileg':91B,231C 'profil':173C 'put':198C 'reach':322C 'reject':312C 'remov':298C 'request':317C 'requir':149C 'role':5A,44B,119C,125C,168C,192C,270C,291C,302C 'scanner':366C 'self':41B 'self-host':40B 'separ':151C 'session':86B,226C 'set':59B,175C,264C,281C,331C,352C 'set-email':330C 'set-phon':263C,280C,351C 'sign':73B,210C,308C 'sign-in':307C 'statement':285C 'summari':142C 'take':83B 'technic':325C 'true':103C 'updat':8A,49B,129C,140C,146C,196C,256C,300C,346C 'update-us':145C,255C 'upgrad':236C 'use':105C,275C 'user':7A,12A,48B,61B,128C,141C,147C,156C,172C,177C,188C,195C,205C,224C,257C,262C,299C,329C,345C,350C 'verifi':68B,185C 'vulner':368C 'workaround':297C 'wqvr':19A,24A","nicknames":[],"mitigations":[],"who_affected":"You are affected only if you use both the admin plugin and the phone number plugin from better-auth, and a role other than the full admin role holds the user:update permission. If only full administrators can update users, you are not affected. Custom access control setups that grant user:update to limited roles are the ones to review.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["betterauth"],"ptokens":["auth","better"],"labels":["auth","betterauth","cwe863","medium","node","npm","package","selfhosted"],"campaign_type":null,"search_a":"'8c3j':20A,25A 'admin':4A 'anoth':11A 'auth':3A,28A,31A 'better':2A,27A,30A 'better-auth':1A,26A,29A 'chang':10A 'g95c':18A,23A 'ghsa':17A,22A 'ghsa-g95c-wqvr-8c3j':16A,21A 'number':15A 'phone':14A 'role':5A 'updat':8A 'user':7A,12A 'wqvr':19A,24A","search_b":"'-863':37B 'account':77B,88B 'admin':56B 'anoth':60B 'attack':70B 'auth':32B,35B,55B 'better':34B,54B 'better-auth':33B,53B 'cwe':36B 'hold':46B 'host':42B 'includ':87B 'mark':66B 'node':38B 'number':64B 'otp':81B 'packag':39B 'permiss':50B 'phone':63B,80B 'plugin':57B 'privileg':91B 'role':44B 'self':41B 'self-host':40B 'session':86B 'set':59B 'sign':73B 'take':83B 'updat':49B 'user':48B,61B 'verifi':68B","search_c":"'-2026':370C '-938':371C '/admin/update-user':319C,327C '1.7.16':241C '1.8.0':244C 'access':277C 'account':207C,214C,228C 'add':279C 'address':159C,337C 'admin':107C,124C,144C,269C 'administr':138C 'affect':94C,97C,134C 'allow':169C 'anoth':176C,204C 'ant':369C 'anthrop':363C 'auth':117C,239C,324C 'bej5j':372C 'beta':248C 'beta.5':245C 'better':116C,238C,323C 'better-auth':115C,237C 'chang':154C,163C,250C,294C,306C,334C 'check':328C,349C 'control':278C 'could':174C 'credit':360C 'custom':276C 'default':268C 'detail':310C,326C 'discov':361C 'edit':171C 'either':355C 'email':158C,332C,336C 'endpoint':148C 'field':341C,357C 'full':123C,137C 'give':220C 'grant':287C 'hold':126C,194C 'impact':186C 'includ':227C 'limit':191C 'line':249C 'mark':182C 'need':259C 'new':261C 'number':112C,166C,180C,202C,296C 'oss':365C 'otp':218C 'pass':339C 'patch':235C 'permiss':130C,152C,266C 'phone':111C,165C,179C,201C,217C,265C,282C,295C,340C,353C,356C 'phonenumb':251C,313C 'phonenumberverifi':253C,315C 'plugin':108C,113C 'present':359C 'privileg':231C 'profil':173C 'put':198C 'reach':322C 'reject':312C 'remov':298C 'request':317C 'requir':149C 'role':119C,125C,168C,192C,270C,291C,302C 'scanner':366C 'separ':151C 'session':226C 'set':175C,264C,281C,331C,352C 'set-email':330C 'set-phon':263C,280C,351C 'sign':210C,308C 'sign-in':307C 'statement':285C 'summari':142C 'technic':325C 'true':103C 'updat':129C,140C,146C,196C,256C,300C,346C 'update-us':145C,255C 'upgrad':236C 'use':105C,275C 'user':128C,141C,147C,156C,172C,177C,188C,195C,205C,224C,257C,262C,299C,329C,345C,350C 'verifi':185C 'vulner':368C 'workaround':297C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-11-03T22:07:18+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-g95c-wqvr-8c3j","url":"https://github.com/better-auth/better-auth/security/advisories/GHSA-g95c-wqvr-8c3j","severity":"medium","published_at":"2026-11-03T22:07:18+00:00","modified_at":"2026-11-03T22:07:18+00:00","first_seen_at":"2026-11-09T22:53:20+00:00","fetched_at":"2026-11-09T22:53:20+00:00","note":null}],"documents":[{"id":110498,"url":"https://github.com/better-auth/better-auth/security/advisories/GHSA-g95c-wqvr-8c3j","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa-repo","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}