Feb 17, 2026·5 min read·3 visits
Easy!Appointments' CSRF protection logic explicitly ignores GET requests. Since the application's controllers accept input via GET, an attacker can craft a malicious link that, when clicked by an admin, creates a new administrator account without triggering any security warnings.
A critical Cross-Site Request Forgery (CSRF) vulnerability in the Easy!Appointments scheduler allows attackers to bypass security checks simply by changing the HTTP method. By downgrading state-changing requests from POST to GET, an attacker can trick an administrator into creating rogue admin accounts or resetting passwords, leading to full system compromise.
Appointment scheduling software is the unsung hero of the SMB world. It’s the digital receptionist that never sleeps, handling personal data, calendars, and business logic. Easy!Appointments is a popular self-hosted choice for this exact purpose. But here's the thing about self-hosted PHP applications: they often carry the legacy of "classic" web development practices.
CVE-2026-23622 isn't a complex memory corruption bug or a futuristic AI-driven exploit. It is a logic error so fundamental it feels almost nostalgic. It reminds us that while we are busy worrying about quantum cryptography, the door is often left unlocked because the carpenter forgot to install the latch properly.
The vulnerability lies in how the application decides when to check for security tokens. It operates on a trust model that assumes the internet plays by the rules: that data is only changed via POST requests, and GET requests are harmless observations. As any hacker knows, the internet does not play by the rules.
Let's talk about the root cause. In the file application/core/EA_Security.php, there is a function called csrf_verify(). Its job is simple: ensure that the person submitting a form is actually the person browsing the site, not a puppet being controlled by a malicious script on another tab.
However, the implementation contains a fatal flaw. The developer implemented a conditional check that effectively says: "If this is a POST request, check the badge. If it's anything else, let them through."
Here is the logic in plain English: The code specifically checks $_SERVER['REQUEST_METHOD']. If it is not POST, the function returns early, skipping the CSRF token validation entirely. This is akin to a nightclub bouncer who aggressively frisks anyone entering through the main door but leaves the side window wide open because "people usually don't climb through windows."
Let's look at the code responsible for this bypass. This is found in application/core/EA_Security.php around line 52.
public function csrf_verify() {
// ... initialization code ...
// THE VULNERABILITY
// The code assumes only POST requests change state.
if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST') {
return; // Security check bypassed!
}
// Actual token validation happens here, but only for POST.
if ($this->get_csrf_hash() !== $this->get_csrf_token_from_request()) {
show_error('The CSRF token is invalid.', 403);
}
}Why this kills the application:
The vulnerability in EA_Security.php wouldn't be catastrophic if the controllers (the code handling the business logic) were strict. However, PHP frameworks often use helper methods like $_REQUEST or CodeIgniter's $this->input->get_post() which happily accept parameters from the query string (GET) just as easily as the body (POST).
Because the controllers don't check the method, and the security middleware ignores the method, we have a perfect storm. We can perform write operations using read semantics.
Exploiting this does not require Burp Suite or Python scripts. It requires a browser and a little social engineering. The goal is to hit the /index.php/admins/store endpoint. Normally, this expects a POST request with the new admin's details and a valid CSRF hash.
The Attack Chain:
Instead of a POST body:
first_name=Hacker&email=h@ack.er...
We send a GET request:
http://target-scheduler.local/index.php/admins/store?first_name=Hacker&last_name=Admin&email=hacker@example.com&password=Pwn3d123!&role_id=1
When the admin's browser loads this URL, the csrf_verify function sees GET, says "carry on," and passes execution to Admins::store(). The controller reads the variables from the URL, assumes they are valid inputs, and writes a new administrative user to the database.
The impact here is high because it bypasses the primary authentication barrier for administrative actions. We aren't just defacing a website; we are creating a persistent backdoor.
Once the attacker has created a new admin account via the CSRF link, they can:
Since this exploit requires no interaction other than loading a URL, it can be embedded in a 1x1 pixel image on a forum the admin frequents. The admin doesn't even need to click a link; they just need to view a page while logged in.
The remediation is straightforward: Do not trust HTTP methods to act as security boundaries. Security checks must be agnostic to the transport method, or the transport method must be strictly enforced.
Official Patch (v1.5.3+):
The vendor has patched this by modifying EA_Security.php (or the relevant controllers) to ensure that state-changing actions cannot be triggered via GET, or by enforcing CSRF token validation on all requests that are not strictly read-only.
Manual Mitigation:
If you cannot update immediately, you must modify application/core/EA_Security.php. Remove the early return for non-POST methods, or explicitly deny GET requests to sensitive controllers (Admins, Account, Settings).
> [!TIP]
> As a developer, never assume $_REQUEST is safe. Always use specific input retrieval methods like $_POST (or $this->input->post() in CodeIgniter) for write operations to ensure the request is coming through the expected channel.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Easy!Appointments alextselegidis | <= 1.5.2 | 1.5.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-352 (CSRF) |
| CVSS v3.1 | 8.8 (High) |
| Attack Vector | Network (Drive-by) |
| Privileges Required | None (Victim Interaction) |
| Affected Component | EA_Security.php / csrf_verify |
| Exploit Status | Proof of Concept Available |
The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.