An open filing cabinet at the front door labeled `wp-json` — and the SendGrid keys are in the top drawer
Gravity SMTP registers a REST route at /wp-json/gravitysmtp/v1/tests/mock-data whose permission_callback is hardcoded to __return_true. Any unauthenticated visitor who appends ?page=gravitysmtp-settings gets back ~365 KB of JSON: PHP version and extensions, web server, document root, DB engine/version, WordPress core version, every active plugin and theme with versions, DB table prefix, and — crucially — the API tokens and credentials configured for whatever mail transport the site uses (SendGrid, Mailgun, SES, SMTP2GO, Postmark, etc.). Affects all versions ≤ 2.1.4; fixed in 2.1.5.
Vendor's HIGH (7.5, C:H only) is technically defensible — direct CIA impact is confidentiality. In practice it understates the chain. The disclosed connector_data blob includes live transport credentials, which means this is not just recon; it's a one-shot credential leak that pivots into the victim's email infrastructure (sender reputation hijack, phishing-as-the-brand, password-reset interception). Wordfence has blocked 17M+ exploit attempts; CrowdSec flipped it to background-noise status by June 1, 2026, with 412 distinct attacker IPs and 83% of observed intent classified as infrastructure takeover. We nudge to 8.2.
5 steps from start to impact.
Identify Gravity SMTP installs
/wp-json/gravitysmtp/v1/ and reading the 200/404, or by fingerprinting plugin assets under /wp-content/plugins/gravitysmtp/. Trivially automated with httpx, nuclei, or any WordPress recon framework.- Internet-reachable WordPress site
- Gravity SMTP plugin installed and active
- REST API not blocked by WAF rule
- Sites behind Cloudflare WAF Managed Rules may block the discovery probe
- REST API disabled via
disable-json-apior similar plugin
CVE-2026-4020 available publiclyHit the unauthenticated endpoint
https://target/wp-json/gravitysmtp/v1/tests/mock-data?page=gravitysmtp-settings. The permission_callback returns true unconditionally, so no nonce, no cookie, no auth header is required. Response is ~365 KB of JSON.- Endpoint reachable
- Plugin version ≤ 2.1.4
- Cloudflare/Sucuri/Wordfence virtual patch can drop the request
- Some hosts rate-limit
/wp-json/aggressively
Parse the System Report
connector_data from the JSON. Each configured transport carries its credentials in cleartext — SendGrid API key, Mailgun domain+key, AWS SES access_key/secret, SMTP2GO token, raw SMTP user/pass, etc. Also harvest plugin/theme inventory for follow-on CVE pivoting.- Site has at least one mail transport configured
- Credentials were entered through the plugin UI (not constants in wp-config.php)
- Sites using only the default
wp_mail()SMTP fallback have no API keys to leak - Rare hardened setups inject keys via env vars not stored in plugin options
Weaponize the stolen credentials
- Stolen key still valid (not rotated)
- Provider tenant not IP-restricted
- SendGrid/Mailgun anomaly detection may rate-limit a new sending pattern
- AWS SES sandbox accounts cap sending to verified addresses
Pivot via the leaked plugin inventory
- Site runs at least one other vulnerable plugin
- No virtual patching layer
- Sites on managed WordPress hosts (WP Engine, Kinsta, Pressable) get automatic virtual patches
The supporting signals.
| In-the-wild status | Active mass exploitation since 2026-05-27 per CrowdSec; 17M+ attempts blocked by Wordfence since early May 2026 |
|---|---|
| Attacker telemetry | 412 distinct source IPs May 27–Jun 1, 2026 (FR/NL/US cloud), classified as Background Noise by Jun 1 — fully commoditized |
| Public PoC | Atomic Edge PoC, pentest-tools.com module, and public nuclei template — single-request curl-grade exploit |
| EPSS | 0.0298 (~~3rd percentile~~) — *score is stale*; real-world exploitation already saturating, expect EPSS to spike |
| KEV status | Not yet KEV-listed as of 2026-06-21, but meets the mass-exploitation criterion; expect addition |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N — confidentiality-only, but C:H is fair given API key leak |
| Affected versions | Gravity SMTP ≤ 2.1.4 (all releases up to and including) |
| Fixed version | 2.1.5 — adds capability check to permission_callback |
| Install base | ~100,000 active installs of Gravity SMTP per WordPress.org plugin directory; victim mix 55% commerce / 39% SOHO (CrowdSec) |
| Disclosed | 2026-03-31 (Wordfence Threat Intelligence advisory; GHSA-jxfc-8wcq-xxcg) |
| Discoverer | Reported via the Wordfence Bug Bounty program; vendor Gravity Forms LLC |
noisgate verdict.
HIGH is the correct bucket because this is a one-request, unauthenticated, internet-reachable leak of live third-party mail-provider API credentials, and exploitation is already commoditized at mass-scan scale (17M+ Wordfence blocks, 412 attacker IPs). The single decisive factor is the credential-leak component of the exposed System Report — this is not generic info disclosure, it is a credential theft primitive against a ~100k-install plugin under active mass exploitation.
Why this verdict
- Zero friction on the chain: unauthenticated, single GET, no nonce, no UA filtering, no rate-limit by default. The vendor's 7.5 already assumes AV:N/PR:N/UI:N — this is the realistic case, not the worst case.
- The blob contains credentials, not just fingerprints. SendGrid/Mailgun/SES tokens give the attacker *signed* outbound email from the victim's domain, which bypasses SPF/DKIM/DMARC and is worth more on the criminal market than the WordPress host itself.
- Mass exploitation is here, not theoretical. 17M+ blocked attempts (Wordfence), 412 attacker IPs in 5 days (CrowdSec), public nuclei template. EPSS of 0.0298 is stale and will spike.
- Role multiplier: Gravity SMTP's role is mail-transport credential storage. ≥10% of installs occupy this 'secret-bearing' role *by definition of installing the plugin*. The high-value-role outcome (provider-tenant takeover, brand-spoofed phishing) is the typical outcome, not the edge case — that floors the verdict at HIGH.
- Why not CRITICAL: the primitive does not produce code execution on the WordPress host itself, and impact is confined to outbound email provider and stale recon data. Pivot to RCE requires a separate vulnerable plugin on the same host.
Why not higher?
Direct impact is confidentiality only — no code execution, no integrity write, no service denial on the WordPress instance itself. CRITICAL would imply fleet- or domain-scale takeover of the WordPress host, which requires a chained second CVE. The leaked credentials are damaging but bounded to whichever mail-provider tenant they unlock.
Why not lower?
MEDIUM would imply low real-world exploitability or non-sensitive disclosure. Neither applies: the chain is a single unauthenticated GET, the disclosed data includes live third-party API keys, and there are 17M+ logged exploit attempts in the wild. Downgrading would mis-rank this against genuinely low-noise info-disclosure bugs.
What to do — in priority order.
- Update Gravity SMTP to 2.1.5 across every WordPress estate host within 3 days — This is the only true fix. The patch adds a proper capability check to the REST
permission_callback. Per noisgate mitigation SLA (HIGH = 30 days) we would normally allow 30 days, but active mass exploitation overrides — patch within 3 days. - Rotate every credential the plugin ever stored — Assume disclosure. Rotate SendGrid/Mailgun/SES/Postmark/SMTP2GO API keys, raw SMTP passwords, and any reply-to webhook tokens. Do this *before* upgrading on any host that has been internet-reachable since 2026-03-31. Patching does not invalidate already-stolen keys.
- Drop the route at the edge / WAF until upgraded — Add a deny rule on the URI pattern
^/wp-json/gravitysmtp/v1/tests/mock-dataat Cloudflare, AWS WAF, F5, or NGINX. Effective within minutes across the fleet and covers slow-to-patch sites. - Review mail-provider tenant logs from 2026-03-31 onward — In SendGrid/Mailgun/SES, look for sends from unexpected source IPs or unusual send volumes. If found, treat the tenant as compromised, not just the WordPress site.
- Block known attacker source IPs at the edge — Pull CrowdSec community blocklist or GreyNoise tags for CVE-2026-4020 scanners; the population is small (~400 IPs) and concentrated in cloud ASNs FR/NL/US.
- Disabling xmlrpc.php — this CVE is in the WP REST API, not XML-RPC.
- Forcing wp-admin behind HTTP basic auth — the vulnerable endpoint is under
/wp-json/, not/wp-admin/. - Relying on
DISALLOW_FILE_EDITor admin lockdown plugins — the bug requires no admin session at all. - Rotating WordPress admin passwords alone — the leak is provider API keys, not WP credentials.
Crowdsourced verification payload.
Run this from an auditor workstation (not the target) against each WordPress host you own. Invoke as ./check-cve-2026-4020.sh https://site.example.com. No credentials needed — the test exercises the same unauthenticated path an attacker would.
#!/usr/bin/env bash
# noisgate verification — CVE-2026-4020 (Gravity SMTP <= 2.1.4)
# Usage: ./check-cve-2026-4020.sh https://target.example.com
# Exits: 0 PATCHED | 1 VULNERABLE | 2 UNKNOWN
set -u
TARGET="${1:-}"
if [[ -z "$TARGET" ]]; then
echo "Usage: $0 https://target" >&2; exit 2
fi
UA="noisgate-check/1.0"
URL="${TARGET%/}/wp-json/gravitysmtp/v1/tests/mock-data?page=gravitysmtp-settings"
# 1. Probe the vulnerable endpoint.
RESP=$(curl -sk -A "$UA" -o /tmp/gsmtp_$$.json -w '%{http_code} %{size_download}' --max-time 20 "$URL") || {
echo "UNKNOWN ($TARGET) — network error"; rm -f /tmp/gsmtp_$$.json; exit 2;
}
CODE=$(echo "$RESP" | awk '{print $1}')
SIZE=$(echo "$RESP" | awk '{print $2}')
# 2. Decide.
if [[ "$CODE" == "200" && "$SIZE" -gt 50000 ]]; then
if grep -qE 'connector_data|system_report|gravitysmtp' /tmp/gsmtp_$$.json; then
echo "VULNERABLE ($TARGET) — endpoint returned ${SIZE}B of System Report JSON"
rm -f /tmp/gsmtp_$$.json; exit 1
fi
fi
if [[ "$CODE" == "401" || "$CODE" == "403" ]]; then
echo "PATCHED ($TARGET) — endpoint returned $CODE"
rm -f /tmp/gsmtp_$$.json; exit 0
fi
if [[ "$CODE" == "404" ]]; then
echo "PATCHED ($TARGET) — plugin not installed or REST route absent"
rm -f /tmp/gsmtp_$$.json; exit 0
fi
echo "UNKNOWN ($TARGET) — HTTP $CODE, ${SIZE}B (inspect /tmp/gsmtp_$$.json manually)"
exit 2
If you remember one thing.
^/wp-json/gravitysmtp/v1/tests/mock-data across the fleet by end-of-day Monday and rotate every SendGrid/Mailgun/SES/SMTP credential the plugin stored before the close of business. The noisgate remediation SLA for HIGH is ≤180 days, but for this CVE compress to ≤7 days: roll Gravity SMTP 2.1.5 to all hosts, then audit mail-provider tenant logs back to 2026-03-31 for unauthorized sends and treat any anomalous activity as a confirmed tenant compromise.Sources
- GitHub Security Advisory GHSA-jxfc-8wcq-xxcg
- CrowdSec — CVE-2026-4020 Vulntracking Report
- The Hacker News — Gravity SMTP exploit coverage
- Atomic Edge — PoC walkthrough
- pentest-tools.com — exploit module
- SentinelOne Vulnerability DB
- Managed-WP advisory (2026-03-31)
- The Next Web — 100k WordPress sites at risk
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.