Copilot Studio Attack Paths | Detection Engineering

 

Introduction


With each service and platform Microsoft adds the underlying core components such as IAM, and any associated security boundaries grow murkier and more complex. Recently I noticed a tweet describing how the Power Platform Administrator role in Entra is not listed as Privileged. This is not intuitive because that role is afforded the explicit permission to perform any action within the Microsoft.Dynamics.CRM namespace.


This namespace now includes services such as Copilot Studio. Copilot Studio affords administrators a low-code no-code experience into custom language model applications such as automated email summary workflows. Copilot Studio features are relatively exhaustive and allow administrators to connect a broad range of Microsoft services together into a single execution environment. It also facilitates cross product communication into AWS or other third party apps. As long as the administrator can instrument the HTTP endpoint it can be built in Copilot Studio. 



Importantly, beyond the general notion that an administrator wouldn't expect Power App permissions to expose attack paths to Copilot Studio, those administrators that do have a deeper understanding of the Microsoft control and data planes would intuit that they are afforded a degree of separation in that the underlying ‘Dataverse’ systems utilise separate IAM controls to Entra; Such that being a user in Entra does not afford you access to Dataverse environments and both the user object and a set of permissions must be directly attributed inside Dataverse. So a typical administrator understands that Dataverse has its own set of permissions and role extensions and its own architecture design removed from Entra.



What this article demonstrates is how an Entra user with the role can ultimately enable an attack path into on-premise environments and beyond thanks to the general purpose nature of Copilot Studio.



Maximising potential



Lateral Movement

Once you have successfully phished an Entra user with the Power Platform Administrator Entra role you can pivot into dataverse environments and therefore establish Copilot Studio attack paths by using the ‘self-elevate’ endpoint at 


https://api.powerplatform.com/usermanagement/environments/$environmentId/user/applyAdminRole?api-version=2022-03-01-preview


You can utilise the API directly but the shortest possible path is to simply download the ‘pac cli’ tool and execute the commands:


pac auth create --name abuseme
pac admin self-elevate

Executing the self-elevate function adds the requesting user to the Dataverse IAM with ‘System Administrator’ privileges. The user is assigned the category ‘Unlicensed’ due to the new role. This means that you can not perform business functions like use a Power Apps flow but of course with admin privileges you can assign yourself the necessary components to do so.


Better Evasion


You can also pivot to using a service principal in the attack path as they are much less likely to be monitored for abuse. I'm writing a blog series on developing detection content to solve common blind spots here: https://www.goblinloot.net/2026/08/azure-service-principals-detection.html


pac admin assign-user --environment 'ENVIRONMENT ID' --user 'APP ID' --role "Bot Contributor" --application-user

Importantly, Bot Contributor is the minimally viable permission set to abuse a Copilot Studio attack path as it contains the necessary table and misc permissions. Using a minimally viable permission set will increase the likelihood of avoiding detection. As mentioned earlier Dataverse uses its own IAM system so those unfamiliar can introduce themselves here: https://learn.microsoft.com/en-us/power-platform/admin/security-roles-privileges


Once you have utilised your Entra role to self-elevate you can register your attacker controlled service principal with the endpoint


https://api.bap.microsoft.com/providers/Microsoft.BusinessAppPlatform/adminApplications/${targetAppId}?api-version=2020-10-01"


This endpoint registers your service principal as a ‘Power Platform Management App’ and grants it full access to the remainder of dataverse. The indirect registration of the app is necessary because Microsoft possessed the foresight to prevent apps from registering themselves: https://learn.microsoft.com/en-us/power-platform/admin/powerplatform-api-create-service-principal#registering-an-admin-management-application


Now with an attacker controlled service principal we can begin making our API calls or ‘pac cli’ commands using the service principal credentials avoiding extended use of the initially compromised account..


pac auth create --name abusespn --applicationId 'APP ID' --clientSecret 'SECRET' --tenant TENANT ID'

Of course we can use the API directly for better evasion:


Fetch a token from https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token


And then for each action simply build your API request like so:


DATAVERSE_URL/api/data/v9.2/(DATAVERSE_TYPE)(TARGET_OBJECT_ID)



Enumerate Copilot Studio


To discover potential targets for exploitation we can query individual workflow components with:


DATAVERSE_URL/api/data/v9.2/botcomponents?`$select=name,schemaname,botcomponentid,componenttype,statecode,statuscode,createdon,_parentbotid_value&`$orderby=createdon desc


In the above example I've parsed the response to make it easier for me to read. The details for each component type are documented here: https://learn.microsoft.com/en-us/power-apps/developer/data-platform/reference/entities/botcomponent#componenttype-choicesoptions


Additionally we can query the workflows themselves. Workflows are effectively lists of components:


DATAVVERSE_URL/api/data/v9.2/workflows?`$select=name,workflowid,category,type,statecode,statuscode,createdon,modifiedon&`$orderby=createdon desc



Poison everything


Now that we have the access we need and the context for what's available to abuse we can be as creative as we desire. As workflows utilise system managed connections built into the definition file we cant extract the identity credentials used by the workflow but it is possible to hijack preexisting implementations.


Viewing the inbuilt actions and connections available in all Copilot Studio tenants one particularly dangerous option is listed as ‘Microsoft Defender ATP’. This of course is a connector for a service now dubbed Defender for Endpoint. This connector provides a list of pre-built schemas for interacting with Defender for Endpoint within Copilot Studio. 






Extending our reaching


To start abusing connectors like Defender for Endpoint we must first retrieve a copy of the workflow. Each workflow is comprised of a definition and metadata file which we can retrieve from the endpoint 


DATAVERSE_URL/api/data/v9.2/workflows(WORKFLOWID)


Once retrieved the json file is easiest viewed using graph nodes. In instances where an attacker is fortunate enough to find sensitive details written within workflow notes, those notes are available in cleartext and can be immediately abused elsewhere.


Take note of the clear text password and other surrounding nodes. These are representations of the following graphical experience:




Once we have the definition file we are free to alter it and include or remove features. In particular we can include the Defender for Endpoint connector. The following process allows for the development of an attacker controlled agent workflow that is considerate of the features available and necessary steps to ensure repeatability:


  1. Download the desired target workflow

  2. View the workflow in a graph node format

  3. Note the workflows structure

    1. Which nodes will always fire

    2. What is the triggering condition

  4. Build a duplicate workflow in an attacker controlled tenant

    1. Apply a HTTP endpoint accepting POST requests

    2. Apply a Defender for Endpoint input schema

  5. Establish a differential between the target workflow and simulated workflow

    1. Overwrite simulated workflow with target workflow details


Any changes applied into a workflow that require a connection to another service must be implemented with the identity you have compromised or with another identity you have moved laterally too. Copilot Studio utilises OAuth authentication flows to facilitate the connections in workflows. 




While building an attacker workflow or hijacking a current target workflow you can include step action settings. Using these step action settings allows easier adjustments of workflows without causing errors that may be noticed by administrators.


{
  "runAfter": {
    "connector-{connector_id}": [
      "Succeeded",
      "Failed",
      "TimedOut",
      "Skipped"
    ]
  }
}

  




This will ensure continuity between action steps and prevent your changes breaking the legitimate workflow potentially raising an alarm.


Upload and Abuse


Now we have made the necessary alterations to the Copilot Studio workflow we can upload the changes using the below endpoint by rebuilding the workflow as a json payload from the file and sending it in the body of the request.


Invoke-RestMethod -Uri "DATAVERSE_URL/api/data/v9.2/workflows(TARGET_WORKFLOWID)" -Method Patch -Headers $headers -Body $body

Using our newly established HTTP endpoint we simply make requests to it with the appropriate payload and elicit the newly added Defender for Endpoint connector to perform actions on hosts or servers for us. In this demo ive elicited defender for endpoint to retrieve sensitive files for me via the Copilot Studio workflow


Of course now we have a foothold in Copilot Studio workflows we can also unisolate assets in Defender for endpoint whenever we trigger alerts on-premise.


Threat Detection


As this attack path extends across numerous services no single log source provides a clear picture as to the actions being undertaken or whether they carry any severity when viewed in continuity with each other; however for each surface touched a wealth of logs are available.


List of all relevant log sources (links):


When working with clients to deliver new detection content for deployed AI agents it is extremely rare for even a single one of the operation types within any of the listed sources to be collected let alone correlated. Now that AI services are being built regularly these data sources are considered tier zero.



Closing


This article does not demonstrate any distinct vulnerability or issue in the described products but rather highlights the core problem with Cloud providers and the deliberate interconnections built into the services. The platforms have grown to a degree such that no single person can capture cause and effect in totality within their mind and therefore the potential impact from a single change can often have unintended consequences.

Dataverse and in turn Copilot Studio have a number of controls that prevent or limit exposure to the general ideas in this article but they are not enabled by default and Microsoft doesn't not afford its users a aggregated documentation resource to the broad objective of securing these surfaces. It's likely they will in the future as we have seen with other products such Microsoft Intune where a nations state will eventually be exposed abusing it and suddenly more energy is expended to bring clarity to customers.

If you want to learn about securing Dataverse the key phrase is Centre of Excellence. https://learn.microsoft.com/en-us/power-platform/guidance/coe/overview

Analytics

I've captured below some introductory analytics, at a minimum these should be inserted into your SIEM and used to hunt and understand your data. Later I will publish analytics more directly associated with strong adversary signals.

Copilot studio workflow agent invocations

CloudAppEvents
| where ActionType == "CopilotInteraction"
| where RawEventData.AgentPlatform == "CopilotStudio"
| where parse_json(tostring(RawEventData.CopilotEventData)).AppHost == "workflow-agents"
| extend Id_ = tostring(ActivityObjects[0].Id)

Defender for Endpoint Live response from inside Copilot studio

CloudAppEvents 
| where ActionType == @"RunLiveResponseApi"
| extend ActivityObjects_0_ApplicationInstance = parse_json(ActivityObjects)["0.ApplicationInstance"]
| where ISP == @"Microsoft Azure"
| extend RawEventData_CommandsString = parse_json(RawEventData)["CommandsString"]
| extend RawEventData_DeviceName = parse_json(RawEventData)["DeviceName"]
| project TimeGenerated, AccountDisplayName, IPAddress, RawEventData_DeviceName, RawEventData_CommandsString

Dataverse environment set to admin mode

PowerPlatformAdminActivity
| extend url_path_ = tostring(Properties.["url.path"])
| extend http_request_method_ = tostring(Properties.["http.request.method"])
| extend powerplatform_analytics_activity_name_ = tostring(Properties.["powerplatform.analytics.activity.name"])
| extend ChangeValue = tostring(parse_json(tostring(Properties.["powerplatform.analytics.resource.environment.property.change"])).NewValue)
| extend powerplatform_analytics_resource_environment_url_ = tostring(Properties.["powerplatform.analytics.resource.environment.url"])
| where ChangeValue == "AdminMode"

Copilot Studio workflows being invoked from new locations

PowerPlatformAdminActivity
| extend url_path_ = tostring(Properties.["url.path"])
| extend powerplatform_analytics_activity_name_ = tostring(Properties.["powerplatform.analytics.activity.name"])
| where url_path_ == "/copilotflows/agentnodes/invokeDefinition"
| where powerplatform_analytics_activity_name_ == "ApiEndpointCallEvent"
| where EventResult == "Succeeded"
| extend user_agent_original_ = tostring(Properties.["user_agent.original"])
| extend enduser_principal_name_ = tostring(Properties.["enduser.principal_name"])

New Copilot Studio agent registrations

CloudAppEvents
| where ActionType == @"Add service principal."
| where AccountDisplayName == @"Microsoft Copilot Studio agent identity blueprint"

Copilot prompts directly inside agent preview (unavailable to non admins)

CloudAppEvents
| where ActionType == "CopilotInteraction"
| extend Id_ = tostring(ActivityObjects[0].Id)
| extend AppIdentity_ = tostring(RawEventData.AppIdentity)
| extend AgentId_ = tostring(RawEventData.AgentId)
| extend AppHost_ = tostring(parse_json(tostring(RawEventData.CopilotEventData)).AppHost)
| extend RecordType_ = tostring(RawEventData.RecordType)
| extend agent = RawEventData.AgentPlatform
| where AppIdentity_ startswith "Copilot.Studio"
| where AppHost_ == "Copilot Studio"

Service principal used to access dataverse

DataverseActivity
| where OriginalObjectId == "UserSignIn authentication"
| where UserId matches regex @"_[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"

Copilot Studio workflow updated from new address

let WorkflowOverwrite = DataverseActivity
| where ItemType == "Dynamics365"
| where OriginalObjectId has_any ("CommitFileBlocksUpload", "ExportComponentVersionFile", "RetrieveDependentComponentsWithMetadata")
| where ClientIp != "127.0.0.1";
let Auth = union isfuzzy=true SigninLogs, AADServicePrincipalSignInLogs
| project UserPrincipalName, ServicePrincipalId, AppDisplayName, IPAddress, Type;
WorkflowOverwrite
| join kind=leftanti (Auth) on $left.ClientIp == $right.IPAddress

Featured

Goblin Diary #2 - AI Tools for Analysts 🐯

Dont Use AI  Analyst work is built on the human capacity for creativity, memory recall and information gathering and using so called 'AI...

Popular