Unauthenticated SSRF in WordPress plugins, and the callback pattern that keeps producing it
A field note on the class of bug behind CVE-2026-7798 in FluentCRM. When a plugin fetches a URL supplied in an unauthenticated request, an allowlist is not optional. Educational only, no weaponization.
Server-side request forgery keeps showing up in WordPress plugins for a structural reason: plugins often act as clients. They call payment gateways, delivery webhooks, and mail providers. Whenever a plugin takes a URL from a request and fetches it on the server, there is a question to answer: who is allowed to choose that URL, and where is it allowed to point.
CVE-2026-7798 in FluentCRM is a clean example of the pattern. This note stays at the level of the public advisory and the general class of bug. There is no payload here, because none is needed to make the point.
The pattern
A handler accepts a parameter that is meant to be a provider callback, then fetches it server side before an authentication or signature check has actually taken effect. From the outside, the request looks like ordinary traffic. From the inside, the server is now making requests that an external attacker normally could not: to link-local metadata endpoints, to internal admin panels, to anything reachable from the host.
The FluentCRM case has an extra wrinkle worth noting for triage. The exposure only exists while the integration is in its default, unconfigured state, because configuring it stores a key that the authentication check then relies on. That is the kind of precondition that changes severity and is easy to miss if you only read the code path and not the state it assumes.
Validating impact safely
You confirm SSRF with an out-of-band interaction against infrastructure you own. A request that the target resolves and connects to your own listener proves the server made the call, and the DNS and HTTP logs are your evidence. You do not need to reach anyone’s internal services to demonstrate the bug, and you should not try.
The fix
Treat any server-side fetch of a request-supplied URL as dangerous by default:
function is_allowed_callback_host( string $url ): bool {
$host = wp_parse_url( $url, PHP_URL_HOST );
if ( ! $host ) {
return false;
}
// Only known provider domains may be fetched.
$allowed = array( 'sns.amazonaws.com', 'hooks.provider.example' );
$host = strtolower( $host );
foreach ( $allowed as $domain ) {
if ( $host === $domain || str_ends_with( $host, '.' . $domain ) ) {
return true;
}
}
return false;
}
An allowlist of expected provider hosts, a real authentication or signature check that runs before the fetch, and a resolver that refuses private and link-local ranges together close the pattern. The advisory for this specific case is on Wordfence.