This time we will try to understand managed identities.
Next time, i will try to understand it better from more offensive side đ
So, what is this all about?
Letâs recap applications and service principals first.
The relationship between an application and a service principal is one to many.
Application is an instance you create in one tenant, and it has a service principal associated with it.
If a client (with a different tenant) wants to use your application, it will create a service principal instance for your application in its own tenant.
The best way to explain this is using a diagram:
The service principal only exists in your tenant. You can assign permissions to it, add certificate or secret for it and in some cases, authenticate with it!
The permissions you give to the SP in your tenant, are the permissions the associated app (local or external) has in your tenant.
This happens by default with Microsoft applications, in the diagram you can see the SharePoint application. This application resides in MS tenant, but it has a SP that represents it on every client tenant. This type of apps (MS apps), called âFirst-Partyâ applications.
This is nice, but what has to do with managed identities?
So managed identities are basically a sub-type of service principals.
The purpose is to allow applications to acquire tokens (MS Entra tokens), without need to manage any credentials.
Managed identities are linked to resources. Letâs understand what the means:
We have 2 resources:
- VM
- DB
If my VM wants to access the DB and read its content, it needs to have read permission on it.
- Note: since these are resources, and not Entra objects, we are talking about RBAC permissions
If the application on our VM have the right permissions, it still needs to acquire a token that will allow it to access the other resource â the DB.
Here we can use managed identities!
Letâs build a diagram here as well:
The managed identity here is linked to the VM resource.
If the resource will be deleted, so does the MI.
- This is only True for System assigned managed identity. We will talk about it in a minute.
Just to make it clearer, letâs use Graph API to read managed identity in our tenant, and see its attributes.
This is interesting, when you search the MS documentation for managed identity âGETâ query, you see this:
GET https://graph.microsoft.com/beta/external/authorizationSystems/{id}/microsoft.graph.azureAuthorizationSystem/associatedIdentities/managedIdentities/YWJkNjM1ZTUtNTUyOC00NTY1LThjYWYtZjJjNjBmNGY4MGY4
It feels a bit weird, what âexternalâ has to do with that and what it âauthorizationSystems/idâ?
I donât know to answer these questions, but I do know, this is not the way to get managed identities. The was is actually very simple!
We just need to perform the good old âservicePrincipalsâ query, and filter for âManagedIdentityâ.
https://graph.microsoft.com/beta/servicePrincipals?$filter=servicePrincipalType+eq+'ManagedIdentity'
Now we can see what managed identity looks like.
{
"id": "8c7d7ffb-cd90-4d9e-beca-b73e60ca3f01",
"deletedDateTime": null,
"accountEnabled": true,
"alternativeNames": [
"isExplicit=False",
"/subscriptions/84945766-aaff-413d-bf94-2e9743f7693a/resourcegroups/sapir_resource_group/providers/Microsoft.Compute/virtualMachines/sapir-vm"
],
"createdDateTime": "2024-07-13T09:22:22Z",
"deviceManagementAppType": null,
"appDescription": null,
"appDisplayName": null,
"appId": "f5c8f961-4667-4a12-afdc-e58090afffd9",
"applicationTemplateId": null,
"appOwnerOrganizationId": null,
"appRoleAssignmentRequired": false,
"description": null,
"disabledByMicrosoftStatus": null,
"displayName": "sapir-vm",
"errorUrl": null,
"homepage": null,
"isAuthorizationServiceEnabled": false,
"isManagementRestricted": null,
"loginUrl": null,
"logoutUrl": null,
"notes": null,
"notificationEmailAddresses": [],
"preferredSingleSignOnMode": null,
"preferredTokenSigningKeyEndDateTime": null,
"preferredTokenSigningKeyThumbprint": null,
"publisherName": null,
"replyUrls": [],
"samlMetadataUrl": null,
"samlSLOBindingType": "httpRedirect",
"servicePrincipalNames": [
"f5c8f961-4667-4a12-afdc-e58090afffd9",
"https://identity.azure.net/dfCNK0Uw80pL0J60p5CfdsiKkChL/iqhXbfzT68pI1E="
],
"servicePrincipalType": "ManagedIdentity",
"signInAudience": null,
"tags": [],
"tokenEncryptionKeyId": null,
"certification": null,
"info": null,
"samlSingleSignOnSettings": null,
"addIns": [],
"api": {
"resourceSpecificApplicationPermissions": []
},
"appRoles": [],
"keyCredentials": [],
"publishedPermissionScopes": [],
"passwordCredentials": [],
"resourceSpecificApplicationPermissions": [],
"verifiedPublisher": {
"displayName": null,
"verifiedPublisherId": null,
"addedDateTime": null
}
}
Something thatâs worth mentioning, you can see that this servicePrincipal has an âappIdâ value. But if you try to look for it in your tenant, youâll see it does not exist.
Managed identities are not tied to applications, they are tied to resources, so the GUID you see in the appId field, is just a random generated GUID meant to support the fact that ServicePrincipals objects always have âappIdâ associated with them.
There are 2 types of managed identities:
- System assigned
- User assigned
System assigned MI is always bound to only one resource.
User assigned MI can be bound to multiple resources. The idea here is that if you have multiple resources that require the same permissions set, you can assign the same managed identity to all of them.
Resources can have both, it can always have 1 system assigned MI, and several users assigned MI.
With user assigned MI, you first create the MI, assign permissions to it, and then assign it to any resource you want.
With system assigned MI, you just need to check the âstatusâ to âonâ.
If we want to know if a MI is user assigned or system assigned, we can check the âalternativeNamesâ.
Example:
/subscriptions/84945766-aaff-413d-bf94-2e9743f7693a/resourcegroups/sapir_resource_group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/sapir_ua_mi
First thing I thought about was, can I add credentials to managed identities and sign-in with them? As we know this is optional for Service Principals.
Well. The answer here is no.
Ok, it was worth a try.
Now, the next interesting question is âHow managed identities acquire tokens?â and âWhat does a managed identity token look like?â
Letâs answer those (:
I think it’s easier to first look at the token.
Iâm going to get a token for a managed identity tied to a VM resource, since:
- Itâs probably the easiest
- Itâs the most documented approach
To get a resource token, you need to be on the resource, so only from the VM I will be able to acquire the token (We will understand it better after we speak about token acquiring process)
So, I need a VM with Managed Identity enabled status. I’ll start with the system assigned MI.
When creating the VM, I ticked the checkbox for manage identity.
Now, as I logged in to the VM, I can authenticate with the managed identity in multiple ways.
I can run the following command:
Connect-AzAccount -Identity
I can also create a web request:
$response = Invoke-WebRequest -Uri 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/' -Method GET -Headers @{Metadata="true"}
This request asks for a token to the âmanagement.azure.comâ resource.
It requests the token from the following IP: 169.254.169.254
This is known as the IMDS (Azure Instance Metadata Service)
This instance is used to manage virtual machines.
I received the following token:
{ "typ": "JWT",
"alg": "RS256",
"x5t": "MGLqj98VNLoXaFfpJCBpgB4JaKs",
"kid": "MGLqj98VNLoXaFfpJCBpgB4JaKs"
}.
{"aud": "https://management.azure.com/",
"iss": "https://sts.windows.net/eba5ea9a-b43a-4fab-af1d-f245e133078d/",
"iat": 1720866122,
"nbf": 1720866122,
"exp": 1720952822,
"aio": "E2dgYFgatTRLqeZzTkV17vLTBR8eAAA=",
"appid": "f5c8f961-4667-4a12-afdc-e58090afffd9",
"appidacr": "2",
"idp": "https://sts.windows.net/eba5ea9a-b43a-4fab-af1d-f245e133078d/",
"idtyp": "app",
"oid": "8c7d7ffb-cd90-4d9e-beca-b73e60ca3f01",
"rh": "0.ARMBmuql6zq0q0-vHfJF4TMHjUZIf3kAutdPukPawfj2MBMTAQA.",
"sub": "8c7d7ffb-cd90-4d9e-beca-b73e60ca3f01",
"tid": "eba5ea9a-b43a-4fab-af1d-f245e133078d",
"uti": "xqdVPppWUEa7Iq_f5eqnAA",
"ver": "1.0",
"xms_idrel": "22 7",
"xms_mirid": "/subscriptions/84945766-aaff-413d-bf94-2e9743f7693a/resourcegroups/sapir_resource_group/providers/Microsoft.Compute/virtualMachines/sapir-vm",
"xms_tcdt": "1720437592"
}.[Signature]
After converting the expiration time, it’s clear that this token is available for a day, which is quite different from users’ AT, which normally expires after 75 minutes (on average).
Another thing that is different, is that MI doesnât have refresh tokens. The application refreshes its AT every day.
If we check the sub, we will see the servicePrincipalId of our managed identity.
Another new fun attribute is the âxms_miridâ, this is the managed identity resource id identifier.
BTW, this is also one of the âalternativeNameâ of this SP.
One thing I tried to do is to ask for a token to different services.
I tried AADGraph and MSGraph.
I was able to get the tokens, but once I tried to use the token to call AADGraph, I got an error:
â The user identity header is invalid.â
When I assigned my managed identity reader permission on my subscription, I was able to use the token I obtained for the resource manager and query all the possible information.
This understanding, alongside this great article by Roee Sagi from 2021 (!!) https://orca.security/resources/blog/azure-ad-iam-part-ii-leveraging-managed-identities-for-privilege-escalation/ , highlights the fact that access to some resources with managed identity can accomplish privilege escalation.
It all depends on the RBAC of that MI, therefore, here is a code that reads all the MI, and prints their RBAC and some extra information.Â
Then you can search the table for the permissions that you want, and see if you can get them by using the associated resource!
az login
$sps = az ad sp list --filter "servicePrincipalType eq 'ManagedIdentity'" | ConvertFrom-Json
foreach($sp in $sps)
{
Write-Host "Managed Identity:`r`n Id: $($sp.id)`r`n Name: $($sp.displayName)"
$rbac = az role assignment list --assignee $sp.id | ConvertFrom-Json
foreach($r in $rbac)
{
Write-Host "RBAC:`r`n $($r.roleDefinitionId)`r`n $($r.roleDefinitionName)`r`n $($r.scope)"
}
Write-Host "`r`n==========================================================`r`n"
}
output:
Leave a comment