← Back to Feed CACHED · 2026-07-15 07:57:23 · CACHE_KEY CVE-2025-10147
CVE-2025-10147 · CWE-434 · Disclosed 2025-09-23

The Podlove Podcast Publisher plugin for WordPress is vulnerable to arbitrary file uploads due to missing…

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
Do you agree?
01 · The Real Story

A podcast plugin left the studio door unlocked and forgot to check ID at the mixing board

The Podlove Podcast Publisher plugin exposes a move_as_original_file routine in lib/model/image.php that accepts uploads without validating file type, extension, or MIME. An unauthenticated HTTP request can drop a .php webshell into a web-accessible directory, giving the attacker code execution as the webserver user on any WordPress instance running the plugin. Everything at and below version 4.2.6 is affected; 4.2.7 ships the fix.

The vendor 9.8 label is *technically* correct for the shape of the bug — no auth, no interaction, RCE — but it ignores population. Wordfence/WPScan-class CVSS scoring rewards raw ergonomics, not exposure. With ~4,000 active installs on a niche podcasting plugin, this is a HIGH by any real-world blast-radius measure, not a fleet-melting CRITICAL like a WooCommerce or Elementor bug of the same shape.

"Unauth RCE is real, but the installed base is ~4K niche podcasting sites — HIGH, not CRITICAL."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Fingerprint plugin presence

Attacker probes for /wp-content/plugins/podlove-podcasting-plugin-for-wordpress/ or the plugin's readme.txt to confirm install and version. Mass scanners (wpscan, nuclei with community WordPress templates) already ship WordPress plugin enumeration.
Conditions required:
  • Target WordPress site reachable on 80/443
  • Plugin directory not blocked at edge
Where this breaks in practice:
  • Only ~4,000 sites globally run this plugin — targeted scanning yields near-zero hit rate
  • Some hosts hide plugin paths via WAF or wp_hide_plugin-style hardening
Detection/coverage: nuclei template http/wordpress/plugins/podlove-podcasting-plugin-for-wordpress.yaml catches version disclosure. WPScan CLI catches version and known-CVE.
STEP 02

Hit the unauthenticated upload endpoint

The attacker crafts an HTTP request to the plugin's image-handling REST/admin-ajax path that reaches move_as_original_file. No nonce, no capability check, no MIME sniff — the file lands on disk with attacker-controlled extension. A generic curl -F with a PHP payload is enough; no custom exploit framework required.
Conditions required:
  • Vulnerable Podlove ≤ 4.2.6 installed and active
  • PHP execution allowed in the upload target directory
Where this breaks in practice:
  • Managed WP hosts (WP Engine, Kinsta, Pantheon) block PHP execution in wp-content/uploads by default
  • ModSecurity CRS rule 932100 catches PHP tags in multipart body
Detection/coverage: WAF logs will show a multipart POST with .php filename or <?php payload. Wordfence Premium ships a signature within days of a public PoC.
STEP 03

Fetch the webshell

Attacker requests the dropped file's URL to trigger PHP interpretation. Result is code execution as the PHP-FPM/Apache user — usually www-data or nobody, sometimes the site's own UID under shared hosting.
Conditions required:
  • Uploaded file lives inside document root
  • .htaccess or Nginx location block does not deny .php under uploads
Where this breaks in practice:
  • Hardened WP configs deny .php in wp-content/uploads
  • Read-only container filesystems (Roots Bedrock, Docker+ephemeral) reject the write
Detection/coverage: EDR on the WP host flags PHP process spawning sh, curl, wget, or outbound connections to unknown IPs.
STEP 04

Loot and pivot

From the webshell, attacker reads wp-config.php (DB creds, AUTH_KEYS), dumps the users table, plants a persistence backdoor in mu-plugins/, and — if DB creds are shared — pivots to the MySQL/MariaDB tier. On multi-tenant hosting, escalation stops at the site boundary; on a self-hosted VPS, they own the box.
Conditions required:
  • DB reachable from web tier
  • No FIM/OSSEC watching wp-content and wp-config.php
Where this breaks in practice:
  • Site isolation on managed hosts contains blast to one tenant
  • chrooted/jailed PHP-FPM pools limit horizontal reach
Detection/coverage: File integrity monitors (Wordfence, Tripwire, wp-cli --skip-plugins verify-checksums) catch modified core/plugin files.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo known active exploitation as of 2026-07-15. Not on CISA KEV. No public campaign reporting from Wordfence Threat Intel, Patchstack, or Sucuri.
Public PoCNo weaponized public PoC in Exploit-DB or GitHub as of assessment. ZeroPath and Patchstack advisories describe the vulnerable function (move_as_original_file in lib/model/image.php:465) — enough for a competent attacker to derive one.
EPSS0.882% — bottom quartile. Reflects both low install base and absent public exploit.
KEV statusNot listed. Would need active mass exploitation to be added; niche install base makes this unlikely.
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8. Network, no auth, no UI — vendor is scoring the *shape* of the bug, not population.
Affected versionsPodlove Podcast Publisher ≤ 4.2.6 (all prior versions).
Fixed version4.2.7 — released with hardened image download-cache handling. No distro backports; plugin ships via WordPress.org and GitHub only.
Exposure / footprint~4,000 active installs per WordPress.org plugin directory. Not enumerated by GreyNoise (no known exploit traffic tag). Shodan/Censys count of exposed WP sites is millions, but this specific plugin footprint is a rounding error.
Disclosure date2025-09-23 via Patchstack and Wordfence Intelligence.
ReporterCredited via Wordfence/Patchstack coordinated disclosure programs.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to HIGH (8.4/10)

Downgraded from CRITICAL 9.8 to HIGH 8.4 because the single most decisive factor is installed-base share — ~4,000 active installs of a niche podcasting plugin puts the exposure population at roughly 0.005% of the WordPress ecosystem, well outside CRITICAL-tier blast radius. The bug itself is genuinely unauth RCE with trivial ergonomics, which floors the verdict at HIGH for anyone actually running it.

HIGH Vulnerability mechanics and patch availability
HIGH Installed-base and exposure estimates
MEDIUM Absence of active exploitation (niche plugin bugs are often exploited quietly)

Why this verdict

  • Unauth RCE with zero friction on affected sites: no capability check, no nonce, no MIME validation — a single curl POST wins. On any host actually running Podlove ≤ 4.2.6 with PHP execution permitted in uploads, this is a full site takeover.
  • Installed base narrows the fleet math: ~4,000 sites globally versus millions for a plugin like Elementor. In a 10,000-host enterprise, you are unlikely to have more than one or two instances, typically on a marketing/podcasting subsite — not the identity plane.
  • Role multiplier — WordPress marketing/podcast tier: compromise ends at the site boundary on managed hosting or at the VPS boundary on self-hosted. This plugin is not deployed on identity providers, hypervisors, CI runners, or backup servers. Blast radius is site-scale, not fleet-scale. Verdict floor stays at HIGH, does not reach CRITICAL.
  • No KEV, low EPSS, no observed campaigns: attacker interest tracks target density, and the target density here is low. But because the bug is easy, expect opportunistic scanning once a PoC is published.
  • Compensating controls are cheap: deny .php execution under wp-content/uploads, put the site behind Wordfence or Patchstack, or simply update — no complex regression testing required for a plugin-level bump.

Why not higher?

Not CRITICAL because installed base is negligible (~4K sites) and the affected component does not sit on an identity, hypervisor, backup, edge, or CI role where compromise cascades. Blast radius stops at a single WordPress tenant. No active exploitation, no KEV, EPSS well under 1%.

Why not lower?

Not MEDIUM because for the ~4,000 sites actually running Podlove ≤ 4.2.6, this is an *unauthenticated* pre-auth RCE with a trivial one-request exploit path. Any site running it gets fully owned; there is no meaningful workflow friction. MEDIUM would imply the attacker needs auth or a rare configuration — neither is true here.

05 · Compensating Control

What to do — in priority order.

  1. Upgrade Podlove Podcast Publisher to 4.2.7 or later — This is the primary fix and the fastest path. wp plugin update podlove-podcasting-plugin-for-wordpress via WP-CLI takes seconds and requires no schema migration. Deploy within 30 days per the noisgate mitigation SLA for HIGH; ideally same-day on public-facing WP.
  2. Deny PHP execution under wp-content/uploads/ — Drop an .htaccess with <FilesMatch "\.php$"> Require all denied </FilesMatch> or an Nginx location ~ ^/wp-content/uploads/.*\.php$ { deny all; } block. This kills the RCE step even if the upload lands. Standard WP hardening — deploy fleet-wide within 30 days regardless of this CVE.
  3. Deploy Wordfence, Patchstack, or a virtual-patching WAF in front of affected sites — Both vendors ship a virtual patch for CVE-2025-10147 that blocks the vulnerable request signature. Buys time on any site you cannot immediately update. Deploy within 30 days.
  4. File Integrity Monitoring on wp-content/plugins/ and wp-config.php — Use Wordfence, Sucuri, or Tripwire to alert on new .php files in unexpected paths. Catches successful exploitation and post-exploit persistence (mu-plugins backdoors). Standing control.
What doesn't work
  • Requiring a login for /wp-admin/ — the vulnerable endpoint is reachable pre-auth; hiding wp-login.php does nothing.
  • Fail2ban on brute-force patterns — this is a single-request exploit, not a brute-force one; rate-limiting login attempts is unrelated.
  • MFA on WordPress admins — no admin session is required to trigger the upload.
  • Cloudflare bot mitigation on default settings — a plain curl -F with a benign-looking filename slips past most bot rules unless you specifically tune for .php uploads.
06 · Verification

Crowdsourced verification payload.

Run from the WordPress host as any user with read access to the webroot, or from an auditor workstation via SSH. Pass the WordPress install path as the first argument, e.g. ./check-cve-2025-10147.sh /var/www/html. No elevated privileges required — this is a read-only version check.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# noisgate verifier — CVE-2025-10147 (Podlove Podcast Publisher <= 4.2.6)
# Usage: ./check-cve-2025-10147.sh /path/to/wordpress
# Exit codes: 0 = PATCHED / NOT INSTALLED, 1 = VULNERABLE, 2 = UNKNOWN

set -u
WP_ROOT="${1:-/var/www/html}"
PLUGIN_DIR="$WP_ROOT/wp-content/plugins/podlove-podcasting-plugin-for-wordpress"
FIXED_VERSION="4.2.7"

if [[ ! -d "$WP_ROOT" ]]; then
  echo "UNKNOWN: WordPress root '$WP_ROOT' not found"
  exit 2
fi

if [[ ! -d "$PLUGIN_DIR" ]]; then
  echo "PATCHED: Podlove Podcast Publisher not installed at $WP_ROOT"
  exit 0
fi

# Extract Version: header from main plugin file
PLUGIN_FILE="$PLUGIN_DIR/podlove.php"
if [[ ! -f "$PLUGIN_FILE" ]]; then
  # fall back to any php file with a plugin header
  PLUGIN_FILE=$(grep -rl "Plugin Name:.*Podlove" "$PLUGIN_DIR" 2>/dev/null | head -n1)
fi

if [[ -z "${PLUGIN_FILE:-}" || ! -f "$PLUGIN_FILE" ]]; then
  echo "UNKNOWN: could not locate Podlove plugin header file"
  exit 2
fi

VERSION=$(grep -iE '^\s*\*?\s*Version:' "$PLUGIN_FILE" | head -n1 | sed -E 's/.*Version:\s*//I' | tr -d '\r' | awk '{print $1}')

if [[ -z "$VERSION" ]]; then
  echo "UNKNOWN: version header not readable in $PLUGIN_FILE"
  exit 2
fi

# Version comparison via sort -V
LOWEST=$(printf '%s\n%s\n' "$VERSION" "$FIXED_VERSION" | sort -V | head -n1)
if [[ "$VERSION" == "$FIXED_VERSION" || "$LOWEST" == "$FIXED_VERSION" ]]; then
  echo "PATCHED: Podlove Podcast Publisher $VERSION (>= $FIXED_VERSION)"
  exit 0
else
  echo "VULNERABLE: Podlove Podcast Publisher $VERSION (< $FIXED_VERSION) — CVE-2025-10147"
  exit 1
fi
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning: run wp plugin list --name=podlove-podcasting-plugin-for-wordpress --field=version across your WP fleet (or the verifier above) to find affected instances. Per the noisgate mitigation SLA for HIGH, deploy a compensating control — either the 4.2.7 update itself, a Wordfence/Patchstack virtual patch, or an Nginx/.htaccess rule denying .php execution under wp-content/uploads/ — within 30 days. Per the noisgate remediation SLA for HIGH, the actual wp plugin update podlove-podcasting-plugin-for-wordpress --version=4.2.7 must be complete fleet-wide within 180 days. If any affected site is internet-facing and hosts anything sensitive (customer data, admin sessions shared with other sites, shared DB), collapse both timelines to same-week action — the exploit is a single curl request and a PoC is trivially derivable from the public patch diff.

Sources

  1. ZeroPath technical writeup
  2. GitHub Security Advisory GHSA-2m7h-5m38-vhh4
  3. Patchstack advisory
  4. CVE Details entry
  5. Wiz vulnerability database
  6. SecAlerts summary
  7. WordPress.org plugin page
  8. Podlove Publisher GitHub
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.