GHSA-gp2f-7wcm-5fhx: Craft CMS has Cloud Metadata SSRF Protection Bypass via DNS Rebinding
SummaryThe SSRF validation in Craft CMS’s GraphQL Asset mutation performs DNS resolution **separately** from the HTTP request. This Time-of-Check-Time-of-Use (TOCTOU) vulnerability enables DNS rebinding attacks, where an attacker’s DNS server returns different IP addresses for validation compared to the actual request.This is a bypass of the security fix for CVE-2025-68437 ([GHSA-x27p-wfqw-hfcc](https://github.com/craftcms/cms/security/advisories/GHSA-x27p-wfqw-hfcc)) that allows access to all blocked IPs, not just IPv6 endpoints.SeverityBypass of cloud metadata SSRF protection for all blocked IPsRequired PermissionsExploitation requires GraphQL schema permissions for:Edit assets in the `<VolumeName>` volumeCreate assets in the `<VolumeName>` volumeThese permissions may be granted to:Authenticated users with appropriate GraphQL schema accessPublic Schema (if misconfigured with write permissions)Technical DetailsVulnerable Code FlowThe code at `src/gql/resolvers/mutations/Asset.php` performs two separate DNS lookups:Root CauseTwo separate DNS lookups occur:**Validation**: `gethostbyname()` in `validateHostname()`**Request**: Guzzle's internal DNS resolution via libcurlAn attacker controlling a DNS server can return different IPs for each query.Bypass MechanismTarget Endpoints via DNS RebindingDNS rebinding allows access to all blocked IPs:| Target | Rebind To | Impact |
|--------|-----------|--------|
| **AWS IMDS** | `169.254.169.254` | IAM credentials, instance identity |
| **AWS ECS** | `169.254.170.2` | Container credentials |
| **GCP Metadata** | `169.254.169.254` | Service account tokens |
| **Azure Metadata** | `169.254.169.254` | Managed identity tokens |
| **Alibaba Cloud** | `100.100.100.200` | Instance credentials |
| **Oracle Cloud** | `192.0.0.192` | Instance metadata |
| **Internal Services** | `127.0.0.1`, `10.x.x.x` | Internal APIs, databases |Attack ScenarioAttacker sets up DNS server with alternating responsesAttacker sends mutation with `url: "http://evil.attacker.com/latest/meta-data/"`First DNS query returns safe IP (e.g., `1.2.3.4`) → validation passesSecond DNS query returns metadata IP (`169.254.169.254`) → request to metadataAttacker retrieves credentials from ANY cloud provider**Attacker can now achieve code execution by creating new instances with their SSH key**RemediationFix: DNS Pinning with CURLOPT_RESOLVEPin the DNS resolution - use the same resolved IP for both validation and request:Alternative: Single Resolution with Immediate UseAdditional Mitigations| Mitigation | Description |
|------------|-------------|
| DNS Pinning (CURLOPT_RESOLVE) | Force same IP for validation and request |
| Single IP-based request | Use resolved IP directly in URL |
| Implement IMDSv2 | Requires token header (infrastructure-level) |
| Network egress filtering | Block metadata IPs at network level |Resourceshttps://github.com/craftcms/cms/commit/a4cf3fb63bba3249cf1e2882b18a2d29e77a8575[GHSA-x27p-wfqw-hfcc](https://github.com/craftcms/cms/security/advisories/GHSA-x27p-wfqw-hfcc) - Original SSRF vulnerability (CVE-2025-68437)[DNSrebinder](https://github.com/mogwailabs/DNSrebinder) - Lightweight Python DNS server for testing DNS rebinding vulnerabilities; responds with legitimate IP for first N queries, then rebinds to target IP[Singularity DNS Rebinding Tool](https://github.com/nccgroup/singularity)[rbndr DNS Rebinding Service](https://github.com/taviso/rbndr)[DNS Rebinding Attacks Explained](https://unit42.paloaltonetworks.com/dns-rebinding/)[CURLOPT_RESOLVE Documentation](https://curl.se/libcurl/c/CURLOPT_RESOLVE.html)OWASP SSRF Prevention Cheat Sheet
CVSS v4.0: 7