Craft CMS exposes a server-side request forgery (SSRF) vulnerability in its GraphQL mutations for asset uploads. Attackers with edit and create permissions on a volume can force the server to connect to arbitrary internal services, bypassing common localhost filters. This hits craftcms/cms, rated medium severity, and relies on poor URL scheme validation—no restrictions to http or https.
The flaw matters because Craft CMS powers thousands of sites, from small blogs to enterprise setups. Its GraphQL API, often publicly exposed for headless use, handles asset operations via mutations like those in the asset uploads. If your schema enables “edit assets” and “create assets” permissions for a volume, you’re at risk. Internal services like Redis, databases, or cloud metadata endpoints become reachable, potentially leading to data leaks, remote code execution, or denial of service.
Exploitation Mechanics
At core, the code fails to whitelist URL schemes. Developers intend uploads over http/https, but nothing enforces it. Attackers supply gopher:// URLs, an old protocol that pipes raw TCP data. Gopher lets you craft payloads for services like Redis without HTTP overhead.
A key bypass uses DWORD notation for IPs. Localhost (127.0.0.1) converts to decimal 2130706433. Filters scanning for “127.0.0.1” or “localhost” miss this. Example payload targets Redis on port 6379:
gopher://2130706433:6379/_FLUSHALL
Send this in the asset upload mutation, and the server flushes Redis data. Swap _FLUSHALL for commands like CONFIG GET * (exfiltrate config) or module load (RCE if modules available). Works against memcached, PostgreSQL, or AWS IMDS too. No auth needed beyond GraphQL perms—often granted to editors or admins.
Craft CMS volumes map to local or remote storage. GraphQL integrates seamlessly, but this oversight predates common SSRF hardening. Similar issues plague other CMS like WordPress plugins or Strapi, where file uploads proxy requests.
Real-World Impact
Why care? SSRF ranks high on OWASP Top 10 for a reason—servers trust themselves more than external input. In Craft, a compromised editor account (phished or weak perms) pivots to internals. Imagine flushing caches before a DDoS, dumping database creds via pg_read_file, or querying IMDS for IAM keys in AWS.
Stats: Craft CMS runs on ~100,000 domains per BuiltWith scans. GraphQL endpoints appear in 20-30% of modern deployments. Redis exposes on localhost in 40% of PHP apps (per security audits). Chain this with a reflected XSS or API key leak, and it’s game over. Medium rating undersells it—blind SSRF often escalates fast.
Skeptical note: Requires GraphQL schema tweaks, so not every install hits. But defaults lean permissive, and agencies customize heavily. No public CVE yet, but advisories like this signal patches incoming. Check your craftcms/cms version; update if below latest 4.x or 5.x releases.
Remediation and Hardening
Fix starts with normalizing hostnames. Convert decimal/hex IPs to dotted quad before checks. Use libraries like PHP’s inet_pton or Node equivalents. Whitelist schemes strictly: http, https, data, blob only. Block private IPs (10.0.0.0/8, 172.16.0.0/12, 127.0.0.0/8, ::1/128).
For Craft: Disable asset mutations in public schemas or gate behind auth. Audit GraphQL with tools like GraphQL Voyager or Introspection queries. Server-side, deploy WAF rules blocking gopher, file, dict schemes. Network-level: VPC endpoints or firewalls restrict localhost binds.
// Example PHP normalization
$host = inet_ntop(inet_pton(parse_url($url, PHP_URL_HOST)));
if (ip2long($host) <= 0 || filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
throw new InvalidArgumentException('Blocked internal IP');
}
Long-term, Craft devs should patch upstream. Test your setup: Spin a local Redis, craft the payload, confirm block. This vuln exposes a pattern—GraphQL’s flexibility invites oversights. Prioritize asset endpoints in pentests.
Bottom line: Patch now, tighten perms, normalize inputs. SSRF bites when ignored; don’t let internal services become public.