Oct 8, 2026·8 min read·1 visit
Ghost CMS versions 6.54.1 through 6.64.0 are vulnerable to unauthenticated blind SSRF due to a validation bypass in the favicon resolution flow. Attackers can leverage this to make HTTP requests targeting private and loopback networks.
An unauthenticated Server-Side Request Forgery (SSRF) vulnerability exists in Ghost CMS from version 6.54.1 to 6.65.0. The vulnerability stems from a validation bypass in the favicon resolution logic within the bookmark-fetching subsystem, which allows remote, unauthenticated attackers to trigger arbitrary HTTP requests to the local host and internal networks. This bypass circumvents the custom DNS-level IP blocklist controls configured globally in the application.
Ghost CMS relies on feature-rich metadata parsing to support administrative features such as incoming Webmentions and dynamic bookmark previews. When a user submits an external URL to be added as a bookmark, the core backend services parse the destination webpage's DOM to extract Open Graph metadata, oEmbed structures, and favicon resources. This functionality inherently exposes the server to Server-Side Request Forgery (SSRF) risks, as the application is directed to retrieve network assets from user-controlled inputs.
To mitigate these risks, Ghost deploys a hardened global HTTP helper, externalRequest. This helper registers hook validation routines that resolve target domain names and match them against blocklists. This connection-time validation blocklist targets loopback (e.g., 127.0.0.1), RFC 1918 private IP blocks (e.g., 10.0.0.0/8), and link-local structures (e.g., 169.254.169.254). Any connection attempt resolving to these private ranges is aborted at the socket initialization phase before any HTTP traffic is sent.
However, a critical vulnerability arose because the application delegated favicon extraction to a third-party library, metascraper-logo-favicon. Rather than utilizing Ghost's central HTTP helper, this library relied on its default package execution routine, which instantiated a standalone, uninstrumented network client (reachable-url). Because this library bypassed the application's central connection hooks entirely, it created a structural gap in the network security architecture, making the platform susceptible to SSRF.
The primary architectural flaw resides in the separation of HTTP clients within the same Node.js execution loop. While Ghost’s core services are wired to route outgoing requests through the customized Got instance wrapped by externalRequest, the metadata scraper executed in a separate modular sandbox. By invoking metascraper-logo-favicon with its default configuration, Ghost permitted the library to perform its own endpoint reachability checks.
Under the hood, metascraper-logo-favicon calls reachable-url, which depends on got v11. Since got v11 was initialized in the library scope, it did not inherit the application-level dnsLookup validation callback designed by Ghost's security team. When a bookmark input contained references to internal endpoints, the library attempted to resolve and connect directly to the destination without examining the resolved IP address.
This behavior matches the class of Time-of-Check Time-of-Use (TOCTOU) weaknesses, where validation is performed on one resource (such as checking the initial bookmark URL), but a separate subsystem accesses an unvalidated relative path (such as a favicon endpoint) without equivalent boundary checks. The sequence of execution can be modeled visually below.
Prior to the fix, Ghost initialized metascraper-logo-favicon in oembed-service.js with only basic options. This allowed the module to execute the network requests utilizing its built-in resolver. The vulnerability was mitigated in version 6.65.0 by overriding the third-party network resolution logic.
The patch implemented a custom resolveFaviconUrl helper within the OEmbedService class, passing this helper as a custom configuration parameter to the constructor of metascraper-logo-favicon. This ensures that all outbound favicon verification requests route directly through Ghost's central externalRequest instance, thereby enforcing connection-time DNS filtering.
// From: ghost/core/core/server/services/oembed/oembed-service.js
// Overriding the default resolver with a secure alternative
/**
* Requests a URL through this.externalRequest and resolves with the response
* and at most the first chunk of its body, aborting the rest of the download.
*/
fetchFirstChunk(url) {
return new Promise((resolve, reject) => {
const stream = this.externalRequest.stream(url, {
headers: {
'user-agent': USER_AGENT,
range: 'bytes=0-0',
},
timeout: {
request: DEFAULT_REQUEST_TIMEOUT,
},
decompress: false,
throwHttpErrors: false,
});
let response;
stream.on('response', (res) => {
response = res;
});
stream.once('data', (chunk) => {
stream.destroy(); // Abort downloading larger payloads to mitigate resource exhaustion
resolve({ response, chunk });
});
stream.once('end', () => resolve({ response }));
stream.on('error', reject);
});
}The implementation of fetchFirstChunk incorporates a critical defense-in-depth pattern. It defines a byte range limitation (range: 'bytes=0-0') in the HTTP request headers and terminates the socket connection by calling stream.destroy() upon receipt of the first chunk of data. This prevents attackers from executing a secondary Denial-of-Service (DoS) vector by forcing the CMS server to download exceedingly large mock binary objects masqueraded as favicons.
Additionally, the patch implements strict verification on the data returned:
if (contentTypes) {
const contentType = response.headers['content-type']?.split(';')[0].toLowerCase();
if (!contentType || !contentTypes.some((ct) => contentType.includes(ct))) {
return undefined;
}
// An empty body or one starting with '<' (ASCII 60) is HTML markup, not an image
if (!chunk?.length || chunk[0] === 60) {
return undefined;
}
}By checking whether the first byte is equivalent to ASCII character 60 (<), the application ensures that HTML documents returned by internal interfaces (such as diagnostic or administrative consoles) are not parsed as image layouts. This breaks timing side-channels and limits structural data-leak potential. This remediation is robust and complete because it fully addresses the network isolation model by routing all traffic through the authenticated hook framework.
To trigger the vulnerability, an attacker does not require administrative or author-level credentials. Any endpoint processing external input for previews or webmentions can be used as the entry point.
The attack begins when the attacker deploys a malicious external page to host a custom DOM structure. This index file acts as a redirection matrix. The attacker inserts a favicon declaration that points to a local or non-routable resource, as shown in this HTML payload:
<!DOCTYPE html>
<html>
<head>
<title>Proof of Concept - CVE-2026-105647</title>
<link rel="icon" href="http://169.254.169.254/latest/meta-data/" type="image/png">
</head>
<body>
<h1>Ghost CMS Exploit Target Page</h1>
</body>
</html>Next, the attacker submits the URL of this external server to the target Ghost CMS instance via a bookmark-generating endpoint. When Ghost executes the metadata lookup, it contacts the attacker-controlled server, retrieves this HTML source, and executes the favicon parsing module.
Because of the lack of input sanitization in the unpatched library, reachable-url tries to establish a direct connection to http://169.254.169.254/latest/meta-data/. The underlying operating system initiates a TCP socket connection to the cloud metadata address, bypassing Ghost's validation policies. Although the application does not return the internal content in the HTTP response, the attacker can observe connection latency variations and HTTP error messages to map internal port state or verify the existence of local service configurations.
This vulnerability is classified as a Blind Server-Side Request Forgery (CWE-918). It receives a CVSS v3.1 base score of 4.0, representing Medium severity. The attack complexity is rated as High because exploiting this blind state for direct host compromise requires structured conditions, such as exploiting insecure local endpoints or leveraging DNS rebinding to execute complex state changes.
The most acute threat presented by this vulnerability applies to Ghost CMS environments hosted on public cloud infrastructure (such as AWS EC2, Google Cloud Platform, or DigitalOcean Droplets). If the hosting environment does not mandate AWS IMDSv2 (which enforces token-based session parameters and prevents unauthenticated headers), an attacker could query IMDSv1 to retrieve metadata or credentials.
Additionally, the blind SSRF allows attackers to scan local ports. By supplying an array of custom links pointing to internal ports (e.g., http://127.0.0.1:8080/), the attacker can monitor the HTTP return code timing. Active ports will close connections or return content-types immediately, while closed ports will reject connections, establishing a precise side-channel map of private network services.
The principal resolution for CVE-2026-105647 is upgrading the application suite directly to Ghost v6.65.0 or higher. This release integrates the secure favicon lookup helper, forcing outbound connections to be evaluated against connection-time DNS constraints. Organizations using npm packages must verify that sub-dependencies of oembed-service do not reference the unhardened module directly.
For enterprise clusters unable to apply immediate patch sets, network isolation should be established using OS-level controls. Network access policies must prevent the user running the Node.js application process from establishing outbound connections to RFC 1918 addresses or cloud metadata endpoints. This can be configured on Linux servers using iptables:
# Block the Node.js user from querying the link-local metadata endpoint
iptables -A OUTPUT -m owner --uid-owner ghost_user -d 169.254.169.254 -j REJECT
# Block the Node.js user from accessing local host administrative ranges
iptables -A OUTPUT -m owner --uid-owner ghost_user -d 127.0.0.0/8 -j REJECTFurthermore, routing all external application queries through an egress forward proxy, such as Squid, provides an additional layer of security. The proxy should be configured with ACL rules that block requests targeting loopback addresses and private subnets, ensuring that application bypasses cannot reach internal resources.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Ghost TryGhost | >= 6.54.1, < 6.65.0 | 6.65.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-918 / CWE-367 |
| Attack Vector | Network |
| CVSS v3.1 Score | 4.0 (Medium) |
| Exploit Status | PoC / Analytical |
| CISA KEV Status | Not Listed |
| Impact | Low Confidentiality (Blind SSRF) |
The web application attempts to fetch a resource from a remote server but fails to validate the resolved destination IP address, enabling requests to internal resources.
An algorithmic complexity vulnerability (CWE-770) in the Excelize library allows remote attackers to cause resource exhaustion (100% CPU usage) via a crafted Microsoft Excel spreadsheet. This occurs because the look-ahead row index parsing in Rows.Columns() fails to enforce upper boundary limits, enabling an out-of-bounds row index to trigger an infinite seek loop inside the Rows iterator.
A resource allocation vulnerability (CWE-770) in lz4-java before version 1.11.4 allows an unauthenticated remote attacker to trigger CPU exhaustion and high garbage collection overhead by streaming empty concatenated LZ4 frames.
A Denial of Service (DoS) vulnerability exists in the yawkat fork of lz4-java prior to version 1.11.4. Under specific non-default configurations (stopOnEmptyBlock = false), parsing crafted streams with a large sequence of contiguous empty LZ4 blocks triggers uncontrolled recursion inside the LZ4BlockInputStream.refill() method, causing stack exhaustion and thread termination.
CVE-2026-76485 is a critical stack-based buffer overflow vulnerability in the VXLAN OAM (NGOAM) parsing component of Cisco NX-OS Software. The flaw enables an unauthenticated, remote attacker to execute arbitrary code with root privileges or trigger a denial of service on affected Nexus switches. This vulnerability is triggered through crafted packets sent to an IP interface. No workarounds are currently available to mitigate the vulnerability while preserving the NGOAM functionality. Cisco has published software patches to address this flaw.
A local privilege escalation and code execution vulnerability exists in the yawkat fork of lz4-java when extracting its bundled JNI shared library into the system temporary directory. Predictable path derivation and lack of exclusive file creation flags allow a local attacker to hijack library loading via a race condition.
A missing authorization vulnerability in Langflow versions 1.0.0 through 1.10.0 allows authenticated users (and unauthenticated users in versions prior to 1.7.2) to access private workflow structures and execute graph components by targeting deprecated API endpoints.