Easy!Appointments, Easy!Pwnage: CSRF Bypass via Method Confusion
Jan 16, 2026·5 min read
Executive Summary (TL;DR)
The Easy!Appointments scheduler fails to validate CSRF tokens for GET requests. Because the application's controllers inadvertently process data from the URL query string, attackers can perform administrative actions (like creating a new admin user) by sending a victim a crafted link. Fixed in version 1.5.3.
A critical Cross-Site Request Forgery (CSRF) vulnerability in Easy!Appointments allows unauthenticated attackers to create administrative accounts by simply tricking an existing admin into clicking a link. The flaw stems from a logic error where CSRF tokens are only validated on POST requests, while sensitive controllers willingly accept GET parameters.
The Hook: Scheduling Your Own Admin Access
Easy!Appointments is a popular self-hosted scheduler used by businesses to handle everything from haircuts to dental checkups. It’s designed to be simple, efficient, and user-friendly. Unfortunately, in versions 1.5.2 and below, it was also "easy" for attackers to grant themselves full administrative access without ever logging in.
The vulnerability lies in a classic disconnect between the security layer and the application logic. The security team (or in this case, the csrf_verify middleware) assumed that dangerous state-changing operations only happen via POST requests. Meanwhile, the controllers handling those operations were happy to take input from anywhere, including the URL bar. This mismatch created a gaping hole: if you ask nicely via a GET request, the bouncer steps aside, and the application processes your command without checking for an invite (CSRF token).
The Flaw: The "GET is Safe" Fallacy
The root cause of CVE-2026-23622 is a logic error in application/core/EA_Security.php. The developers implemented a global CSRF check, which is generally a good practice. However, they made a fatal assumption: they assumed that if a request isn't a POST, it doesn't need to be checked.
In the HTTP standard, GET requests are supposed to be "idempotent" and "safe"—meaning they should only retrieve data, not change it. The security middleware relied on this convention rather than enforcing it. The code explicitly checks $_SERVER['REQUEST_METHOD']. If it sees anything other than POST (like GET, HEAD, or OPTIONS), it essentially says, "Nothing to see here, go right ahead," and returns early, bypassing the token verification entirely.
This would have been fine if the application actually respected that rule. But PHP's $_REQUEST superglobal (or loose framework input handling) often merges $_GET and $_POST data. This means that when the Admins::store controller asks for a first_name, it doesn't care if that data came from a submitted form or a URL query string. The security layer let the request through because it looked safe (GET), and the controller executed it because it had the data it needed.
The Code: The Sleeping Watchman
Let's look at the smoking gun in EA_Security.php. This is the function responsible for stopping Cross-Site Request Forgery. It decides whether to inspect a request or let it pass.
// application/core/EA_Security.php
public function csrf_verify() {
// VULNERABLE LOGIC
// The developer assumes only POST requests can change state.
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
return; // The gate opens wide.
}
// The actual token validation logic is down here.
// If the attacker uses GET, this code is dead code.
if ( ! isset($_POST[$this->token_name]) || ... ) {
// Error handling
}
}Now consider the target controller, for example, Admins.php. It likely grabs input using a wrapper that doesn't discriminate by method:
// application/controllers/Admins.php (Conceptual)
public function store() {
// The framework happily pulls 'first_name' from the URL query string
$admin_data = array(
'first_name' => $this->input->get_post('first_name'),
'email' => $this->input->get_post('email'),
// ...
);
// And saves it to the database, creating a new admin.
$this->admins_model->add($admin_data);
}This combination is catastrophic. The security layer ignores the request because of its method, and the logic layer processes the payload regardless of the method.
The Exploit: Clicking Your Way to Compromise
Because the vulnerability is a CSRF bypass, the exploit requires user interaction. The attacker needs to trick a logged-in administrator into clicking a link or visiting a malicious page. This is trivial to achieve via a phishing email (
The Impact: Game Over
This is a High Severity vulnerability (CVSS 7.4) for a reason. In a self-hosted environment, the scheduler is often the gateway to customer PII (Personally Identifiable Information), appointment histories, and internal business logic.
By exploiting this, an attacker can:
- Create a new Administrator account: This provides persistent, full access to the application.
- Modify existing accounts: Change the password of the current admin (Account Takeover).
- Exfiltrate Data: Once logged in as an admin, the attacker can export the entire customer database.
- RCE Potential: In many PHP applications, administrative access leads to Remote Code Execution via file upload settings or template editing. If Easy!Appointments allows admins to upload avatars or modify email templates without strict sanitization, the server is compromised completely.
The Fix: Trust No Method
The fix was applied in version 1.5.3. The patch likely involves one of two strategies, or both combined:
- Strict Method Checking in Controllers: Ensuring that actions like
store,save, andupdateexplicitly rejectGETrequests. - Universal CSRF Checks: Modifying
csrf_verifyto run validation on all state-changing endpoints, regardless of the HTTP verb, or simply ensuring the framework forcesPOSTfor sensitive routes.
To patch this manually if you cannot upgrade (which you really should):
// Quick and Dirty Mitigation
public function csrf_verify() {
// Don't just return early.
// If it's a sensitive path, ENFORCE POST or CHECK TOKEN.
if ($_SERVER['REQUEST_METHOD'] === 'GET' && $this->is_sensitive_path()) {
die('CSRF Attempt blocked: GET method not allowed for this action.');
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
return;
}
// ...
}Recommendation: Update to version 1.5.3 immediately. This version correctly handles request methods and token validation, closing the window attacker's were climbing through.
Fix Analysis (1)
Technical Appendix
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:PAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
Easy!Appointments alextselegidis | <= 1.5.2 | 1.5.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-352 (CSRF) |
| CVSS v4.0 | 7.4 (High) |
| Attack Vector | Network |
| User Interaction | Required (Passive) |
| Impact | Admin Account Creation / Takeover |
| Patch Status | Fixed in 1.5.3 |
MITRE ATT&CK Mapping
The 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.
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.