
Proof-of-concept exploit for CVE-2026-13447, a critical authentication bypass in the WordPress MStore API plugin via forged Firebase JWT tokens, with a local lab reproduction.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · CVE-2026-13447
MStore API 4.18.4 - inspireui
I am @abraxas_null. Loopback lab. The client is CVE-2026-13447-Abraxas-Labs.py.
The kid is not a signature. FirebasePhoneAuthHelper::verify_id_token checks alg == RS256, that kid is in Google's x509 list, that aud/iss match the uploaded Firebase project_id. Then it returns phone_number. It never calls openssl_verify. HTTP is POST /wp-json/api/flutter_user/firebase_sms_v2 with JSON id_token. NVD lists through 4.20.0. No 4.20.0 zip in the lab; 4.18.4 is the tree that ran. Patched in 4.21.1.
| CVE | CVE-2026-13447 · CVE.org |
| CWE | CWE-287 |
| CVSS | Critical: 9.8 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Product | WordPress - MStore API |
| Affected | all versions through 4.20.0 (lab 4.18.4; no 4.20.0 zip) |
| Patched | 4.21.1 and later |
| Auth | none |
| License | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 only |
Mint an RS256 JWT with a live Google kid, matching aud/iss, and a phone_number bound to an existing user. POST it. You get wp_user_id, cookie, displayname. That is admin if that phone is on admin. I am not printing the token.
Wordfence named the missing openssl_verify. I read the helper, then the REST route, then bound a lab phone to admin.
GET Google's x509. Pick a kid. Build RS256. POST {id_token}. Witness POCWitness13447 as displayname, plus a cookie.
Wrong turns: action=verify_id_token (theme or REST 404); GET; alg not RS256; random kid; aud/iss not matching project_id (poc13447 in the lab file); no user with that registered_phone_number (User does not exist); Firebase private key file is not found (missing config, not a signature).
Port 8088. MStore API 4.18.4. Uploaded Firebase JSON project_id=poc13447. Admin phone meta bound.
Target only 127.0.0.1:8088 (or the loopback you bound).
cd lab
docker compose up --force-recreate
python3 ../CVE-2026-13447-Abraxas-Labs.py
Witness: Small JSON with wp_user_id, cookie, displayname POCWitness13447. id_token is invalid / homepage is not it.
Ways to lose without learning anything:
User does not existUpdate MStore API to 4.21.1 or newer. Re-run CVE-2026-13447-Abraxas-Labs.py against the patched build: the forged JWT must not log anyone in.
plugins.trac.wordpress.org/browser/mstore-api/tags/4.18.4/controllers/flutter-user.php#L829
plugins.trac.wordpress.org/browser/mstore-api/tags/4.18.4/controllers/flutter-user.php#L940
plugins.trac.wordpress.org/browser/mstore-api/trunk/controllers/flutter-user.php#L829
plugins.trac.wordpress.org/browser/mstore-api/trunk/controllers/flutter-user.php#L940
www.wordfence.com/threat-intel/vulnerabilities/id/4a1127af-74f6-4748-9aee-5a8c6c2766a4?source=cve
Plugin directory: mstore-api
Trac browser: plugins.trac.wordpress.org/mstore-api
SVN tags: plugins.svn.wordpress.org/mstore-api
Abraxas Labs: abraxaslabs.tech · github.com/abraxas · @abraxas_null
GNU Affero GPL v3.0. See LICENSE.
The client talks to loopback. Using it against systems you do not own is not authorized by Abraxas Labs. No warranty.