GHSA-hg7g-56h5-5pqr: CAPTCHA Bypass in WWBN/AVideo via Attacker-Controlled Length Parameter and Missing Token Invalidation on Failure
Summary`objects/getCaptcha.php` accepts the CAPTCHA length (`ql`) directly from the query string with no clamping or sanitization, letting any unauthenticated client force the server to generate a 1-character CAPTCHA word. Combined with a case-insensitive `strcasecmp` comparison over a ~33-character alphabet and the fact that failed validations do NOT consume the stored session token, an attacker can trivially brute-force the CAPTCHA on any endpoint that relies on `Captcha::validation()` (user registration, password recovery, contact form, etc.) in at most ~33 requests per session.DetailsThree cooperating flaws in `objects/getCaptcha.php` and `objects/captcha.php` reduce CAPTCHA protection to a deterministic bypass.1. External control of CAPTCHA strength (`objects/getCaptcha.php:7`)There is no minimum, no type-check, and no clamping. Requesting `/objects/getCaptcha.php?ql=1` causes the server to generate a single-character word and save it to the attacker's own PHP session.2. Small alphabet stored in the session (`objects/captcha.php:33-39`)After case-folding the alphabet is 25 letters (A–Z minus `O`) plus digits `2-9`, i.e. 33 unique values. For an unauthenticated attacker the admin branch at line 35 is unreachable, so the value is purely random over that 33-symbol set.3. Weak comparison and token NOT invalidated on failure (`objects/captcha.php:58-75`)Two problems here:`strcasecmp` is case-insensitive, collapsing the alphabet to ~33 distinct values.`unset($_SESSION["palavra"])` only runs in the **success** branch. Every failed guess leaves the stored word intact, so the same session can be retried against the same stored answer until it matches.Reachability`Captcha::validation()` is invoked from unauthenticated entry points including:`objects/userCreate.json.php:38` — user registration (`Captcha::validation($_POST['captcha'])`)`objects/userRecoverPass.php:31` — password recovery`objects/sendEmail.json.php:10` — public contact email`plugin/API/API.php:4243` and `:5684` — public API endpoints`plugin/CustomizeUser/donate.json.php:62`, `confirmDeleteUser.json.php:15``plugin/YPTWallet/view/transferFunds.json.php:25`None of these require authentication for the CAPTCHA check to matter — they rely on it exactly because they're exposed to anonymous or lightly-authenticated callers.PoCAttacker flow against an unauthenticated signup/recovery endpoint:Step 1 — Weaken the CAPTCHA to one character and install it in the attacker's own PHP session:Step 2 — Brute-force the single-character answer. Because failed attempts do NOT reset `$_SESSION["palavra"]`, the same cookie jar is reused and the same stored value is checked against each guess:Worst case: 33 POSTs per session to pass the CAPTCHA once.With `ql=2` the keyspace is ~1089 — still trivial and more robust against any edge cases involving `empty()` on a single-digit word.The same technique works against `userCreate.json.php`, `sendEmail.json.php`, and every other `Captcha::validation()` caller.Observed behavior on the local instance: each wrong guess returns `"Your code is not valid"` without rotating `$_SESSION["palavra"]`; the logged `session is (<char>)` message in `_error_log` stays the same across all failed attempts in a session, confirming the token is not rotated.ImpactCAPTCHA is the only "are you human" control on several anonymous endpoints. Reducing it to a deterministic ≤33-try bypass enables:**Automated account creation / spam signups** via `userCreate.json.php`.**User enumeration / password-reset spamming** via `userRecoverPass.php`.**Unsolicited email abuse** via `sendEmail.json.php`.**Comment / donation / wallet abuse** on plugin endpoints that rely on `Captcha::validation`.It does not by itself leak secrets or grant privileges, hence Integrity:Low (abuse of an intended rate-limiting/anti-bot control) with no direct Confidentiality/Availability impact.Recommended FixThree coordinated changes in `objects/getCaptcha.php` and `objects/captcha.php`:Clamp `ql` (and ideally the other image params) to a safe server-side range:Always consume the stored CAPTCHA answer on any validation attempt (success or failure) so each guess costs one fresh `getCaptcha.php` round-trip:Use a CSPRNG for word generation instead of `str_shuffle`, e.g.:Optionally also add an application-level rate limit (per IP / per session) on all endpoints that call `Captcha::validation()` as defense in depth.
CVSS v3.1: 5.3