[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fwsSXH7Foe1ut-ty3mVgx2ZNc9FRXBlc8lZ1F1Qd617o":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":30,"research_verified":31,"research_rounds_completed":32,"research_plan":33,"research_summary":34,"research_vulnerable_code":35,"research_fix_diff":36,"research_exploit_outline":37,"research_model_used":38,"research_started_at":39,"research_completed_at":40,"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":31,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":31,"source_links":41},"CVE-2026-32448","podlove-podcast-publisher-authenticated-contributor-stored-cross-site-scripting-2","Podlove Podcast Publisher \u003C= 4.3.3 - Authenticated (Contributor+) Stored Cross-Site Scripting","The Podlove Podcast Publisher plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 4.3.3 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with contributor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.","podlove-podcasting-plugin-for-wordpress",null,"\u003C=4.3.3","4.3.4","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-03-08 00:00:00","2026-04-15 21:21:48",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F069efaf5-8e8b-46f2-93ac-e3648b06855d?source=api-prod",39,[22,23,24,25,26,27,28,29],"includes\u002Fsetup.php","lib\u002Fmodel\u002Fdownload_intent_clean.php","lib\u002Fmodel\u002Fmedia_file.php","lib\u002Fmodel\u002Fpodcast.php","lib\u002Fmodules\u002Fbase.php","lib\u002Fmodules\u002Fplus\u002Fapi.php","lib\u002Fmodules\u002Fplus\u002Ffeed_proxy.php","lib\u002Fmodules\u002Fplus\u002Ffeed_pusher.php","researched",false,3,"This research plan targets **CVE-2026-32448**, a stored Cross-Site Scripting (XSS) vulnerability in the Podlove Podcast Publisher plugin. The vulnerability allows authenticated users with Contributor-level permissions or higher to inject malicious scripts into podcast episode metadata, which is subsequently rendered unsanitized on the frontend.\n\n### 1. Vulnerability Summary\nThe Podlove Podcast Publisher plugin manages podcast episodes as a Custom Post Type (`podcast`). When saving an episode, the plugin stores metadata (such as subtitles, summaries, and episode numbers) in a custom database table (`wp_podlove_episode`). Versions up to 4.3.3 fail to properly sanitize these inputs upon saving and fail to escape them upon output in frontend templates or shortcodes. This allows a Contributor to inject a payload into an episode field that executes in the context of any user (including Administrators) viewing that episode.\n\n### 2. Attack Vector Analysis\n*   **Vulnerable Endpoint**: `wp-admin\u002Fpost.php` (via the `editpost` action).\n*   **Vulnerable Parameter**: `podlove_episode[subtitle]` (and potentially `podlove_episode[summary]`).\n*   **Authentication Required**: Contributor+ (any user capable of creating\u002Fediting a `podcast` post type).\n*   **Preconditions**: The plugin must be active, which registers the `podcast` post type and the associated meta boxes.\n\n### 3. Code Flow\n1.  **Entry Point**: A Contributor submits a POST request to `wp-admin\u002Fpost.php` to save or update an episode.\n2.  **Processing**: The plugin hooks into `save_post` (registered in `lib\u002Fepisode.php`, though the file is partially inferred, the behavior is standard for the `Model\\Episode` logic seen in `includes\u002Fsetup.php`).\n3.  **Storage**: The `Model\\Episode` class (referenced in `includes\u002Fsetup.php`'s `podlove_setup_database_tables()`) extracts data from `$_POST['podlove_episode']` and saves it to the `wp_podlove_episode` table using a `save()` method similar to the one seen in `lib\u002Fmodel\u002Fpodcast.php`.\n4.  **Sink**: When a user views the episode on the frontend, the default template (defined in `podlove_setup_default_template` in `includes\u002Fsetup.php`) or the `[podlove-episode-subtitle]` shortcode retrieves the data.\n5.  **Rendering**: The template engine (Twig-based) or the shortcode handler outputs the `subtitle` field directly without calling `esc_html()` or applying appropriate Twig escaping filters.\n\n### 4. Nonce Acquisition Strategy\nTo save post metadata, the agent must obtain the standard WordPress post nonce and the Podlove-specific security nonce used for episode metadata.\n\n1.  **Identify Post ID**: Create a draft episode or edit an existing one to get a valid `post_ID`.\n2.  **Navigate**: Use `browser_navigate` to `wp-admin\u002Fpost-new.php?post_type=podcast`.\n3.  **Extract Nonces**:\n    *   **WordPress Post Nonce**: Located in the `#_wpnonce` hidden input.\n    *   **Podlove Nonce**: Podlove typically uses a nonce field within its meta box. Look for an input named `_podlove_nonce` or similar.\n    *   **JS Strategy**: \n        ```javascript\n        \u002F\u002F Execute in browser_eval\n        const wp_nonce = document.querySelector('#_wpnonce')?.value;\n        const podlove_nonce = document.querySelector('input[name*=\"podlove_nonce\"]')?.value;\n        const post_id = document.querySelector('#post_ID')?.value;\n        return { wp_nonce, podlove_nonce, post_id };\n        ```\n\n### 5. Exploitation Strategy\nThe goal is to inject a payload into the `subtitle` field of a podcast episode.\n\n**Step-by-Step Plan:**\n1.  **Setup**: Ensure a Contributor user exists.\n2.  **Capture Context**: Log in as Contributor and navigate to the \"Add New Episode\" page.\n3.  **Submit Payload**: Send a POST request to `wp-admin\u002Fpost.php` mimicking the form submission.\n    *   **URL**: `https:\u002F\u002F\u003Ctarget>\u002Fwp-admin\u002Fpost.php`\n    *   **Headers**: `Content-Type: application\u002Fx-www-form-urlencoded`\n    *   **Body Parameters**:\n        *   `action`: `editpost`\n        *   `post_ID`: `\u003Cpost_id_from_step_4>`\n        *   `_wpnonce`: `\u003Cwp_nonce_from_step_4>`\n        *   `post_type`: `podcast`\n        *   `post_title`: `Exploit Episode`\n        *   `podlove_episode[subtitle]`: `\">\u003Cscript>alert(document.domain)\u003C\u002Fscript>`\n        *   `podlove_episode[summary]`: `Malicious Summary`\n        *   (Include other required Podlove fields if necessary, e.g., `podlove_episode[number]`)\n4.  **Trigger**: Navigate to the permalink of the newly created episode (e.g., `\u002F?podcast=exploit-episode`).\n\n### 6. Test Data Setup\n1.  **Plugin Setup**: Ensure Podlove is installed and the setup wizard is completed (defaulting settings is fine).\n2.  **User**: Create a user with the `contributor` role.\n3.  **Enable Modules**: Ensure the `podlove_web_player` is active (it is a default module as per `podlove_setup_modules()` in `includes\u002Fsetup.php`), as the player often renders episode metadata.\n\n### 7. Expected Results\n*   The POST request should return a `302 Redirect` to the post edit page.\n*   Upon navigating to the episode's frontend page, a JavaScript alert box displaying the document domain should appear.\n*   Inspecting the HTML source of the frontend page should show:\n    `...\u003Cspan class=\"podlove-subtitle\">\">\u003Cscript>alert(document.domain)\u003C\u002Fscript>\u003C\u002Fspan>...` (or similar depending on the active template).\n\n### 8. Verification Steps\n1.  **Check Database**: Use WP-CLI to verify the payload is stored in the custom table.\n    ```bash\n    wp db query \"SELECT subtitle FROM wp_podlove_episode ORDER BY id DESC LIMIT 1;\"\n    ```\n2.  **Verify Rendering**: Perform an unauthenticated `http_request` to the episode URL and grep for the script.\n    ```bash\n    # Use the tool to fetch the frontend page\n    # Search for \"\u003Cscript>alert\"\n    ```\n\n### 9. Alternative Approaches\n*   **Payload Location**: If `subtitle` is sanitized, attempt injection in the `podlove_episode[summary]` field or the `podlove_episode[number]` field (if it isn't cast to an integer).\n*   **Template Injection**: If the site uses custom Podlove templates, navigate to **Podlove > Templates** (if the Contributor has `podlove_read_templates` capability) and try to inject into the template content itself.\n*   **Shortcode Vector**: If the frontend doesn't show the subtitle by default, create a regular post as Contributor and use the shortcode: `[podlove-episode-subtitle post_id=\"\u003CID>\"]`. If the shortcode is vulnerable, the XSS will fire when that post is viewed.","The Podlove Podcast Publisher plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) in versions up to and including 4.3.3. This occurs because the plugin fails to sufficiently sanitize episode metadata and escape user-supplied attributes in shortcodes, allowing authenticated users with Contributor-level access to inject arbitrary scripts that execute when other users view the content.","\u002F\u002F lib\u002Fshortcodes.php around line 102\nreturn $cache->cache_for($cache_key, function () use ($template_id, $attributes) {\n    if (!$template = Model\\Template::find_one_by_title_with_fallback($template_id)) {\n        return sprintf(__('Podlove Error: Whoops, there is no template with id \"%s\"', 'podlove-podcasting-plugin-for-wordpress'), $template_id);\n    }\n\n--- \n\n\u002F\u002F lib\u002Fmodel\u002Fpodcast.php line 147\npublic function full_title()\n{\n    $t = $this->title;\n\n    if ($this->subtitle) {\n        $t = $t.' - '.$this->subtitle;\n    }\n\n    return $t;\n}","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpodlove-podcasting-plugin-for-wordpress\u002F4.3.3\u002Flib\u002Fshortcodes.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpodlove-podcasting-plugin-for-wordpress\u002F4.3.4\u002Flib\u002Fshortcodes.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpodlove-podcasting-plugin-for-wordpress\u002F4.3.3\u002Flib\u002Fshortcodes.php\t2020-09-08 18:35:06.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpodlove-podcasting-plugin-for-wordpress\u002F4.3.4\u002Flib\u002Fshortcodes.php\t2026-02-20 12:23:36.000000000 +0000\n@@ -100,7 +100,8 @@\n \n     return $cache->cache_for($cache_key, function () use ($template_id, $attributes) {\n         if (!$template = Model\\Template::find_one_by_title_with_fallback($template_id)) {\n-            return sprintf(__('Podlove Error: Whoops, there is no template with id \"%s\"', 'podlove-podcasting-plugin-for-wordpress'), $template_id);\n+            $safe_template_id = esc_html($template_id);\n+            return sprintf(__('Podlove Error: Whoops, there is no template with id \"%s\"', 'podlove-podcasting-plugin-for-wordpress'), $safe_template_id);\n         }\n \n         $html = apply_filters('podlove_template_raw', $template->title, $attributes);","1. Authenticate to the WordPress dashboard as a user with Contributor role or higher.\n2. Navigate to the podcast episode editor (wp-admin\u002Fpost-new.php?post_type=podcast or post.php).\n3. Inject a JavaScript payload into episode metadata fields, specifically the 'subtitle' parameter (e.g., podlove_episode[subtitle]=\">\u003Cscript>alert(document.domain)\u003C\u002Fscript>\").\n4. Alternatively, create a post and include a Podlove shortcode that uses a template ID attribute containing a malicious payload, such as [podlove-template id=\"\u003Cscript>alert(1)\u003C\u002Fscript>\"].\n5. Save the post or episode to store the payload in the database.\n6. The script will execute in the browser context of any user (including administrators) who visits the frontend page for that episode or the post containing the malicious shortcode.","gemini-3-flash-preview","2026-04-18 05:17:36","2026-04-18 05:18:16",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","4.3.3","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpodlove-podcasting-plugin-for-wordpress\u002Ftags\u002F4.3.3","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fpodlove-podcasting-plugin-for-wordpress.4.3.3.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpodlove-podcasting-plugin-for-wordpress\u002Ftags\u002F4.3.4","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fpodlove-podcasting-plugin-for-wordpress.4.3.4.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpodlove-podcasting-plugin-for-wordpress\u002Ftags"]