TrueBooker WordPress Arbitrary Password Reset: CVE-2026-14364
By WP Vanguard Team
On August 6, 2026, two critical CVEs landed against the same WordPress plugin on the same day. Both hit TrueBooker (Appointment Booking and Scheduler System), both scored CVSS 9.8, and both are a WordPress arbitrary password reset vulnerability that lets an unauthenticated attacker reset any user's password, including an administrator's. CVE-2026-14364 does it through a parameter called tbab-userid. CVE-2026-14365 does it through a parameter called truebooker_wp_user_id. Same plugin, same day, same bug, two different names for the user ID field that triggers it.
That pairing is the interesting part. It's not two unrelated flaws that happened to ship together. It's one root cause, missing authorization on a password reset action, reached through two separate code paths that never talked to each other. Both affect every TrueBooker version up to and including 1.2.3. Both are fixed in 1.2.4.
This post isn't really about TrueBooker. It's about what happens when a team fixes the symptom a bug report describes instead of the function underneath it, and what the fix should have looked like instead.
How the arbitrary password reset works
Both CVEs boil down to the same sentence: the plugin resets a user's password based on a user ID pulled from the request, without confirming the requester has any right to change that account's credentials. No token check, no ownership check, no capability check. Just an ID and a write.
That's about as bare as an authorization flaw gets. An attacker doesn't need a valid session, a stolen cookie, or a phished credential. They send a request naming a target user ID, tbab-userid in one path and truebooker_wp_user_id in the other, and the plugin treats that number as sufficient proof of identity. Point it at user ID 1, which is very often the original administrator account, and the attacker walks away with a new password for the site owner.
Compare that to how WordPress core handles this. Core's own reset flow, built around check_password_reset_key(), never trusts a bare user ID. It generates a hashed, single-use activation key, emails it to the address on file, and requires that exact key to be presented and matched against the target account before any password write happens. The user ID is present in core's flow too, but it's inert on its own. It only becomes meaningful once it's paired with a key the attacker can't guess or forge.
TrueBooker's two vulnerable paths skipped that binding entirely. A user ID is an identifier. It tells the system which account you're talking about. It is never proof of who's asking, and treating it as both is the exact gap that turns a normal-looking request into an unauthenticated password reset.
The contrast is small in code terms but total in effect:
// Vulnerable: the request supplies identity and gets a write.
$user_id = intval($_REQUEST['tbab-userid']);
wp_set_password($new_password, $user_id);
// Correct: identity has to be proven before any write happens.
$user_id = intval($_REQUEST['user_id']);
$key = sanitize_text_field($_REQUEST['reset_key']);
$check = check_password_reset_key($key, get_userdata($user_id)->user_login);
if (is_wp_error($check)) {
wp_die('Invalid or expired reset request.');
}
wp_set_password($new_password, $user_id);
The vulnerable version reads a request field and writes. The correct version verifies a hashed, single-use, user-bound token first, and only reaches the write if that check passes.
Who is affected
Both CVEs affect TrueBooker (Appointment Booking and Scheduler System) through version 1.2.3. Neither requires authentication, a specific role, or any non-default configuration to exploit. Version 1.2.4 fixes both. If you're running anything older than 1.2.4, treat the site as compromised until you've checked the indicators below, not just patched and moved on.
Because both flaws are unauthenticated and hit the same class of target, admin accounts, the practical exposure is identical for both CVEs. There's no meaningful difference in severity between the tbab-userid path and the truebooker_wp_user_id path. An attacker only needed one to work.
Both CVSS 9.8 scores reflect the same reasoning: network-accessible, no privileges required, no user interaction, and full impact on confidentiality, integrity, and availability once an administrator account is taken over. A booking plugin doesn't sound like a high-value target next to something like a membership or e-commerce plugin, but the account it protects is the same WordPress administrator account either way, and from there an attacker can install a plugin, edit theme files, or create new admin users at will.
The same bug, twice: what a root-cause fix actually looks like
Here's the part worth sitting with. Two CVEs, filed the same day, for the same missing check, reached through two different parameter names in the same plugin. That's not a coincidence of timing. It's the signature of a specific kind of mistake: someone found and fixed one password-reset code path, and never grepped the codebase for the sibling that did the same thing under a different name.
It's easy to see how this happens. A bug report names a symptom: "attacker can reset a password using tbab-userid." A developer opens the file where that parameter is read, adds a check right there, ships it, closes the ticket. The fix is real and it works. But it fixes the call site, not the underlying operation. If there's a second function elsewhere that also writes a new password based on a request-supplied user ID, under a different parameter name, it's still wide open. Nobody looked for it because the ticket didn't mention it.
This is the difference between a symptom fix and a root-cause fix, and it's worth naming precisely because the two look identical in a diff. A symptom fix adds a guard where the bug was found. A root-cause fix asks what class of operation this is, finds every place that operation happens, and puts the guard where all of them are forced to pass through it.
For a password-reset feature, that means one function: something like verify_reset_authorization($user_id, $token), that does the token binding, the expiry check, and the single-use invalidation, and every code path that changes a password calls it first. No branch skips it. No admin-tools shortcut, no AJAX handler, no REST endpoint bypasses it by calling wp_set_password() directly. If a second parameter name shows up two months later doing the same job, it can't reintroduce the bug, because there's only one place the password write is allowed to happen.
The practical discipline is simple and doesn't require new tooling: before you mark any authorization bug fixed, grep the codebase for every caller of the function that does the sensitive write, in this case anything that calls wp_set_password() or equivalent, and confirm each one routes through the same authorization check you just added. If TrueBooker's maintainers had done that after finding the first path, the second CVE wouldn't have needed to exist as a separate finding on the same day.
We saw the same category of missing check, an unauthenticated password reset with no binding to the account it targets, in our writeup on the Kirki account takeover. The shape here is different, one flaw duplicated across two parameters instead of one flaw in one place, but the underlying habit, checking every caller before declaring a fix complete, is the same lesson. It's also cheaper than it sounds: a grep across the codebase for every call to the password-write function takes a few minutes, and it catches exactly the sibling that TrueBooker's fix missed.
We've written before about an authentication bypass that came from a different kind of missing check, where a nonce that should have proven identity didn't actually bind to anything. The pattern recurs because "check who's asking" is one of the easiest steps to skip when the rest of the logic works correctly. The fix isn't more code. It's fewer places where the check can be skipped.
What to do now
Update to TrueBooker 1.2.4 immediately. That's the fixed version for both CVE-2026-14364 and CVE-2026-14365, and there's no partial-mitigation configuration that closes one path while leaving the other open, since both were unauthenticated and required nothing from the attacker.
If your site ran any version up to 1.2.3, don't treat the update alone as closing the incident. Check for signs the flaw was already used against you:
- Review password-change timestamps for every administrator and editor account. WordPress doesn't surface this by default, but your hosting provider's audit log or a security plugin's activity log usually will.
- Look for password resets that don't correspond to a request from the account owner, particularly on accounts nobody remembers requesting a reset for.
- Check active sessions for privileged accounts and terminate anything you don't recognize. WordPress core lets an admin log out all sessions for a user from their profile screen.
- If you find any unexplained password change on an administrator account, assume the account was accessed and rotate every credential connected to it: the WordPress password, any application passwords, and any API keys stored in plugin settings that account could reach.
A silent takeover through this kind of flaw often leaves no error message and no failed-login trail, since the attacker's request succeeds on the first try. That's exactly why the password-change timestamp is the indicator worth checking even if nothing else looks wrong.
If you're unsure whether your site shows other signs of compromise beyond this specific flaw, our guide to spotting a hacked WordPress site covers the broader checklist, and our WordPress security checklist covers the baseline hardening steps worth having in place regardless of which plugin gets hit next. WP Vanguard's scanner flags exactly this class of missing-authorization issue during a site scan, so it's worth a pass if you want a second check beyond manual log review.
Forcing a password reset for every privileged account, not just the ones you think were touched, is the safe default here. The cost of an unnecessary reset is small. The cost of leaving an administrator account with a password an attacker silently set weeks ago is not.
References
Related reading
Check Your WordPress Site Security
Free scan, no login required. Find vulnerabilities before attackers do.
Scan Your Site FreeGet weekly WordPress security tips
Vulnerability alerts, plugin updates, and security guides. No spam. Unsubscribe any time.