Executive brief
Speakr is a self-hosted audio transcription application. Prior to version 0.8.21-alpha, attackers could bypass CSRF protection on state-changing operations (like account modifications and admin privilege escalation) using a forged API token in a URL parameter. By combining this with a password-change flaw affecting SSO-only accounts, an attacker could set a new password on a victim's account from a malicious website and then log in directly, completely bypassing single sign-on authentication.
Technical details
The vulnerability combines two related flaws. First, the csrf_exempt_for_api_tokens() before_request hook calls Flask-WTF's csrf.exempt(view_func) when a token-shaped value is present (including from query parameters like ?token=...), without validating the token; since this mutates a process-global exemption set, a single attacker request permanently disables CSRF for that view. Second, a Simple Cross-Origin GET request to a query-string token doesn't trigger CORS preflight, allowing an attacker page to poison CSRF state. Third, the /change_password route skips current-password verification when current_user.password is empty (SSO-only accounts), allowing an attacker to set a new local password. The attack vector is network-reachable (cross-origin), requires no authentication, and modifies session/profile data, custom prompts, transcription settings, and administrative status. Fixed in v0.8.21-alpha by removing the hook, moving CSRF-skipping decisions to per-request evaluation via load_user_from_token_headers_only() with proper database validation, and rejecting query-string tokens on non-API endpoints.
Affected products
- Murtaza Nasir Speakr prior to 0.8.21-alpha
Timeline
- 2026-09-17: disclosed
- 2026-06-04: patched: Fix released in v0.8.21-alpha