Jul 30, 2026·6 min read·67 visits
An authorization bypass flaw in Easy!Appointments allows authenticated backend users to bind their personal Google OAuth token to other providers' profiles, syncing external scheduling metadata and customer PII directly to the attacker's calendar.
An authorization bypass vulnerability exists in Easy!Appointments before version 1.6.0. The application fails to validate ownership of the provider ID during the Google OAuth synchronization process. This allows authenticated backend users to link their personal Google Calendars to peer providers, leading to unauthorized access to scheduled appointments and associated customer metadata.
Easy!Appointments is an open-source, self-hosted appointment scheduler written in PHP. To facilitate workflow integration, the application allows provider accounts to synchronize their schedules with Google Calendar. This sync engine relies on OAuth 2.0 to safely exchange credentials and interact with Google's APIs.
Historically, the application managed the OAuth handshaking phase using a multi-step redirection workflow. The configuration process starts at /google/oauth/<provider_id> and concludes when the Google authorization server redirects the browser back to the /google/oauth_callback callback route. This multi-step process introduces a state-tracking dependency.
The vulnerability is categorized under CWE-639 (Authorization Bypass Through User-Controlled Key). Because the application lacks permission checks when restoring and writing state during the finalization phase, authenticated attackers can reconfigure third-party settings. This mechanism exposes internal appointment schedules and associated client Personally Identifiable Information (PII).
The root cause of this flaw lies in the insecure handling of session state data in the Google controller (application/controllers/Google.php). When a provider initiates synchronization, the browser calls /google/oauth/<provider_id>, passing the target provider's identifier. The application stores this parameter directly inside the active PHP session variable oauth_provider_id.
The initialization step does not verify whether the currently logged-in user owns the specified provider ID or possesses administrative privileges to alter settings for peer users. Any authenticated user can supply an arbitrary ID in the URL. This action forces the server to update the attacker's active session state with the targeted provider's ID.
Upon successful authentication at the Google OAuth gateway, Google redirects the client to the callback endpoint /google/oauth_callback. The server retrieves the target provider ID from the attacker's active session and applies the OAuth sync configuration. Because the backend trusts the value of oauth_provider_id without validating it against the active user_id or administrative permissions, the session pollution results in an unauthorized configuration change.
Before the fix was applied, the application's callback logic did not validate the ownership of the session state. The following code fragment demonstrates the vulnerable execution flow:
// application/controllers/Google.php (Vulnerable version)
// The oauth() method trustingly populates the session:
// session('oauth_provider_id', $provider_id);
public function oauth_callback(): void
{
// ... [Token retrieval logic is executed] ...
// Vulnerable step: retrieve value and modify DB without access controls
$oauth_provider_id = session('oauth_provider_id');
if ($oauth_provider_id) {
$this->providers_model->set_setting($oauth_provider_id, 'google_sync', true);
$this->providers_model->set_setting($oauth_provider_id, 'google_token', json_encode($token));
$this->providers_model->set_setting($oauth_provider_id, 'google_calendar', 'primary');
}
}The security vulnerability is addressed in commit 4b2d245d2cd2058dc76e05f6eb65b26699268471 by introducing robust data sanitization, explicit integer casting, and active validation rules:
// application/controllers/Google.php (Patched version)
public function oauth_callback(): void
{
// ... [Token retrieval logic is executed] ...
// Store the token into the database for future reference.
$oauth_provider_id = filter_var(session('oauth_provider_id'), FILTER_VALIDATE_INT);
$user_id = (int) session('user_id');
if ($oauth_provider_id && $oauth_provider_id > 0) {
// Verify permissions and ensure ownership matching
if (cannot('edit', PRIV_USERS) && $user_id !== (int) $oauth_provider_id) {
show_error('Forbidden', 403);
return;
}
$this->providers_model->set_setting($oauth_provider_id, 'google_sync', true);
$this->providers_model->set_setting($oauth_provider_id, 'google_token', json_encode($token));
$this->providers_model->set_setting($oauth_provider_id, 'google_calendar', 'primary');
// Clear session state to prevent replay attacks
session(['oauth_provider_id' => null]);
}
}The integration of the authorization guard blocks unauthorized updates. If the active $user_id does not match the $oauth_provider_id session value and the caller lacks global administration permissions (cannot('edit', PRIV_USERS)), the request is rejected with an HTTP 403 response. Additionally, purging the session variable post-binding prevents replay exploitation vectors.
Exploitation of CVE-2026-52841 requires an active authenticated session within the Easy!Appointments system. This vulnerability cannot be exploited by an unauthenticated network actor. The attacker must occupy a legitimate backend role, such as a low-privileged provider or secretary.
To perform the attack, the attacker interacts with the OAuth initialization endpoint using a target provider's identifier. For example, if the attacker wishes to compromise provider user ID 2, they navigate directly to the following path:
https://<target-app>/google/oauth/2
This HTTP request populates the attacker's session state. The attacker then undergoes the standard OAuth approval process with their own Google credentials. Upon redirection back to /google/oauth_callback, the server reads the target ID of the victim from the session storage and links the attacker's Google API credentials to the victim's scheduling profile. The execution flow is summarized below:
Following the successful completion of this handshake, all new bookings and schedule changes associated with the victim automatically sync to the attacker's Google Calendar.
The operational impact of this vulnerability is the compromise of confidential client booking details. This data exposure poses a risk of regulatory non-compliance with frameworks like GDPR. Information synced to the external calendar database includes customer names, emails, phone numbers, and reservation metadata.
Additionally, calendar hijacking impairs business operations. The victim provider is prevented from syncing their appointments to their legitimate Google Calendar. Instead, they lose synchronization functionality, while the unauthorized attacker gains persistent oversight of their client queues and operational timelines.
Although the low CVSS v3.1 score of 3.1 reflects high privilege requirements and necessary user interaction, the vulnerability remains critical in multi-tenant environments. In organizations where multiple independent providers operate on a shared scheduler, the ability to spy on peers represents a direct compromise of the application's isolation model.
To address this vulnerability, self-hosted administrators must update their Easy!Appointments instances to version 1.6.0 or newer. This release implements explicit identity matching and privilege controls within the Google OAuth callback handler.
If immediate software upgrades are not feasible, administrators should audit their active configurations to identify potential signs of exploitation. You can query the configuration table to identify unexpected duplicate token strings or suspicious settings values shared between different provider profiles.
Reviewing web server access logs is also recommended. Look for pattern sequences where a single user session initiates /google/oauth/<id> queries for multiple distinct numeric identifiers in close temporal proximity. This log signature indicates active session manipulation attempts.
CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:U/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Easy!Appointments Alextselegidis | < 1.6.0 | 1.6.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-639 |
| Attack Vector | Network |
| CVSS v3.1 Score | 3.1 (Low) |
| EPSS Score | 0.00129 |
| CISA KEV Status | Not Listed |
| Exploit Status | No public PoC |
| Vulnerable Versions | < 1.6.0 |
The system's authorization mechanism does not adequately verify the identity of the user requesting to access or modify resources, relying instead on user-provided keys.
Contao CMS versions 4.1.0 through 5.3.49 and 5.4.0-RC1 through 5.7.11 fail to validate form submission tokens and enforce rate limiting when processing activation email resend requests via HTTP POST, enabling resource exhaustion and account state enumeration.
An information disclosure vulnerability in Contao CMS allows unauthenticated site visitors to view protected page titles, URLs, and text excerpts through search queries when protected page indexing is disabled after previously being enabled.
In Vikunja prior to version 2.6.0, relation creation via the CalDAV endpoint fails to invoke the TaskRelation.CanCreate authorization check. This missing access control allows an authenticated user to establish unauthorized relationships and perform write operations against any task, provided its unique identifier (UID) is known.
A cross-project information disclosure vulnerability in Vikunja allows authenticated users with read access to one project to view private task details from unauthorized projects via subtask expansion parameters.
An information disclosure vulnerability in Vikunja allows authenticated users with read access to a task to expose private email addresses of assigned users through the API task assignees endpoint due to an unmasked database query.
An authorization bypass vulnerability in Vikunja versions prior to v2.6.0 permits authenticated users to delete relationships between tasks across project boundaries without requiring read or write authorization for the target related task.