Jul 30, 2026·6 min read·3 visits
Authenticated providers can inject or reassign appointments into schedules of other providers due to missing session-boundary validation on mutation endpoints, compounded by a write-before-crash logic flaw.
An authorization bypass and logical 'write-before-crash' vulnerability in Easy!Appointments versions prior to 1.6.0 allows authenticated users with the 'Provider' role to inject unauthorized bookings into foreign providers' schedules or reassign existing appointments via parameter manipulation on the 'store' and 'update' endpoints.
Easy!Appointments is an open-source, self-hosted appointment scheduling application written in PHP on top of the CodeIgniter framework. The application uses a multi-tenant role model to separate system administrators, secretaries, providers, and customers. The 'Provider' role is designed to manage individual calendars, service availabilities, and customer appointments associated only with that specific provider account.
While the application successfully restricts read access boundaries—such as limiting the appointments returned in search results through the appointments/search endpoint—it does not enforce equivalent logical separations during data mutation operations. The endpoints responsible for persisting new appointments (appointments/store) and modifying existing ones (appointments/update) allow callers to supply arbitrary provider identifiers. This configuration exposes an insecure direct object reference (IDOR) vector to authenticated attackers.
Because the backend controller lacks verification checks to confirm that the submitted provider identifier corresponds to the active session user, authenticated providers can manipulate foreign calendars. An attacker with standard provider privileges can inject unauthorized appointments into another provider's schedule or hijack existing entries. This vulnerability breaks the primary multi-tenant isolation assumption of the platform.
The root cause of this vulnerability lies in the lack of session-based validation within the controller class located at application/controllers/Appointments.php. During HTTP POST requests to the store and update endpoints, the application decodes a JSON payload containing the appointment data structure. This structure includes the id_users_provider field, which determines the database association for the appointment.
The controller directly maps the request data to the database abstraction model without checking if the caller holds the appropriate permissions to write to the requested provider ID. The backend relies solely on the application-wide check that verifies whether the caller has the general privilege to create or edit appointments. It fails to apply contextual, row-level verification to ensure the caller's session matches the owner of the target schedule.
Furthermore, the store flow contains a logical 'write-before-crash' anomaly. After executing the database save operation, the code attempts to retrieve the newly written record using a find operation but incorrectly passes the complete associative appointment array instead of the integer insertion ID. This mismatch causes PHP to raise a fatal runtime TypeError, terminating execution. Because the database transaction is already committed before the crash occurs, the unauthorized injection succeeds despite the application returning an HTTP 500 error to the client.
Analyzing the vulnerable code path in application/controllers/Appointments.php before version 1.6.0 reveals the unsafe parameter mapping. The JSON input is converted into an associative array, parsed for allowed database fields, and immediately processed by the active record model. The application processes the database write using trusted and untrusted inputs interchangeably.
// Vulnerable Controller Flow (Before 1.6.0)
public function store(): void
{
// Missing validation to ensure session_id matches the submitted provider ID
$appointment = json_decode(request('appointment'), true);
$this->appointments_model->only($appointment, $this->allowed_appointment_fields);
$this->appointments_model->optional($appointment, $this->optional_appointment_fields);
// The application commits the arbitrary id_users_provider directly to the database
$appointment_id = $this->appointments_model->save($appointment);
// TYPE ERROR CRASH: The find() method expects an integer ID but receives the array
$appointment = $this->appointments_model->find($appointment);
$this->webhooks_client->trigger(WEBHOOK_APPOINTMENT_SAVE, $appointment);
}The patched implementation in version 1.6.0 resolves this validation gap by actively checking the caller's session role. If the active session is associated with the 'Provider' role, the controller overwrites any client-provided id_users_provider key with the authenticated user ID stored in the server session. The update flow implements an identical mapping check to secure existing records.
// Patched Controller Flow (Version 1.6.0)
public function store(): void
{
$appointment = json_decode(request('appointment'), true);
if (!is_array($appointment)) {
throw new InvalidArgumentException('Invalid appointment data provided.');
}
$user_id = (int) session('user_id');
$role_slug = session('role_slug');
// Strict session enforcement for the provider role
if ($role_slug === DB_SLUG_PROVIDER) {
$appointment['id_users_provider'] = $user_id;
}
$this->appointments_model->only($appointment, $this->allowed_appointment_fields);
$this->appointments_model->optional($appointment, $this->optional_appointment_fields);
$appointment_id = $this->appointments_model->save($appointment);
// Fixed type argument prevents the runtime server crash
$appointment = $this->appointments_model->find($appointment_id);
$this->webhooks_client->trigger(WEBHOOK_APPOINTMENT_SAVE, $appointment);
}To exploit this vulnerability, an attacker must first obtain valid credentials associated with a 'Provider' account. Once authenticated, the attacker monitors network traffic to capture the structure of legitimate creation and modification requests sent to the backend endpoints. The target identifiers are gathered by viewing the public scheduling interface or querying public directories.
An attacker targets the appointments/store endpoint by formulating a POST request containing an appointment structure. By replacing the id_users_provider parameter with the ID of the target provider, the transaction is routed to the target provider's schedule. The attacker triggers the exploit payload directly over standard HTTP.
POST /appointments/store HTTP/1.1
Host: scheduling-system.local
Content-Type: application/x-www-form-urlencoded
Cookie: csrf_cookie_name=...; session_id=...
appointment=%7B%22id_users_provider%22%3A12%2C%22id_users_customer%22%3A45%2C%22id_services%22%3A2%2C%22start_datetime%22%3A%222026-08-01+10%3A00%3A00%22%2C%22end_datetime%22%3A%222026-08-01+11%3A00%3A00%22%7DWhen the server processes this request, it writes the record to the database and returns an HTTP 500 response. This status code indicates a generic failure to the client but masks the successful injection of the record. The attacker can verify the injection by querying the target provider's public calendar page, which now registers the block.
The security impact of CVE-2026-52839 is rated as low-to-moderate with a CVSS v3.1 base score of 3.3. The vulnerability does not allow remote code execution or direct database exposure. However, it compromises data integrity and authorization boundaries within multi-tenant deployments of the application.
Because of this vulnerability, unauthorized users can modify, corrupt, or inject records into restricted schedules. In organizations with competing providers or sensitive internal workflows, this issue allows malicious actors to systematically block available slots or reassign high-value bookings. The silent success of the write operation complicates detection.
This behavior makes manual log inspections necessary because security systems might misinterpret the HTTP 500 error responses as simple system failures rather than successful exploitation attempts. The scope remains restricted to the database tables associated with Easy!Appointments scheduling records.
The primary remediation for this vulnerability is to upgrade Easy!Appointments instances to version 1.6.0 or higher. Version 1.6.0 introduces input verification checks and enforces session-based provider parameters. This release also resolves the type error bug in the store endpoint, ensuring consistent error responses.
If upgrading is not immediately possible, security administrators can apply a manual code patch. This is done by modifying application/controllers/Appointments.php to include role checks that overwrite client-provided provider parameters with active session IDs. The type error bug must also be corrected to ensure correct webhook processing.
Organizations should also audit active database tables for historical inconsistencies. Queries can be executed to compare the logged creation user with the actual provider assigned to active bookings. This process helps locate historical indicators of compromise.
CVSS:3.1/AV:N/AC:H/PR:H/UI:N/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.3 (Low) |
| Exploit Status | None (No active public exploits) |
| CISA KEV Status | Not Listed |
| Affected Functions | appointments/store, appointments/update |
The system uses a user-controlled key to identify resources in an SQL transaction without verifying that the key belongs to the authenticated actor.
Easy!Appointments prior to version 1.6.0 is vulnerable to Server-Side Request Forgery (SSRF) within its CalDAV integration module. The system handles user-supplied URLs in the connection test endpoint without verifying host constraints or network schemes, allowing authenticated backend users to probe internal infrastructure.
An unauthenticated Personally Identifiable Information (PII) disclosure vulnerability exists in Easy!Appointments versions up to and including 1.5.2. Requesting the booking reschedule endpoint with a valid appointment hash causes the system to embed the raw customer database record into the HTML response as inline JavaScript variables, exposing sensitive details. This includes email addresses, phone numbers, physical addresses, custom database metadata, and internal directory configurations such as LDAP DNs. The vulnerability has been resolved in version 1.6.0.
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.
This report provides a comprehensive technical teardown of CVE-2026-52838 (GHSA-996f-334j-67g7), a stored Cross-Site Scripting (XSS) vulnerability in Easy!Appointments. The flaw occurs in how the application manages the 'booking disabled' custom message configuration, allowing high-privileged administrators to persist unsanitized payloads that execute on unauthenticated guest landing pages.
CVE-2026-54574 (GHSA-9xq3-3fqg-4vg7) is a critical Symlink Escape and Arbitrary Host File Write vulnerability in proot-distro, an open-source utility for managing rootless PRoot containers on Termux and general Linux environments. The vulnerability is rooted in an asymmetric validation flaw during the archive extraction process of container installations, Docker/OCI layers, and container backup restorations. While the extraction engine successfully validated file names to prevent standard directory traversal (e.g., rejecting components containing '..'), it failed to validate symbolic link targets. An attacker could craft a malicious tar archive or container image that plants an absolute host-path symlink. Subsequent file members within the same archive could then traverse through this symlink, writing arbitrary files directly onto the host filesystem under the privileges of the executing process.
A container isolation bypass vulnerability exists in proot-distro prior to version 5.1.6. The utility accepted hardlink entries pointing outside the container directory being restored, allowing cross-container file read and write capabilities.