Leaving the cash register unattended in a store where anyone can walk in and stamp their own receipt "PAID"
CVE-2026-4986 affects WPForms (Lite and Pro) versions 1.10.0.1 through 1.10.0.4 — specifically sites that enabled the PayPal Commerce payment integration introduced in 1.10.0. The plugin registers a public REST endpoint at /wp-json/wpforms/ppc/webhooks/ with permission_callback => '__return_true' and processes incoming JSON payloads as PayPal events *without verifying the Paypal-Transmission-Sig header or performing any server-to-server callback*. An unauthenticated attacker can POST a forged PAYMENT.CAPTURE.COMPLETED or PAYMENT.CAPTURE.DENIED event to flip the payment state of any matching transaction record. The fix in 1.10.0.5 (released May 12, 2026) adds signature verification before dispatching events.
The vendor's MEDIUM / 5.3 score is honest and defensible. The CVSS vector AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N accurately captures the attack surface: unauthenticated, network-accessible, zero-click — but the ceiling is integrity-only with no confidentiality breach, no code execution, and no availability impact. The real-world harm is financial fraud on individual WordPress sites (marking unpaid orders as paid, or denying legitimate payments), not lateral movement or fleet compromise. Eleven independent researchers found the same bug, the PoC is a single curl command, and the install base is ~6 million — but only the subset running PayPal Commerce is actually vulnerable. The vendor called it right.
4 steps from start to impact.
Identify a vulnerable WPForms + PayPal Commerce target
/wp-json/wpforms/ppc/webhooks/ is publicly enumerable — a 200 or 405 response confirms the route is registered. No authentication or prior access is required.- Target site runs WordPress with WPForms 1.10.0.1–1.10.0.4
- PayPal Commerce addon is enabled and configured
- Only a subset of WPForms' 6M installs actually enable PayPal Commerce — most use it for simple contact forms
- Sites on managed WordPress hosts may have WAF rules blocking unexpected REST API POSTs
Craft a forged PayPal webhook event
event_type (e.g., PAYMENT.CAPTURE.COMPLETED) and resource.id (a transaction identifier). No PayPal-Transmission-Sig, Paypal-Transmission-Id, or other signature headers are required because the plugin never checks them. The PoC is a single curl -X POST command.- Knowledge of PayPal webhook event schema (publicly documented by PayPal)
- None — the payload format is trivially available in PayPal's developer documentation
Submit forged event to the unprotected endpoint
https://target/wp-json/wpforms/ppc/webhooks/. The handler accepts the body, decodes JSON, and dispatches it to the payment state machine without any sender verification. The forged event is processed as if it came from PayPal's infrastructure.- HTTP POST access to the target site (internet-facing WordPress)
- Rate limiting or IP-based blocks on the REST API could slow automated attacks
- Some CDN/WAF configurations strip or reject requests missing expected PayPal source IPs
Payment state is manipulated
PAYMENT.CAPTURE.COMPLETED event marks a pending transaction as paid, potentially triggering downstream fulfillment actions (email confirmations, access grants, order processing). A PAYMENT.CAPTURE.DENIED event can cancel legitimate payments. The attacker achieves financial fraud bounded to the site's WPForms payment workflow — but gains no code execution, no database access beyond payment states, and no ability to pivot laterally.- The forged resource.id must match or be accepted by the handler's lookup logic
- If the handler does strict transaction ID matching, the attacker needs a valid pending transaction ID — blind spraying may produce mostly misses
- The gap between WordPress payment records and PayPal's actual ledger creates reconciliation discrepancies that merchants can detect
/wp-json/wpforms/ppc/webhooks/ to only accept POSTs from PayPal's documented webhook IP ranges. This breaks the attack chain at step 3 by preventing unauthorized sources from reaching the handler. No noisgate mitigation SLA applies for MEDIUM — go straight to the 365-day remediation window, but this quick server config change can be deployed immediately as defense-in-depth.- WordPress user role restrictions — the endpoint uses
permission_callback => '__return_true', so WordPress authentication and capability checks are bypassed by design. Tightening user roles has zero effect. - Disabling XML-RPC — this is a REST API endpoint, not XML-RPC. Disabling XML-RPC provides no protection.
- CAPTCHA or anti-bot on forms — the webhook endpoint is a server-to-server callback path, not a user-facing form. CAPTCHA does not apply.
The supporting signals.
| In-the-Wild Exploitation | No evidence. Not listed on CISA KEV. No GreyNoise tags or Shodan honeypot data observed for this endpoint. No known campaigns targeting WPForms PayPal webhooks as of October 2026. |
|---|---|
| Proof-of-Concept | Public and trivial. A single curl command suffices: curl -X POST 'https://TARGET/wp-json/wpforms/ppc/webhooks/' -H 'Content-Type: application/json' -d '{"event_type":"PAYMENT.CAPTURE.COMPLETED","resource":{"id":"TXNID"}}'. Documented in the researcher's blog post. Not indexed on pocindex.io or major PoC aggregators as of this writing. No Metasploit or Nuclei template found. |
| EPSS | 0.0033 (bottom ~15th percentile) — indicates very low predicted exploitation probability in the next 30 days. |
| KEV Status | Not listed. No CISA KEV entry as of 2026-10-07. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N — Network-accessible, low complexity, no privileges, no interaction. Impact limited to low integrity. No confidentiality or availability impact. |
| Affected Versions | WPForms 1.10.0.1 through 1.10.0.4 (Lite and Pro). PayPal Commerce feature was introduced in 1.10.0. Versions prior to 1.10.0 are *not affected* (no PayPal Commerce endpoint). |
| Fixed Version | 1.10.0.5 (released May 12, 2026). Adds verify_webhook_signature() check before event dispatch. Auto-update available via WordPress plugin updater. |
| Install Base / Exposure | WPForms has 6 million+ active installations across Lite and Pro. However, only sites that (a) upgraded to 1.10.0+ AND (b) enabled PayPal Commerce are vulnerable. PayPal Commerce is a premium/addon feature — the exposed population is a fraction of the total base. |
| Disclosure Date | 2026-05-19 (publicly published by WPScan). Fix released 2026-05-12 (patch preceded public disclosure). |
| Researcher | Sudhanshu Chauhan (RedHunt Labs). Notably, 11 independent researchers reported the same bug, indicating the flaw was easily discoverable. Blog writeup by Himanshu Anand (reporter #11). |
Sources.
Why this verdict
- Trivially exploitable, but impact-capped. The attack is unauthenticated, zero-click, and requires only a
curlcommand — there is essentially zero friction to *reach* the endpoint. However, the impact ceiling is integrity-only: payment state manipulation. No code execution, no data exfiltration, no privilege escalation. This justifies the vendor's MEDIUM bucket. - Role multiplier: WPForms is a WordPress form plugin, not a high-value-role component. It is not an identity provider, hypervisor, CI/CD system, PAM, backup platform, database engine, security agent, or network edge appliance. The blast radius of successful exploitation is per-site financial records — not domain, fleet, or supply-chain scale. Even on an e-commerce WordPress site (the worst plausible role), the outcome is payment fraud bounded to one merchant's WPForms transactions, not lateral movement. No high-value-role floor applies.
- Exposure narrowing. The 6M install base is the theoretical maximum. The actually-vulnerable population is the subset that (a) upgraded to 1.10.0+ and (b) activated PayPal Commerce — likely a single-digit percentage of total installs. This narrows the real-world target surface significantly but doesn't warrant a score adjustment because the remaining population is still large in absolute terms.
- No exploitation signal. EPSS is 0.0033, no KEV listing, no GreyNoise/Shodan data, no known campaigns. Eleven researchers found it independently (indicating discoverability), but none reported active exploitation. This supports keeping the score where it is rather than upgrading.
Why not higher?
The impact is strictly integrity-only with no path to code execution, data exfiltration, or lateral movement. The CVSS vector's C:N/I:L/A:N accurately reflects the ceiling. WPForms is not a high-value-role component — even in the worst case (e-commerce site), successful exploitation yields per-site payment fraud, not domain takeover or fleet compromise. There is no deployment role that would trigger a HIGH or CRITICAL floor.
Why not lower?
The exploit is unauthenticated, network-accessible, requires no user interaction, and the PoC is a single curl command — the lowest possible friction. The financial fraud potential is real and actionable: an attacker can mark their own unpaid order as completed and receive goods or services. With 6M+ installs, even a small fraction using PayPal Commerce represents tens of thousands of targets. Dropping below MEDIUM would understate the ease of exploitation and the tangible financial harm.
Crowdsourced verification payload.
Run this script on the target WordPress host (or any machine with HTTP access to the target). It checks the installed WPForms version via the plugin's readme.txt. Requires curl and grep. Usage: bash check_cve_2026_4986.sh https://example.com
#!/usr/bin/env bash
# CVE-2026-4986 checker — WPForms PayPal webhook forgery
# Usage: bash check_cve_2026_4986.sh <wordpress_url>
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
TARGET="${1:-}"
if [ -z "$TARGET" ]; then
echo "Usage: $0 <wordpress_url>"
exit 2
fi
# Strip trailing slash
TARGET="${TARGET%/}"
# Try both plugin slugs (lite and pro)
VERSION=""
for SLUG in wpforms-lite wpforms; do
RESPONSE=$(curl -sf "${TARGET}/wp-content/plugins/${SLUG}/readme.txt" 2>/dev/null || true)
if [ -n "$RESPONSE" ]; then
VERSION=$(echo "$RESPONSE" | grep -i '^Stable tag:' | head -1 | sed 's/[Ss]table [Tt]ag:[[:space:]]*//' | tr -d '[:space:]')
break
fi
done
if [ -z "$VERSION" ]; then
echo "UNKNOWN — Could not detect WPForms version at ${TARGET}"
echo "The plugin may not be installed, or readme.txt is not publicly accessible."
exit 2
fi
echo "Detected WPForms version: ${VERSION}"
# Version comparison: vulnerable if < 1.10.0.5
# Split version into components
IFS='.' read -ra PARTS <<< "$VERSION"
MAJOR=${PARTS[0]:-0}
MINOR=${PARTS[1]:-0}
PATCH=${PARTS[2]:-0}
SUB=${PARTS[3]:-0}
# PayPal Commerce introduced in 1.10.0 — versions before 1.10.0 are not affected
if [ "$MAJOR" -lt 1 ] || ([ "$MAJOR" -eq 1 ] && [ "$MINOR" -lt 10 ]); then
echo "PATCHED — Version ${VERSION} predates PayPal Commerce feature (introduced in 1.10.0). Not affected by CVE-2026-4986."
exit 0
fi
# Vulnerable: 1.10.0.1 through 1.10.0.4
if [ "$MAJOR" -eq 1 ] && [ "$MINOR" -eq 10 ] && [ "$PATCH" -eq 0 ] && [ "$SUB" -lt 5 ]; then
echo "VULNERABLE — WPForms ${VERSION} is affected by CVE-2026-4986 (PayPal webhook forgery). Update to 1.10.0.5+."
exit 1
fi
echo "PATCHED — WPForms ${VERSION} is >= 1.10.0.5. CVE-2026-4986 is fixed."
exit 0