Skip to content

CVE-2026-11387: Verified Repro With Script Download

CVE-2026-11387: SMS Alert WordPress plugin <= 3.9.5 allows unauthenticated attackers to change a user’s email and reset their password, leading to account takeover and privilege escalation when OTP password reset verification is enabled.

CVE-2026-11387 is verified against sms-alert · WordPress plugin (hosted on WordPress.org SVN, not GitHub). Affected versions: <= 3.9.5. Fixed in 3.9.6. Vulnerability class: Privilege Escalation. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00242.

REPRO-2026-00242 sms-alert · WordPress plugin (hosted on WordPress.org SVN, not GitHub) Privilege Escalation Variant found Jul 6, 2026 CVE entry ↗ .txt
Severity
CRITICAL
CVSS
9.8
Confidence
HIGH
Reproduced in
22m 56s
Tool calls
321
Spend
$7.05
01 · Overview

What Is CVE-2026-11387?

CVE-2026-11387 is a critical improper-authentication vulnerability (CWE-287, CVSS 9.8) in the SMS Alert WordPress plugin that lets an unauthenticated attacker change a user's email and reset their password, leading to full account takeover including administrators. Pruva reproduced it (reproduction REPRO-2026-00242).

02 · Severity & CVSS

CVE-2026-11387 Severity & CVSS Score

CVE-2026-11387 is rated critical severity, with a CVSS base score of 9.8 out of 10.

CRITICAL threat level
9.8 / 10 CVSS base
Weakness CWE-287 (Improper Authentication) — Improper Authentication

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

03 · Affected Versions

Affected sms-alert Versions

sms-alert · WordPress plugin (hosted on WordPress.org SVN, not GitHub) versions <= 3.9.5 are affected.

How to Reproduce CVE-2026-11387

$ pruva-verify REPRO-2026-00242
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00242/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-11387

Privilege escalation — reproduced
  • reached the target end-to-end
  • full exploit chain demonstrated
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

Unauthenticated POST to wp-login.php?action=lostpassword with user_login=<admin_phone>&wc_reset_password=true (sets session), then POST with option=smsalert-change-password-form&smsalert_user_newpwd=<attacker_pwd>&smsalert_user_cnfpwd=<attacker_pwd> (resets password without OTP)

Attack chain
  1. wp-login.php?action=lostpassword
  2. lostpassword_post hook
  3. WPResetPassword::startSmsalertResetPasswordProcess()
  4. smsalert_site_challenge_otp() sets $_SESSION['user_login']
  5. POST ?option=smsalert-change-password-form
  6. WPResetPassword::routeData()
  7. handleSmsalertChangedPwd()
  8. reset_password($admin, $attacker_pwd)
Variants tested

UltimateMember integration (class-ultimatemember.php, option=smsalert-um-reset-pwd-action reached via um_reset_password_process_hook) is a materially different unauthenticated entry point that reaches the same handleSmsalertChangedPwd()->reset_password() sink without OTP validation. Runtime-reproduced on vulnerable 3.…

How the agent worked 749 events · 321 tool calls · 23 min
23 minDuration
321Tool calls
191Reasoning steps
749Events
1Dead-ends
Agent activity over 23 min
Support
25
Hypothesis
2
Repro
285
Judge
26
Variant
407
0:0022:56

Root Cause and Exploit Chain for CVE-2026-11387

Versions: ≤ 3.9.5

The SMS Alert – SMS & OTP for WooCommerce WordPress plugin (versions ≤ 3.9.5) contains an unauthenticated privilege escalation vulnerability in its OTP-based password reset handler. The WPResetPassword::routeData() method in handler/forms/class-wpresetpassword.php calls handleSmsalertChangedPwd() whenever the request parameter option equals smsalert-change-password-form, without verifying that the OTP challenge was actually completed/validated. Because smsalert_site_challenge_otp() sets $_SESSION['user_login'] to the target user's login at OTP initiation time (before the OTP is sent), an attacker who triggers the OTP challenge for an administrator's phone number can immediately submit the password-change form and reset that administrator's password — bypassing OTP verification entirely. This leads to full account takeover and privilege escalation.

  • Package: SMS Alert – SMS & OTP for WooCommerce, Order Notifications & Abandoned Cart Recovery (slug: sms-alert)
  • Affected versions: ≤ 3.9.5
  • Patched version: 3.9.6
  • Risk level: Critical (CVSS 9.8)
  • Consequences: An unauthenticated remote attacker can reset any WordPress user's password (including administrators) when the SMS Alert plugin has OTP password-reset verification enabled and the target user has a billing_phone set. The attacker gains full administrative access to the WordPress site. The same class of vulnerability exists in the UltimateMember integration (class-ultimatemember.php, option smsalert-um-reset-pwd-action).

Impact Parity

  • Disclosed/claimed maximum impact: Unauthenticated privilege escalation via arbitrary password reset — attacker changes admin email/password and takes over the account.
  • Reproduced impact from this run: Unauthenticated attacker resets the administrator's password directly (without needing email change) by exploiting the missing OTP validation check. The attacker can then log in as administrator with the new password.
  • Parity: full — the core claimed impact (unauthenticated admin account takeover via arbitrary password reset) was demonstrated end-to-end against a running WordPress + WooCommerce + SMS Alert service.
  • Not demonstrated: The advisory describes an email-change → standard-password-reset path as one exploitation variant. Our reproduction demonstrates a more direct path: the password itself is reset via reset_password() without any email change or OTP validation, which is the same root cause (missing identity/OTP-validation check in routeData()).

Root Cause

Vulnerable code (class-wpresetpassword.php v3.9.5)
public function routeData()
{
    if (! empty($_REQUEST['option']) && sanitize_text_field(wp_unslash($_REQUEST['option'])) === 'smsalert-change-password-form' ) {
        $this->handleSmsalertChangedPwd($_POST);
    }
}

routeData() is called on every init hook (via FormInterface::__construct()add_action('init', ...)) and is therefore reachable by unauthenticated users. When option=smsalert-change-password-form is present, handleSmsalertChangedPwd() is invoked, which reads $_SESSION['user_login'] and calls WordPress's reset_password($user, $new_password) directly.

How $_SESSION['user_login'] gets set

The session variable is set by smsalert_site_challenge_otp() in handler/smsalert_form_handler.php:

function smsalert_site_challenge_otp(...) {
    SmsAlertUtility::checkSession();
    $_SESSION['user_login']      = $user_login;   // <-- set at INITIATION, not validation
    $_SESSION['phone_number_mo'] = $phone_number;
    _handle_otp_action(...);                       // <-- sends OTP; may fail
}

This function is called from WPResetPassword::startSmsalertResetPasswordProcess() (hooked to WordPress's lostpassword_post action) when a user submits the lost-password form with a phone number and wc_reset_password=true. The session variables are set before the OTP is sent via the SMS Alert API. Even if the OTP API call fails (invalid credentials, network error), the session already contains the target user's login.

Missing validation check

In the vulnerable version, handle_post_verification() (called after successful OTP validation) does not set $_SESSION[$this->form_session_var] to 'validated'. And routeData() does not check for this value. Therefore, the password reset proceeds regardless of whether the OTP was ever validated.

Fix (v3.9.6, changeset 3587983)

The patch adds two changes to class-wpresetpassword.php:

  1. routeData() now requires OTP validation:
public function routeData()
{
    SmsAlertUtility::checkSession();
    if (! empty($_REQUEST['option']) && (sanitize_text_field(wp_unslash($_REQUEST['option'])) === 'smsalert-change-password-form') && isset($_SESSION[ $this->form_session_var ]) && strcasecmp($_SESSION[ $this->form_session_var ], 'validated') === 0 ) {
        $this->handleSmsalertChangedPwd($_POST);
    }
}
  1. handle_post_verification() now marks the session as validated:
$_SESSION[ $this->form_session_var ] = 'validated';

The same fix pattern was applied to class-ultimatemember.php (option smsalert-um-reset-pwd-action, session var form_session_var2).

Fix reference

Reproduction Steps

  1. Script: bundle/repro/reproduction_steps.sh
  2. What the script does:
    • Installs PHP, MariaDB, and wp-cli
    • Downloads and installs WordPress 7.0 + WooCommerce 10.9.3
    • Installs SMS Alert plugin 3.9.5 (vulnerable) and configures it: sets non-empty gateway credentials (so is_user_authorised() returns true), enables reset_password=on, and sets the admin user's billing_phone to 919999990001
    • Starts a PHP built-in web server on localhost:8080
    • Phase A (vulnerable): Sends an unauthenticated POST to wp-login.php?action=lostpassword with user_login=919999990001&wc_reset_password=true — this triggers lostpassword_poststartSmsalertResetPasswordProcess()smsalert_site_challenge_otp() which sets $_SESSION['user_login']='admin'. The OTP API call fails ("Wrong SMSAlert credentials") but the session is already poisoned. Then sends a second unauthenticated POST with option=smsalert-change-password-form&smsalert_user_newpwd=Pwned!Pass42&smsalert_user_cnfpwd=Pwned!Pass42 — this calls handleSmsalertChangedPwd() which reads the session and calls reset_password($admin, 'Pwned!Pass42'). The server returns HTTP 302 redirect to ?password-reset=true.
    • Verifies via wp user check-password that the new password works and the original does not
    • Phase B (patched negative control): Repeats the exact same attack against SMS Alert 3.9.6 — the password change is blocked (HTTP 200, no redirect), and the original password still works
  3. Expected evidence:
    • Vulnerable 3.9.5: HTTP 302 with Location: ?page_id=8&password-reset=true; wp user check-password admin 'Pwned!Pass42' succeeds; original password fails
    • Patched 3.9.6: HTTP 200 (no redirect); wp user check-password admin 'HackedPass99' fails; original password still works

Evidence

Log file locations
  • bundle/logs/reproduction_steps.log — full script output
  • bundle/logs/exploit_vuln_step1_response.html — OTP challenge response (vulnerable)
  • bundle/logs/exploit_vuln_step2_headers.txt — password change response headers showing 302 redirect
  • bundle/logs/exploit_vuln_cookies.txt — session cookies used in the exploit
  • bundle/logs/exploit_fixed_step2_headers.txt — patched version response (200, no redirect)
  • bundle/repro/runtime_manifest.json — structured runtime evidence
Key excerpts

Vulnerable 3.9.5 — Step 2 response (password reset confirmed):

HTTP/1.1 302 Found
X-Redirect-By: WordPress
Location: http://localhost:8080/?page_id=8&password-reset=true

Vulnerable 3.9.5 — Password verification:

New password 'Pwned!Pass42'       → OK   (expected: OK)
Old password 'admin_original_123' → FAIL (expected: FAIL)

Patched 3.9.6 — Step 2 response (attack blocked):

HTTP/1.1 200 OK
(no redirect — attack blocked)

Patched 3.9.6 — Password verification:

Hacked password 'HackedPass99'      → FAIL (expected: FAIL)
Original password 'admin_original_123' → OK   (expected: OK)
Environment
  • PHP 8.5.4 (built-in server)
  • MariaDB 11.8.6
  • WordPress 7.0
  • WooCommerce 10.9.3
  • SMS Alert 3.9.5 (vulnerable) / 3.9.6 (patched)

Recommendations / Next Steps

  1. Upgrade immediately to SMS Alert plugin version 3.9.6 or later.
  2. Audit all routeData() handlers across the plugin's form classes for similar missing OTP-validation checks. The same pattern was found and fixed in class-ultimatemember.php.
  3. Defense in depth: Consider adding nonce verification and capability checks to all password-modification endpoints, not just session-state checks.
  4. Testing: Add integration tests that verify password reset requires a validated OTP session before allowing handleSmsalertChangedPwd() to execute.

Additional Notes

  • Idempotency: The script was run twice consecutively; both runs produced identical results (vulnerable exploit succeeded, patched control blocked). The script is designed to be idempotent — it reuses existing WordPress installs and reconfigures the plugin on each run.
  • Preconditions: The vulnerability requires (a) OTP password reset enabled (smsalert_general[reset_password]=on), (b) the plugin authorized (smsalert_gateway credentials non-empty), (c) WooCommerce active, and (d) the target user having a billing_phone set. These match the advisory's stated preconditions.
  • No real SMS credentials needed: The exploit works even when the SMS Alert API credentials are invalid, because smsalert_site_challenge_otp() sets the session variables before the OTP API call, and handleSmsalertChangedPwd() does not check OTP validation status.
  • Session cookie handling: The exploit uses PHP's native session mechanism (PHPSESSID cookie). Both HTTP requests (OTP challenge + password change) must share the same PHPSESSID cookie.

Variant Analysis & Alternative Triggers for CVE-2026-11387

Versions: ≤ 3.9.5 (alternate trigger); fixed in 3.9.6

This variant run identified and runtime-confirmed a materially different entry point for the same root cause as CVE-2026-11387: the UltimateMember integration in the SMS Alert plugin (handler/forms/class-ultimatemember.php, request option smsalert-um-reset-pwd-action, reached via UltimateMember's um_reset_password_process_hook action). An unauthenticated attacker can reset an administrator's password without completing OTP verification by (1) submitting UltimateMember's password-reset form for the admin to poison $_SESSION['user_login'] at OTP initiation time, then (2) POSTing option=smsalert-um-reset-pwd-action to call handleSmsalertChangedPwd()reset_password(). The 3.9.5 vulnerable code dispatches with no OTP-validation check. This is an alternate trigger, NOT a bypass: the 3.9.6 fix adds the same $_SESSION['SA_UM_RESET_PWD'] === 'validated' gate to the UM handler, and the attack is blocked on 3.9.6. No bypass of the fix was found.

Fix Coverage / Assumptions

  • Invariant the fix relies on: the per-form session variable equals the literal 'validated' only after a real OTP validation (otp_verification_successfulhandle_post_verification()), and handleSmsalertChangedPwd() is gated on it.
  • Code paths explicitly covered: WPResetPassword::routeData() (option smsalert-change-password-form, session var wp_default_lost_pwd) and UltimateMember::handleForm() (option smsalert-um-reset-pwd-action, session var SA_UM_RESET_PWD). Both were patched in 3.9.6 (changeset 3587983).
  • What the fix does NOT cover: nothing for this vulnerability class. Only two handlers call reset_password(); both are gated. There is no separate email-change sink, and the two handlers use distinct session keys so no cross-handler confusion is possible. (See patch_analysis.md for the full exhaustive handler scan.)

Variant / Alternate Trigger

Entry point (materially different from the repro's wp-login/WooCommerce path):

  1. Session poisoning — Unauthenticated POST to the UltimateMember password-reset core page (e.g. /?page_id=<um_pwd_page>) with body _um_password_reset=1&username_b=admin. UltimateMember fires um_reset_password_process_hook → SMS Alert's UltimateMember::smsalertUmResetPwdSubmitted() (registered in handleForm() when reset_password=on). That calls SmsAlertUtility::initialize_transaction() (sets $_SESSION['SA_UM_RESET_PWD']=true) and startOtpTransaction()smsalert_site_challenge_otp(), which sets $_SESSION['user_login']='admin' before the OTP API call. The OTP API call fails ("Wrong SMSAlert credentials") but the session is already poisoned.
  2. Password reset — Unauthenticated POST to / with body option=smsalert-um-reset-pwd-action&smsalert_user_newpwd=<pwd>&smsalert_user_cnfpwd=<pwd>. On 3.9.5, UltimateMember::handleForm() matches the option and calls handleSmsalertChangedPwd($_POST), which reads $_SESSION['user_login'] and invokes WordPress's reset_password($admin, $pwd) with no OTP-validation check, then redirects to ?sa_um_reset_pwd=1.

Code path: POST ?page_id=<um_pwd> (_um_password_reset=1&username_b=admin)um_reset_password_process_hookUltimateMember::smsalertUmResetPwdSubmitted()initialize_transaction('SA_UM_RESET_PWD') + startOtpTransaction()smsalert_site_challenge_otp() sets $_SESSION['user_login']POST ?option=smsalert-um-reset-pwd-actionUltimateMember::handleForm()handleSmsalertChangedPwd()reset_password($admin,$pwd).

Files/functions: handler/forms/class-ultimatemember.php (handleForm() dispatch ~line 88 in 3.9.5; smsalertUmResetPwdSubmitted() ~line 337; handleSmsalertChangedPwd() ~line 270; handle_post_verification() ~line 486), handler/smsalert_form_handler.php (smsalert_site_challenge_otp() ~line 88, _handle_success_validated()otp_verification_successful).

Preconditions (all match the plugin's own enablement logic): smsalert_gateway credentials non-empty; smsalert_general[reset_password]=on; smsalert_general[buyer_signup_otp]=on (required for UltimateMember::isFormEnabled()); target user has billing_phone; UltimateMember active with a password-reset core page.

  • Package: SMS Alert – SMS & OTP for WooCommerce (slug sms-alert), UltimateMember integration
  • Affected versions: ≤ 3.9.5 (alternate trigger); fixed in 3.9.6
  • Risk level: Critical (CVSS 9.8) — unauthenticated admin account takeover
  • Consequences: Unauthenticated remote attacker resets any user's password (including administrators) when the UM-based OTP reset flow is enabled, gaining full administrative access. Identical impact class to the disclosed CVE.

Impact Parity

  • Disclosed/claimed maximum impact: Unauthenticated privilege escalation via arbitrary password reset / account takeover.
  • Reproduced impact from this variant run: Unauthenticated attacker reset the administrator's password directly via the UM entry point without OTP, then (verified via wp user check-password) the new password works and the original no longer does — full admin takeover on 3.9.5.
  • Parity: full — same impact class and end-state as the parent CVE, via a different entry point.
  • Not demonstrated: The advisory's email-change variant was not exercised (no separate email-change sink exists in the code; see patch_analysis.md).

Root Cause

The same underlying bug as CVE-2026-11387: smsalert_site_challenge_otp() writes $_SESSION['user_login'] (and the per-form transaction var) at OTP initiation time, before the OTP is sent/validated, while the password-change dispatch in UltimateMember::handleForm() consumes that session state to call reset_password() without requiring proof that the OTP was ever validated. The 3.9.6 fix adds the $_SESSION['SA_UM_RESET_PWD'] === 'validated' gate (set only in handle_post_verification() after real OTP success) to the UM dispatch, closing the alternate trigger.

Reproduction Steps

  1. Script: bundle/vuln_variant/reproduction_steps.sh
  2. What the script does:
    • Assumes the repro environment (MariaDB + PHP + wp-cli + WordPress + plugin zips at bundle/artifacts/) is present.
    • Installs/activates WooCommerce + UltimateMember; creates the UM password-reset core page and disables the UM reset-attempt limit.
    • Phase A (vulnerable 3.9.5): configures SMS Alert 3.9.5 (reset_password=on, buyer_signup_otp=on, admin billing_phone=919999990001), starts the PHP server, runs the two-step UM attack, verifies via wp-cli that the admin password was changed to UmPwned!Pass42.
    • Phase B (patched 3.9.6): repeats the identical attack against 3.9.6 and verifies the password was not changed (original still works).
    • Writes runtime_manifest.json and validation_verdict.json.
  3. Expected evidence:
    • 3.9.5: Step 2 returns HTTP/1.1 302 with Location: …?page_id=11&sa_um_reset_pwd=1; wp user check-password admin 'UmPwned!Pass42' → OK; original → FAIL.
    • 3.9.6: Step 2 returns HTTP/1.1 200 (no redirect); wp user check-password admin 'UmHacked!Pass99' → FAIL; original → OK.

Evidence

  • bundle/logs/vuln_variant_reproduction.log — full script output (both runs)
  • bundle/logs/vv_vuln_step1_headers.txt — 3.9.5 step 1 response: Set-Cookie: PHPSESSID=… (session poisoned)
  • bundle/logs/vv_vuln_step2_headers.txt — 3.9.5 step 2 response: HTTP/1.1 302 Found / Location: http://localhost:8072/?page_id=11&#038;sa_um_reset_pwd=1
  • bundle/logs/vv_fixed_step2_headers.txt — 3.9.6 step 2 response: HTTP/1.1 200 OK (no redirect → attack blocked)
  • bundle/logs/vv_vuln_cookies.txt, bundle/logs/vv_fixed_cookies.txt — session cookies (PHPSESSID present in both; only 3.9.5 consumes the poisoned session)
  • bundle/vuln_variant/runtime_manifest.json, bundle/vuln_variant/validation_verdict.json

Key excerpts (3.9.5 — variant reproduced):

[*] vuln Step 2: submit smsalert-um-reset-pwd-action (no OTP)...
    HTTP HTTP/1.1 302 Found
    Location: http://localhost:8072/?page_id=11&#038;sa_um_reset_pwd=1
[*] post-exploit: new 'UmPwned!Pass42' -> OK (expected OK)
[*] post-exploit: old 'admin_original_123'     -> FAIL (expected FAIL)
[+] VULNERABLE 3.9.5: UM variant SUCCEEDED

Key excerpts (3.9.6 — attack blocked, NOT a bypass):

[*] fixed Step 2: submit smsalert-um-reset-pwd-action (no OTP)...
    HTTP HTTP/1.1 200 OK
[*] post-exploit: new 'UmHacked!Pass99' -> FAIL (expected FAIL)
[*] post-exploit: old 'admin_original_123'       -> OK (expected OK)
[+] PATCHED 3.9.6: UM attack BLOCKED (NOT a bypass)

Environment: PHP 8.5.4 (built-in server), MariaDB (alive), WordPress latest, WooCommerce, UltimateMember 2.12.0, SMS Alert 3.9.5 (vulnerable) / 3.9.6 (patched). Server bound on 127.0.0.1:8072 (the repro's default 8080 was unavailable in this run; the WP site URL was reconfigured to 8072).

Recommendations / Next Steps

  1. No additional fix required for the UM path — 3.9.6 already gates it. Sites must simply upgrade to ≥ 3.9.6.
  2. Defense in depth (applies to both handlers): add a WordPress nonce check and a capability/identity re-check to the password-change dispatch, rather than relying solely on the session 'validated' marker. Also clear $_SESSION['user_login'] on OTP failure (currently the initiation-time value persists even when the OTP API errors).
  3. Audit remaining handlers for any future "dispatch → privileged action without validated gate" pattern; the current exhaustive scan found none beyond the two already fixed.

Additional Notes

  • Bypass vs alternate trigger: This is an alternate trigger (different entry point, same root cause, same sink). It reproduces only on the vulnerable 3.9.5; the 3.9.6 fix blocks it. It is not a bypass.
  • Idempotency: The script was run twice consecutively; both runs produced identical results (3.9.5 variant succeeded, 3.9.6 blocked) and exited 1.
  • Note on buyer_signup_otp: The UM handler's isFormEnabled() requires buyer_signup_otp=on (in addition to reset_password=on). Without it, handleForm() is never called and neither the hook nor the dispatch registers. The variant script sets both.
  • Negative-result breadth: A full scan of all 53 form handlers confirmed only class-wpresetpassword.php and class-ultimatemember.php reach reset_password(); no other unauthenticated account-takeover sink exists.

CVE-2026-11387 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:001:52
0:00
session startedaccounts/fireworks/routers/glm-5p2-fast · CVE-2026-11387 · REPRO-20
0:03
0:05
web search
0:06
web search
0:09
0:10
1:11
1:14
1:15
web search
1:16
web search
1:20
1:20
1:20
error

Unknown error

1:22
1:23
1:35
1:35
extract_facts
no facts extracted
1:36
1:36
1:36
supportrepro
1:48
1:50
1:50
1:50
1:51
1:51
1:51
1:51
1:51
1:52

Artifacts and Evidence for CVE-2026-11387

Scripts, logs, diffs, and output captured during the reproduction.

bundle/artifact_promotion_manifest.json13.4 KB
bundle/vuln_variant/source_identity.json1.9 KB
bundle/vuln_variant/root_cause_equivalence.json2.3 KB
bundle/repro/reproduction_steps.sh15.8 KB
bundle/repro/rca_report.md10.2 KB
bundle/repro/runtime_manifest.json1.1 KB
bundle/repro/validation_verdict.json1.2 KB
bundle/logs/exploit_vuln_step2_headers.txt0.4 KB
bundle/logs/exploit_fixed_step2_headers.txt0.3 KB
bundle/logs/reproduction_steps.log4.2 KB
bundle/logs/patch_diff_wpresetpassword.txt1.1 KB
bundle/logs/exploit_vuln_step1_response.html29.5 KB
bundle/logs/exploit_vuln_step2_response.html0.0 KB
bundle/logs/exploit_vuln_cookies.txt0.3 KB
bundle/logs/exploit_fixed_step1_response.html29.5 KB
bundle/logs/exploit_fixed_step2_response.html72.7 KB
bundle/logs/exploit_fixed_cookies.txt0.3 KB
bundle/logs/php-server.log0.6 KB
bundle/vuln_variant/reproduction_steps.sh16.3 KB
bundle/vuln_variant/rca_report.md10.7 KB
bundle/vuln_variant/patch_analysis.md7.4 KB
bundle/vuln_variant/variant_manifest.json4.3 KB
bundle/vuln_variant/validation_verdict.json1.2 KB
bundle/vuln_variant/runtime_manifest.json1.6 KB
bundle/logs/vv_vuln_step2_headers.txt0.4 KB
bundle/logs/vv_fixed_step2_headers.txt0.3 KB
bundle/logs/vuln_variant_reproduction.log2.2 KB
bundle/logs/vv_vuln_step1.html33.5 KB
bundle/logs/vv_vuln_step1_headers.txt0.7 KB
bundle/logs/vv_vuln_step2.html0.0 KB
bundle/logs/vv_vuln_cookies.txt0.2 KB
bundle/logs/vv_fixed_step1.html33.5 KB
bundle/logs/vv_fixed_step1_headers.txt0.7 KB
bundle/logs/vv_fixed_step2.html81.2 KB
bundle/logs/vv_fixed_cookies.txt0.2 KB
bundle/logs/vv-php-server.log4.2 KB
08 · How to Fix

How to Fix CVE-2026-11387

Upgrade sms-alert · WordPress plugin (hosted on WordPress.org SVN, not GitHub) to 3.9.6 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-11387 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-11387

How does the CVE-2026-11387 account takeover work?

An unauthenticated attacker triggers the SMS Alert OTP password-reset flow for an administrator account that has a billing_phone set, which populates $_SESSION['user_login'] with the admin's login. The attacker then submits the password-change form directly, bypassing OTP verification, and resets the administrator's password to gain full administrative access to the WordPress site.

Which SMS Alert plugin versions are affected by CVE-2026-11387, and where is it fixed?

SMS Alert - SMS & OTP for WooCommerce, Order Notifications & Abandoned Cart Recovery versions <= 3.9.5 are affected. It is fixed in 3.9.6.

How severe is CVE-2026-11387?

It is rated critical severity with a CVSS score of 9.8: an unauthenticated remote attacker can reset any WordPress user's password, including administrators, when OTP password-reset verification is enabled and the target has a billing phone set.

How can I reproduce CVE-2026-11387?

Download the verified script from this page and run it in an isolated environment against WordPress with SMS Alert <= 3.9.5 installed and OTP password-reset enabled. It triggers the OTP challenge for a target user without providing the OTP code, then submits the password-change form directly and confirms the password was reset on the vulnerable build.
11 · References

References for CVE-2026-11387

Authoritative sources for CVE-2026-11387 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.