Easy!Appointments: Account Takeover via Poisoned Password Reset Links
An anonymous attacker sets the host in another account's password reset link to their own server and takes over the account after the victim clicks it.
Advisory ID: TP-2026-112
Product: Easy!Appointments (open source web application for online appointment booking)
Vulnerability type: Weak password recovery mechanism (CWE-640)
CVE: not requested
CVSS 3.1: 7.5 (High) · CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
Affected versions: <= 1.6.0
Vendor advisory: GHSA-c5rh-f2vc-2qvj
Status: No fix available, vendor advisory published
Reported: 17 July 2026
Summary
Easy!Appointments is a web application for appointment booking with roles for administrators, providers, secretaries and customers. The password reset function builds the link in the reset mail from the request's Host header instead of the configured base URL. An anonymous attacker triggers the reset for another user's account with their own Host header, and the genuine mail to the victim points to the attacker's server with a valid token. When the victim opens the link, the attacker receives the token, sets a new password and takes over the account, which for the administrator means the whole instance.
Root cause
application/config/config.php:25 reads the host name from $_SERVER['HTTP_HOST'], and application/config/config.php:33 derives base_url from it for every web request, while the configured constant Config::BASE_URL only applies to CLI calls. Recovery::perform() (application/controllers/Recovery.php:56) builds the reset link with site_url() on top of that URL (application/controllers/Recovery.php:127). The link is sent through Email_messages::send_password_reset_link() to the stored address of the account (application/libraries/Email_messages.php:222). The token itself is strong, bin2hex(random_bytes(32)) stored as SHA-256 (application/libraries/Accounts.php:225-228), so the weakness lies solely in the host of the link. The anonymous endpoint only asks for the account's username and email address (application/controllers/Recovery.php:98-117), and no allowlist of trusted hosts checks the Host header before the mail goes out.
Proof of Concept
Schematically:
POST /index.php/recovery/perform HTTP/1.1
Host: <attacker-host>
Content-Type: application/x-www-form-urlencoded
username=<username>&email=<email-address>&<csrf-field>=<csrf-token>
-> reset mail to the victim points to http://<attacker-host>/.../recovery/reset?token=<token>
The application copies the forged host into the link, and the victim's click delivers the token to the attacker's server. With this token the attacker sets a new password through the regular reset flow.
Impact
- Takeover of any account whose username and email address are known, after the victim clicks the link in a genuine mail from the application.
- Full administrator rights for the default administrator account, with access to all appointments, customer data and the system configuration.
- No sign of an attack in the mail itself, since sender and content come from the application.
References
A patch from the vendor is still pending. The fix release and any CVE ID will be added here once available.
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.
