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.
4 steps from start to impact.
Fingerprint plugin presence
/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.- Target WordPress site reachable on 80/443
- Plugin directory not blocked at edge
- 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
nuclei template http/wordpress/plugins/podlove-podcasting-plugin-for-wordpress.yaml catches version disclosure. WPScan CLI catches version and known-CVE.Hit the unauthenticated upload endpoint
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.- Vulnerable Podlove ≤ 4.2.6 installed and active
- PHP execution allowed in the upload target directory
- Managed WP hosts (WP Engine, Kinsta, Pantheon) block PHP execution in
wp-content/uploadsby default - ModSecurity CRS rule 932100 catches PHP tags in multipart body
.php filename or <?php payload. Wordfence Premium ships a signature within days of a public PoC.Fetch the webshell
www-data or nobody, sometimes the site's own UID under shared hosting.- Uploaded file lives inside document root
.htaccessor Nginx location block does not deny.phpunder uploads
- Hardened WP configs deny
.phpinwp-content/uploads - Read-only container filesystems (Roots Bedrock, Docker+ephemeral) reject the write
sh, curl, wget, or outbound connections to unknown IPs.Loot and pivot
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.- DB reachable from web tier
- No FIM/OSSEC watching
wp-contentandwp-config.php
- Site isolation on managed hosts contains blast to one tenant
- chrooted/jailed PHP-FPM pools limit horizontal reach
wp-cli --skip-plugins verify-checksums) catch modified core/plugin files.The supporting signals.
| In-the-wild status | No known active exploitation as of 2026-07-15. Not on CISA KEV. No public campaign reporting from Wordfence Threat Intel, Patchstack, or Sucuri. |
|---|---|
| Public PoC | No 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. |
| EPSS | 0.882% — bottom quartile. Reflects both low install base and absent public exploit. |
| KEV status | Not listed. Would need active mass exploitation to be added; niche install base makes this unlikely. |
| CVSS vector | CVSS: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 versions | Podlove Podcast Publisher ≤ 4.2.6 (all prior versions). |
| Fixed version | 4.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 date | 2025-09-23 via Patchstack and Wordfence Intelligence. |
| Reporter | Credited via Wordfence/Patchstack coordinated disclosure programs. |
noisgate verdict.
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.
Why this verdict
- Unauth RCE with zero friction on affected sites: no capability check, no nonce, no MIME validation — a single
curlPOST 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
.phpexecution underwp-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.
What to do — in priority order.
- 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-wordpressvia 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. - Deny PHP execution under
wp-content/uploads/— Drop an.htaccesswith<FilesMatch "\.php$"> Require all denied </FilesMatch>or an Nginxlocation ~ ^/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. - 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.
- File Integrity Monitoring on
wp-content/plugins/andwp-config.php— Use Wordfence, Sucuri, or Tripwire to alert on new.phpfiles in unexpected paths. Catches successful exploitation and post-exploit persistence (mu-pluginsbackdoors). Standing control.
- 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 -Fwith a benign-looking filename slips past most bot rules unless you specifically tune for.phpuploads.
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.
#!/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
If you remember one thing.
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
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.