Access our demo library to view BeyondTrust products in action.
Learn More Learn MoreComplete your PAM journey with detailed guidance, hands-on capability checklists, and more.
Learn More Learn MoreLearn why Gartner® has named BeyondTrust as a PAM Leader once again.
Learn More Learn MoreExplore how customers are using our solutions to advance security and productivity.
Learn More Learn MoreOffering a wide array of services and benefits tailored to your specific needs
Learn More Learn MoreLearn how BeyondTrust solutions protect companies from cyber threats.
Learn More Learn MoreAccess our demo library to view BeyondTrust products in action.
Learn More Learn MoreDataverse security usually centers on access controls, roles, and environment governance, but this research examines what a custom .NET plugin could expose from inside a Microsoft Dataverse sandbox container.
Microsoft Dataverse lets you deploy custom .NET plugins that run server-side in process-isolated Windows Server containers. We deployed one. Within minutes, we had SYSTEM privileges on the box, debug access to every running process, a production TLS private key for Microsoft's sandbox infrastructure, a live internal Microsoft API key verified against production endpoints, 51 other organization GUIDs from co-located tenants, and 69 proprietary Microsoft DLLs that were never meant to leave that container.
Sometimes learning about Azure feels more like an exercise in archaeology than engineering. This is certainly the case when trying to learn Microsoft’s no-code AI agent building platform: Copilot Studio.
In Part 1 Copilot Studio AI agents security risks, Phantom Labs™ covered Copilot Studio and its underlying architecture, we covered the basics of Copilot Studio and its underlying architecture. To recap: Copilot Studio (formerly Power Virtual Agents) is part of the Power Platform, which was built around Dataverse (formerly the Common Data Service), a direct descendant of the Dynamics CRM data platform (XRM).
The importance of this lineage is that Copilot Studio AI agents can inherit some pretty old, wacky features from the XRM. This blog focuses on one of them: Custom API Plugins.
Plugins were originally built for XRM to allow developers to register .NET assemblies against the platform’s event bus. This allowed for custom business logic in response to system messages triggered by events like record creation, updates, or deletion.
Custom API plugins extended this concept further, allowing developers to define their own custom messages. Once a Custom API Plugin has been registered, Dataverse exposes a full-blown Rest API endpoint to allow these custom messages to be triggered.
For example, a developer could define a new `ApproveExpenseReport` message and upload a .NET assembly to the Dataverse. After which, the Dataverse exposes the following:
```
`POST /api/data/v9.2/new_ApproveExpenseReport`
```
which will send an `ApproveExpenseReport` message to the registered .NET assembly.
Phantom Labs realised that we had many such .NET assemblies sitting in our out-the-box Dataverse environment and we were able to download and decompile the binaries to their source code using XRM Toolbox. After realising we could decompile these .NET plugins and inspect their source code, we became intrigued as to what kind of attack surface these plugins exposed. So we decided to register one.
While this plugin allowed us to probe the environment, we quickly realized that more sophisticated attacks would be harder to execute. Since the plugin requests first hit a load balancer, meaning back-to-back calls were routed to different containers.
Instead, we used the Echo Plugin as a beachhead to setup a command and control (C2) channel via Azure Blob storage. Once active, the container polls an Azure bucket for commands and writes the results back to the C2 bucket, establishing persistent access for the lifetime of the container (~15-30 mins).
Now that we’ve landed on the box as the user “user manager\containeradministrator”, which belongs to the “BUILTIN\Administrators” group. Moreover, inspecting the user’s security profile revealed critical privileges of the user were enabled:
As a member of the Administrators group, we could interact directly with the Service Control Manager (SCM). This allowed us to create custom services and choose which user it will run under.
```
sc create SystemSvc binPath= "cmd.exe /c {COMMAND} > C:\Windows\Temp\output.txt" obj= LocalSystem
sc start SystemSvc
```
This registers and starts a new service called SystemSvc as the LocalSystem aka “NT AUTHORITY\SYSTEM”. Because this isn’t a proper service implementation, it ultimately errors out—but not before running our chosen command as SYSTEM and writing the output to a file.
Now that we've escalated to SYSTEM on the box, let's do some fun things 😈.
SeDebugPrivilege is enabled by default, giving us debug access to every running process. Outbound HTTPS is completely unrestricted: no egress filtering, no proxy, no DLP. Anything we find, we can send straight to the internet.
Next, we pulled the certificate store. The container held a TLS certificate and its corresponding RSA 2048-bit private key for `wus107.prd.sbx.dynamics.com`, a Microsoft-internal sandbox infrastructure hostname that doesn't resolve in public DNS. The private key is marked exportable and came out with a simple `certutil` call. This result was uploaded to our Azure Blob bucket over unrestricted outbound HTTPS.
We also found a self-signed worker certificate with a `Plex_Nonce` embedded in the Common Name. We'll come back to what that nonce is and why it matters shortly.
The Dataverse container runs a single critical process: `Microsoft.CDSRuntime.SandboxWorker.exe`, the gRPC worker responsible for executing plugins. With SeDebugPrivilege, we could read its entire address space.
We scanned 20 MB of the worker's memory and found 52 GUIDs that we can confirm are Dataverse organization IDs. Our own org ID appeared among them, positively identified via the sidecar's `GetWorkerAssignedMetadata` response (labeled as `CDS/OrganizationId:53c71faa-...`.
Out of the 52 GUIDs, 43 matched exactly with org IDs found in the sidecar's `AppInsightsConnectionString`, a telemetry routing table that maps every organization on the cluster to its instrumentation key. The remaining 9 GUIDs did not appear in the sidecar data, leaving their origins unknown. This cross-org information leak was confirmed with Microsoft on a call.
We also walked away with 46 proprietary Microsoft DLLs comprising the entire Plex infrastructure stack. After decompiling them into roughly 14,000 C# source files using ILSpy, what began as a container escape turned into a full infrastructure audit.
The decompiled source revealed a host-side service called the "sidecar" that the worker connects to on startup, the methods it exposes, the parameters they accept, and how authentication is (or isn't) enforced.
Next, we dumped the container's environment variables. Buried within the standard Windows noise, `sidecarIP` and `SideCarShimEndpointPort` gave us the address of the host-side gRPC sidecar service.
The `Plex_Nonce` is a 128-bit GUID that turned out to be the sole authentication credential required for the worker execution RPC. Meanwhile, `AzureTenantId` contained four Microsoft tenant IDs, including `microsoft.com` corporate (`72f988bf`) and `dynamics.com` (`f8cdef31`). Finally, the `CS_CLUSTER*` variables revealed the infrastructure topology: cluster `prdil107wus`, type `islandcluster`, and region `westus`.
With the decompiled source code, an IP address, a port number, and a stolen nonce, it was time to see what the sidecar had to say for itself.
The decompiled source revealed a four-tier gRPC architecture that Microsoft calls "Plex." We reconstructed the full service definitions from the C# stubs and published them as an OpenAPI specification alongside Go tooling (link below). We also published a website called plex-explorer, documenting the plex architecture.
We called every sidecar method without any credentials. The headline discovery: GetEnvironmentVariables hands back the Plex_Nonce, four Microsoft tenant IDs, the full cluster configuration, and a telemetry routing table mapping all 43 co-located org IDs to their AppInsights instrumentation keys. Additionally, GetOpenIdSigningKeys returns a full JWKS containing RSA public keys and X.509 certificate chains.
Seven write methods also accepted calls without authentication, covering worker lifecycles, crash events, metric injection, and port proxy creation. We also tried testing the extension-loading methods (`LoadExtensionPackage`, `LoadHostInjectionPackage`, `LoadDataverseExtensionAssembly`). None returned data for any input we tried.
Plugins from the same organization share a container pool. We confirmed this by registering a second plugin and observing both executing on the same container, sharing a filesystem and process space.
In production, organizations routinely install third-party plugins from Microsoft AppSource, which lists over 665 Dataverse solutions. Because these plugins handle financial data, PII, CRM records, and authentication tokens, having them co-located next to an attacker-controlled plugin with SYSTEM privileges means a single malicious installation can immediately pivot to intercepting legitimate plugin traffic.
We proved this by using the malicious plugin’s ability to debug process memory and set up an interceptor that periodically scans the workers memory, catching the data going in and out of legitimate plugins co-located on the same container pool.
Our PoC injected a unique marker into a payload sent to another legitimate plugin; the malicious plugin scanned for this marker in memory and pulled the surrounding payload data, confirming that we were able to catch the payload in the worker’s memory.
In a real attack—given the worker has full internet access—the interceptor would likely just periodically dump all the worker’s memory to a blob storage for offline analysis.
We also attempted a DLL cache poisoning attack by replacing a legitimate plugin's cached assembly on the shared filesystem with a trojaned version. Alarmingly, the worker loads whatever binary it finds in the cache without re-verifying signatures. Ultimately, SYSTEM on a shared container opens many doors—memory interception and cache poisoning are just two methods among many potential vectors.
After sharing the findings and writeup to Microsoft, they asked we don't refer to this primitive as a "container escape". Their position is that getting SYSTEM on the box via user supplied code is part of their threat model for sandbox containers. Their exact quote from our discussion:
“As discussed, the container is treated as a hostile, multi-tenant environment within our security model.” --MSRC, June 22 2026
However, when Phantom Labs looked through the Dataverse plugin docs (June 5th 2026) we could not seem to find Microsoft’s portrayal of this security model stated anywhere clearly. Rather, their documentation on how to “write custom Azure-aware plugins” stated the following:
The line “No other external access, such as access to a local file system, is allowed” was dropped sometime after this screenshot was taken. As this was a stated boundary of the security model prior, Phantom Labs stands by the “container escape framing”.
As part of this research, we have released Plex Explorer, a live website detailing the plex architecture and the tooling we used to probe sandbox containers.
Phantom Labs™ researchers "think like attackers" to expose privilege escalation paths and identity attack vectors, helping defenders proactively uncover misconfigurations and detect emerging identity, cloud, and AI security threats in complex hybrid and cloud environments. Using advanced graph modeling, Phantom Labs researchers map attack paths to privileged access across cloud and on-premises infrastructure.
Simon Maxwell-Stewart is a University of Oxford physics graduate with over a decade of experience in the big data environment. Before joining BeyondTrust, he worked as a Lead Data Scientist in healthcare, and successfully brought multiple machine learning projects into production. Now working as a "resident graph nerd" on BeyondTrust's security research team, Simon applies his expertise in graph analysis to help drive identity security innovation.
BeyondTrust Phantom Labs™ believes the best way to fully understand cybersecurity threats is to work closely with our customers and partners, conducting real world research into the attacks that matter most to them. By dissecting emerging attack methods and exploitation techniques of threat actors, as well as conducting novel research, the team’s mission is to help organizations defend against identity threats.