Our research
CVE-2026-114123: Missing Authentication in the D365 F&O MCP Server Explained
CVE-2026-114123 is a missing-authentication flaw caused by an insecure default in the open-source D365 F&O MCP Server (npm package d365fo-mcp): the official Azure one-click deployment set the API key to an empty value by default, and with an empty key the server let every request through without authentication. Anyone who followed the documented steps and left the optional field blank ended up with a publicly reachable endpoint that served indexed, proprietary X++ source code and metadata from Dynamics 365 Finance & Operations. The issue is rated CVSS 7.5 (high), affects all versions up to and including 1.10.1, is fixed in 1.11.0, and was reported by CyberSec42.
- The Azure template passed an empty API key to the App Service, the server treated "empty" as "authentication off", and it listened on every network interface.
- The read tools were exposed, including search, object info, references and event handlers, along with snippets of indexed X++ source. According to the advisory, no write tools were reachable on this path.
- Rating per the advisory and the CVE record: CVSS 3.1 base score 7.5, vector
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N, CWE-306 and CWE-1188. - What operators should do: set a strong
API_KEY, upgrade to 1.11.0 or later, and add a network-level control in front of the endpoint.
Background
What the server does and what sits in its index.
Go to sectionThe vulnerability
The mechanism in plain terms and in detail.
Go to sectionWho was affected
Versions and deployment paths.
Go to sectionThe fix
What changed and what operators should do.
Go to sectionTimeline
Publicly documented dates only.
Go to sectionThe underlying pattern
Optional authentication as an insecure default.
Go to sectionKey facts
| Item | Detail |
|---|---|
| Identifiers | CVE-2026-114123, GHSA-8764-fh3m-wf7g |
| Product | D365 F&O MCP Server (dynamics365ninja/d365fo-mcp-server), npm package d365fo-mcp |
| Class | CWE-306 "Missing Authentication for Critical Function", CWE-1188 "Insecure Default Initialization of Resource" |
| Severity | CVSS 3.1: 7.5, high (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N) |
| Affected | all versions up to and including 1.10.1 |
| Fixed | 1.11.0, released 13 Aug 2026 |
| Published | GitHub advisory on 12 Aug 2026, CVE record on 21 Oct 2026 (GitHub as CVE Numbering Authority) |
| Credit | Reported by CyberSec42 (GitHub: lwebmedia), credited as reporter on the GitHub advisory |
Background: what the D365 F&O MCP Server is
The Model Context Protocol (MCP) is an open standard that lets AI assistants call tools and data sources. An MCP server receives tool calls from an AI client and answers them from a target system. According to the CVE record, the D365 F&O MCP Server is an MCP server for querying Dynamics 365 Finance & Operations X++ code and metadata. Developers use it so that an AI assistant writing X++ can rely on the real classes, tables, fields and labels of their own system.
To do that, the server builds an index: a symbol database generated from the D365 environment's PackagesLocalDirectory. The project's security policy is explicit about what it contains: X++ source snippets, extension and event-handler wiring, security roles, privileges and duties, and label text, including the organisation's own custom models and not just Microsoft's standard ones. In other words, the index is a company's proprietary business logic in machine-readable form.
The server supports two transports. With stdio, the AI client launches the server as a local process and there is no network endpoint. With the HTTP transport, the server runs as a network service and accepts JSON-RPC requests at /mcp. For HTTP operation the project's README offers a "Deploy to Azure" button that creates an Azure App Service and its configuration from an ARM/Bicep template. That path is the subject of this article. For general hardening advice, see our guide to securing MCP servers.
The vulnerability
In plain terms
Picture an archive room with a combination lock. It ships configured so that an empty combination opens the door, and the setup guide marks the combination field as "optional". Follow the guide, skip that field, and you have a door that opens for anyone standing in front of it. The archive also happens to sit on a public street.
That is how the Azure deployment of the D365 F&O MCP Server behaved up to 1.10.1. The combination is the API key; the public street is the App Service's public hostname.
Technical detail
Three pieces combined, each documented in the advisory down to file and line:
- The template. In
infrastructure/azuredeploy.jsonandinfrastructure/main.bicep, theapiKeyparameter was declared with a default of""and passed unchanged into the App Service'sAPI_KEYsetting. The Azure portal wizard could therefore be completed without ever filling it in. - The middleware.
src/middleware/apiKeyAuth.tstreated an empty or missingAPI_KEYas "authentication disabled" and passed every request through. The middleware runs globally in front of all routes, so with an empty key it did nothing for/mcpeither. - The bind address.
src/index.tsbound the HTTP listener to0.0.0.0whenHOSTwas unset. The template provisioned no Private Endpoint, VNet integration, IP restriction or App Service authentication.
The template also hard-coded MCP_SERVER_MODE=read-only on the App Service, so write tools were not offered on this path. The read tools remained reachable, among them search, get_object_info, batch_get_info, extension_info, security_info, find_references and find_event_handlers. Several of them return the source_snippet field from the index, i.e. excerpts of X++ source. The project's security policy now states explicitly that MCP_SERVER_MODE is a tool-surface partition, not a privilege boundary: it limits which tools are reachable, not who may reach them.
Reading the vector: AV:N, PR:N and UI:N mean the attack works over the network, without credentials and without any user action. C:H denotes a high loss of confidentiality. Integrity and availability are unaffected according to the vector, because no write tools were exposed on this path. We recomputed the 7.5 base score from the formula in the CVSS 3.1 specification.
Schematic flow
- An organisation deploys the server via "Deploy to Azure" and leaves the
apiKeyfield, described as optional, empty. - The App Service runs with
API_KEY=""on its public HTTPS hostname. - A caller who knows that hostname sends a JSON-RPC request to
/mcpwith no key header. - The middleware sees no configured key and hands the request to the tool.
- The tool responds as it would to an authorised user, including source snippets from the index.
We deliberately do not publish exploit code. The description is enough to assess your own deployment and follow the fix.
How we tested
The finding comes from a source-only review of the public repository. As stated in the advisory, we did not access, scan or contact any live deployment of this software, any Azure subscription belonging to another party, or any D365 Finance & Operations environment. At no point did we execute, build or install code from the project. The flow above was traced through the source code, not against anyone's system.
Who was affected
The GitHub advisory lists the npm package d365fo-mcp in all versions up to and including 1.10.1; the CVE record phrases it as "prior to 1.11.0". Both describe the official Azure one-click deployment. The CVE record draws an explicit line: deployments protected by a configured API key, effective upstream authentication, or network restrictions that keep an attacker out are outside this scenario.
| Deployment | Assessment up to 1.10.1 |
|---|---|
stdio (local process of the AI client) | No HTTP endpoint; the described path does not exist |
Azure one-click, apiKey empty, no network or access control of your own | Affected. Read tools reachable without authentication on the public hostname |
Azure one-click with apiKey set | Requests without a valid key are rejected |
| Own network restriction or upstream authentication (Private Endpoint, IP restriction, Easy Auth) | Outside the scenario per the CVE record, provided it actually keeps an attacker from reaching the endpoint |
Two further points come from the maintainer's commit messages. First, according to the maintainer's analysis, a deployment through the bundled Azure DevOps pipeline onto a hand-created App Service without API_KEY was just as exposed; the second fix commit closes that path too. Second, the commit notes that the hostname is not hard to find: it is derived from the resource group name and appears in Certificate Transparency logs.
How much risk an individual deployment carried depends on whether the key stayed empty, whether separate Azure controls were in place, and which models were in the index.
The fix and what operators should do now
What changed in 1.11.0
The fix consists of two commits that shipped in 1.11.0:
- The key is mandatory.
apiKeyis now a required template parameter with a minimum length of 32 characters. The Azure portal wizard cannot be completed without it. - Safe bind without a key. With no
API_KEY, the server now binds to127.0.0.1by default rather than every interface. A forgotten key costs reachability, not confidentiality. - Startup is refused. If the server is asked to listen on a public interface and nothing authenticates it, it does not start. Deployments that authenticate upstream have a documented opt-out,
ALLOW_UNAUTHENTICATED=true. - Clearer docs and tests. The startup banner states the authentication status, the docs now describe read-only mode as a tool selection rather than access control, a
SECURITY.mdwas added, and new tests cover the middleware and the template.
The fix is flagged as a breaking change: an existing App Service without API_KEY will not start after the upgrade. That is intentional; a silent exposure becomes a visible failure.
Recommendations for operators
The steps below are our recommendations. Where they draw on the project's own guidance, that is noted.
| Step | Action |
|---|---|
| 1. Set a key | Before upgrading, store a long random API_KEY in the App Service, for example from openssl rand -hex 32 as the project docs suggest. Otherwise 1.11.0 will not start. |
| 2. Upgrade | Move to 1.11.0 or later. Per the advisory, 1.10.1 is still affected. |
| 3. Assess | If the key was empty and the App Service was reachable without a network restriction of your own, assume the index was readable from outside and assess that internally. |
| 4. Review logs | If App Service HTTP logging was enabled, look for requests to /mcp from unknown addresses. |
| 5. Restrict the network | Put a Private Endpoint or IP restrictions in front of the App Service if your team's addresses are known. The project's security policy recommends the same. |
| 6. Identity over a shared key | Add Easy Auth with Entra ID so every user has their own credentials. Rotate the shared key when someone leaves the project (also per the project's policy). |
Timeline
Only dates that can be verified from public sources are listed.
| Date | Event | Source |
|---|---|---|
| 27 Jul 2026 | Version 1.5.2 released, the state reviewed in the advisory | npm registry, advisory |
| 28 Jul 2026 | Advisory created in the repository | GitHub advisory (API) |
| 29 Jul 2026 | First fix commit authored | GitHub, commit e70bd05 |
| 11 Aug 2026 | Version 1.10.1 released, the last affected version | GitHub release, npm registry |
| 12 Aug 2026 | Both fix commits on the main branch; GitHub advisory GHSA-8764-fh3m-wf7g published | GitHub |
| 13 Aug 2026 | Version 1.11.0 with the fix released | GitHub release, npm registry |
| 20 Oct 2026 | CVE ID reserved by GitHub as CNA | CVE record |
| 21 Oct 2026 | CVE record CVE-2026-114123 published | CVE.org, NVD |
The underlying pattern: optional authentication as the default
Behind CVE-2026-114123 is a pattern common in developer tooling. Locally, a server without authentication is convenient, so authentication is optional. Once the same code ships to the cloud through a template, "optional" turns into an open internet service. It becomes dangerous when an empty secret means "check disabled" and the deployment tooling ships exactly that empty value as its default.
In our walkthrough of the OWASP MCP Top 10, the case touches two classes: missing transport authentication (class 1) and insecure defaults (class 9). For MCP server developers, four rules follow:
- An empty secret means deny. If the key is missing, the server rejects requests or refuses to start; it never passes them through.
- Templates enforce what the docs recommend. A security-relevant parameter in an infrastructure template has no empty default; it is required or randomly generated.
- Bind to loopback until something authenticates. Without authentication, the server listens only on
127.0.0.1. The 1.11.0 fix does exactly that. - A mode is not access control. Read-only mode limits what is reachable, not who can reach it. With confidential data, reading is already the damage.
The flip side for operators: an MCP server with an HTTP transport is a network service with access to your data. Treat it like any other API, with authentication, network restrictions and a close look at what a one-click template actually provisions.
Thanks to the maintainer
We thank the project's maintainer (GitHub: dynamics365ninja) for the coordinated collaboration. The report was handled privately through a GitHub repository advisory. The maintainer fixed the root cause in several places rather than only changing the default, covered an additional deployment path along the way, added tests, published a security policy, requested the CVE ID from GitHub as CNA, and credited CyberSec42 as reporter on the advisory.
Is your MCP server as locked down as you think?
We test MCP servers and AI agents within a scope agreed in writing: who can reach the endpoint, what gets through without authentication, what your deployment templates actually provision, and what data a tool call returns. Every finding comes with reproducible evidence and a concrete fix. More under AI pentest & MCP security.
Request an AI pentestCyberSec42 is an independent technical security assessment, not an accredited certification body, and does not provide legal advice. CyberSec42 is not affiliated with the D365 F&O MCP Server project or with Microsoft. Dynamics 365 is a trademark of Microsoft.
Frequently asked questions
What is CVE-2026-114123?
A missing-authentication flaw caused by an insecure default in the D365 F&O MCP Server. The official Azure one-click deployment left the API key empty by default, and with an empty key the server accepted requests without authentication. The CVE record rates it CVSS 7.5 (high).
Which versions are affected?
Per the GitHub advisory, all versions of the npm package d365fo-mcp up to and including 1.10.1. It is fixed in 1.11.0, released on 13 Aug 2026. Set an API_KEY before upgrading, or the new version will not start on the App Service.
Am I affected if I run the server locally over stdio?
In stdio mode the AI client launches the server as a local process without an HTTP endpoint, so the described path does not exist. The vulnerability is described for the Azure deployment using the HTTP transport. You should still upgrade.
Is Dynamics 365 Finance & Operations itself affected?
No. The CVE record names only the D365 F&O MCP Server. What was exposed was the data the server had indexed from your D365 environment, not the D365 environment itself. The project's security policy explicitly excludes vulnerabilities in D365 F&O.
Was the vulnerability actively exploited?
The public sources, meaning the advisory, the CVE record and the fix commits, make no statement on this. Whether your own deployment was affected can only be judged from your configuration and, where available, your logs.
Further reading
- Research: our publicly documented findings
- The OWASP MCP Top 10, Explained in Practice
- Securing MCP Servers: A Practical Guide
- Service: AI pentest & MCP security
Sources
- GitHub advisory: GHSA-8764-fh3m-wf7g
- CVE.org: CVE-2026-114123
- NVD: CVE-2026-114123
- GitHub: fix commit e70bd05
- GitHub: fix commit 384394a
- GitHub: release v1.11.0
- GitHub: changes between v1.10.1 and v1.11.0
- GitHub: project security policy (SECURITY.md)
- npm: d365fo-mcp
- MITRE: CWE-306
- MITRE: CWE-1188
- FIRST: CVSS v3.1 specification