Foundations: Attacking Entra ID & Azure

Before any exploit makes sense you need the map: what Entra is versus Azure, the two separate role systems, how app registrations and service principals fit together, which API is which, and how Conditional Access decides. Written for someone who has never opened the portal.

Easy · Reading · ~60 min
Log in to start the challenges in this room.

Why this room exists

Every write-up you will read about attacking Microsoft's cloud - including the CARTP exam report these labs are built from - assumes you already know a set of words: tenant, service principal, app registration, managed identity, Graph, Conditional Access, RBAC. None of them mean what they sound like, several sound like each other, and getting two of them confused is the usual reason an attack "should work" and does not.

This room is the map. No exploit yet - just the handful of ideas you have to hold in your head for the rest to make sense. Read it once now; come back to it whenever a later step feels like it is using a word you nodded along to.

The single most important idea, the one everything else hangs off:

Microsoft's cloud is really two products stapled together - an identity service (Entra ID) and a resource platform (Azure) - each with its own notion of "who are you" and its own notion of "what are you allowed to do". Almost every escalation lives in the gap between them.

1. Azure vs Entra ID - two products, one login

People say "Azure" for the whole thing. It is actually two.

Entra ID (you will still see its old name, Azure AD, everywhere - they are the same product) is the identity directory. It holds users, groups, and the registrations for applications. It issues the tokens that prove who you are. It is a SaaS product: there is no "server" you get shell on, only an API and a sign-in page. Think of it as the company's phone book plus its bouncer.

Azure (more precisely, Azure Resource Manager, "ARM") is the resource platform - the virtual machines, storage accounts, key vaults, web apps, databases. This is the stuff that costs money and holds data. ARM is a completely separate API (management.azure.com) that happens to trust Entra to say who you are.

The container words matter and are constantly mixed up:

  • A tenant is one instance of Entra ID - one company's directory. It has a

tenant ID (a GUID) and at least one domain like contoso.onmicrosoft.com. Users live in a tenant. Groups live in a tenant. App registrations live in a tenant.

  • A subscription is a billing-and-container boundary inside Azure. Your

VMs and key vaults live in a subscription. A subscription trusts exactly one tenant for identity.

So a tenant can have many subscriptions, and a subscription belongs to one tenant. When you authenticate you are always authenticating to a tenant; what you can then do in Azure depends on your roles in its subscriptions.

A concrete consequence you will hit immediately: a token is minted for one audience. A token good for Entra's Graph API is not good for ARM, and neither is good for a Key Vault's data. Same identity, three different tokens. More on that in section 4.

2. Two role systems that do not talk to each other

This is the one that trips up everyone, because both are called "roles".

Entra roles (directory roles)

These govern Entra objects: users, groups, app registrations. Examples:

  • Global Administrator - owns the entire tenant's identity.
  • Application Administrator - can manage every app registration, including

adding credentials to them. (Remember this one; it is a whole attack.)

  • User Administrator, Groups Administrator, **Privileged Role

Administrator**, and so on.

An Entra role says nothing about a virtual machine or a key vault. Global Administrator - the most powerful identity role there is - has, by default, no access to any Azure resource at all.

Azure roles (Azure RBAC)

These govern Azure resources through ARM. The famous three:

  • Owner - full control, including handing out roles to others.
  • Contributor - full control, except handing out roles.
  • Reader - look, do not touch.

...plus hundreds of narrow built-ins like Key Vault Secrets User, Storage Blob Data Reader, Website Contributor, Virtual Machine Contributor.

An Azure role is always scoped: it applies at a management group, a subscription, a resource group, or a single resource, and it inherits downward. "Reader on this resource group" means reader on everything in it.

The gap between them

Here is the sentence to tattoo on the inside of your eyelids:

An Entra role does not grant Azure access, and an Azure role does not grant Entra access. They are separate graphs of permission over separate things.

The one official bridge: a Global Administrator can flip a switch ("Access management for Azure resources") that grants them User Access Administrator at the root of every subscription - which is an Azure role - and from there they can give themselves Owner anywhere. That switch is a one-way door from the identity world into the resource world, and it is exactly the kind of edge an attacker looks for.

Control plane vs data plane - the last twist

Even within Azure there are two layers, and this catches people constantly:

  • The control plane is ARM: create/read/delete the resource. "I can see

this key vault exists, read its configuration, even delete it."

  • The data plane is the resource's own API: the contents. "I can read the

secrets inside it."

Owner is a control-plane role. On a modern (RBAC-enabled) key vault, Owner lets you manage the vault but not read a single secret - reading secrets needs a data-plane role like Key Vault Secrets User. The first time you see Owner get a 403 on Get-AzKeyVaultSecret it looks like a bug. It is not. (It is also why an Owner can simply grant themselves the data-plane role and then read - control of the control plane is control of who gets the data plane.)


3. Applications, service principals, and the words around them

This is the densest knot in all of Entra, and the CARTP chains live inside it. Take it slowly.

An "application" in Entra is really two objects, and Microsoft shows you each under a different, unhelpful name.

The application object = "App registration"

When a developer registers an app, they create an application object. In the portal this is the App registrations blade. There is exactly one application object, and it lives in the tenant that owns the app (its "home" tenant). It is the global definition of the app:

  • its Application (client) ID - a GUID that identifies the app everywhere
  • its credentials - client secrets and certificates the app uses to prove

it is itself when logging in

  • the API permissions it requests (e.g. "read all users from Graph")
  • whether it is single-tenant or multi-tenant

Think of the application object as the blueprint.

The service principal = "Enterprise application"

To actually use an app inside a tenant, that tenant needs a local instance of it - a service principal (SP). In the portal this is the Enterprise applications blade. The service principal is:

  • the app's identity in this specific tenant - the thing that role

assignments point at, the thing that signs in

  • where the app's actual access lives (its Azure RBAC roles, its Entra roles)

Think of the service principal as the account the app logs in as.

One blueprint, potentially many accounts:

  • A single-tenant app: one application object, one service principal, both

in the home tenant.

  • A multi-tenant app (like a SaaS product): one application object in the

vendor's tenant, and a separate service principal in every customer tenant that consented to it. This is exactly how an "illicit consent grant" attack plants an attacker-controlled identity inside a victim tenant.

The mental model that makes everything click: a user is a person's identity; a service principal is an application's identity. Both are "security principals" - both can hold roles, own objects, and be added to groups. When a role assignment says ObjectType: ServicePrincipal, it is an app logging in, not a person.

Managed identity = a service principal Azure babysits

A managed identity is a service principal with one special property: Azure holds its credential for you. You never see a secret. Instead, code running inside an Azure resource (a VM, a web app, a function, an automation account) can ask a local endpoint for a token, and Azure hands one out proving "I am this resource's identity".

  • System-assigned: born with the resource, dies with it. One resource, one

identity.

  • User-assigned: a standalone identity you can attach to several resources.

Managed identities are the single most common Azure escalation, because "I can run code in this resource" silently becomes "I can act as this resource's identity" - and that identity often has roles the attacker never had. Every CARTP kill chain has one of these in it.

Ownership is a credential factory - the sharp edge

Here is the part that turns all of this into an attack. A security principal can be an owner of an application object. An owner is allowed to add a new credential (client secret or certificate) to that application.

Read that again. If you own an app - or become Application Administrator, or compromise a managed identity that owns the app - you do not need to steal its existing secret. You mint a fresh one. The app keeps working, nothing is revoked, the audit log shows a legitimate owner doing a legitimate thing, and now you can authenticate as that application and inherit everything its service principal can do.

This is the "Service Principal Impersonation" step in the exam report, and it is why reviewing who has a role tells you almost nothing about your real exposure

  • you also have to ask *who can mint a credential for something that has a

role*.


4. The APIs - Graph, Azure AD Graph, and ARM

You will see three different base URLs in commands and tokens. Knowing which is which saves hours, because a token for one returns 401 on another and the error rarely says why.

Microsoft Graph - https://graph.microsoft.com

The current, correct API for Entra ID (and Microsoft 365): users, groups, applications, service principals, addPassword, directory roles. When you read or manipulate identity objects, this is the API. Tokens for it have the audience https://graph.microsoft.com. Get one with, e.g.:

az account get-access-token --resource-type ms-graph

Azure AD Graph - https://graph.windows.net

The old, retired identity API. Microsoft spent years pushing everyone off it, and it is now shut down. You will still see it in older tooling, older scripts, and some CARTP material, because for a long time it exposed things MS Graph did not, and some classic attack scripts targeted it. Treat any reference to graph.windows.net as "this is the legacy path" - reach for Microsoft Graph instead unless a specific old technique needs it.

Do not confuse either of these with "Azure Resource Graph", which is a query service for Azure resources - a completely different thing that just shares the word "graph". Naming in this space is genuinely hostile.

Azure Resource Manager (ARM) - https://management.azure.com

The API for Azure resources: subscriptions, resource groups, VMs, storage, role assignments, deployments. When you enumerate or touch resources (not identities), this is the API. Its tokens have the audience https://management.azure.com. Get one with:

az account get-access-token --resource-type arm

And the data-plane endpoints - each resource type its own audience.

A Key Vault's secrets are not on Graph or ARM; they are on https://<vault>.vault.azure.net, and reading them needs a token whose audience is https://vault.azure.net. Storage data plane is https://storage.azure.com. This is the token-audience point from section 1 made concrete:

# three DIFFERENT tokens for the SAME identity, for three jobs:
az account get-access-token --resource-type ms-graph   # manage identities
az account get-access-token --resource-type arm        # manage resources
az account get-access-token --resource-type keyvault   # read a vault's secrets

When a request 401s and you are sure you have access, the first question is almost always: is this token even for the right audience?


5. Conditional Access - the bouncer's rulebook

Conditional Access (CA) is how Entra enforces security policy at sign-in. A CA policy is, quite literally, an if-then:

IF these signals are true, THEN grant with these requirements (or block).

The signals (the "if") include:

  • who: specific users or groups (all users, guests, one group...)
  • what app: which cloud application they are signing in to
  • conditions: device platform (Windows/iOS/...), location (a country, an IP

range), whether the device is compliant/managed, sign-in risk

The controls (the "then"):

  • Block access outright, or
  • Grant access but require one or more of: MFA, a compliant device,

a hybrid-joined device, an approved app, a password change.

A tenant usually has several policies; all of them evaluate, and the strictest wins. The decision happens when the token is issued, inside Entra - not at the resource. If a policy demands MFA for a given app and you have not done MFA, the token simply is not minted.

Why attackers care - the gaps

CA is powerful but it is a rulebook, and rulebooks have edges. The classic evasions all work by falling outside the "if":

  • A policy scoped to specific apps does not cover apps it did not name.

Authenticate against a different client (a different Azure app ID) and the app-specific policy never fires. The Azure CLI, Azure PowerShell, and a handful of Microsoft "first-party" client IDs are the usual escape hatches.

  • A policy scoped to device platforms or locations does not cover platforms

or locations it did not list. The exam report's CA bypass is exactly this: drive the sign-in from a device-code flow (az login --use-device-code) and present the browser as a phone, so a policy written for desktop platforms never applies.

  • Device-code and other flows move the interactive step onto a device the

policy is not evaluating, which is why device-code phishing is such a durable technique.

The lesson for both attacker and defender: a Conditional Access policy is only as strong as the completeness of its "if". A policy that requires MFA for the Azure portal has said nothing about the Azure CLI. Enforcing a control on some front doors is the same as leaving the others open.

Practice: the seven CARTP chains

Everything above is exercised by seven Azure attack chains built into this lab (azure-labs/), which your instructor stands up in a dedicated training tenant. Each is a real path through a real (intentionally broken) Azure environment, ending at a flag you submit. They are not started from this page like the web challenges in other rooms - Azure objects are not containers - so you run them against the live tenant with az / PowerShell using the account your instructor gives you.

Unless noted, each chain hands you an application whose source contains a client secret - finding it is step zero. Every chain ends at a secret of the form onvio{entra_chainNN_...}.

They are ordered roughly easiest-first. Do them in order the first time; each leans on an idea the previous one taught.

Chain 01 - Group ownership into a Key Vault. The leaked service principal can read nothing directly, but it owns a group, and that group holds Key Vault Secrets User. Add yourself to the group you own, then read the secret. Teaches section 2 (a principal owning a thing is not the same as having its access - until you use the ownership) and section 3 (principals hold roles).

Chain 02 - Automation Account managed identity. The principal has Contributor on an Automation Account and nothing else. Contributor lets you author and run a runbook, and a runbook executes as the account's managed identity - which can read the vault. You borrow an identity you never had. Teaches section 3 (managed identities) end to end.

Chain 03 - App Service, Kudu, and the token endpoint. The principal has Website Contributor on a web app. That grants the Kudu command console; from inside, the app's managed identity is reachable over IDENTITY_ENDPOINT. Extract the raw token and use it from your own machine. Teaches section 4's token audiences and the fact that managed-identity tokens are exfiltratable.

Chain 04 - Anonymous blob, SAS sprawl, deployment history. No credential to start - just a URL. A public blob holds a SAS URI to a private container; that container holds a service-principal credential; that principal has Reader on a resource group, and Reader can read ARM deployment history, where a password was passed as a plain string and sits in clear text forever. Three lessons in one path.

Chain 05 - Application ownership as a credential factory. A vault you can reach holds a user's password. That user owns a privileged app registration. In user context, ownership alone lets you mint a fresh client secret for the app - you never steal the original. Authenticate as the app and inherit its access. This is the Add-AzADAppSecret move from the exam report, and section 3's sharp edge made concrete.

Chain 06 - Owner is not read access, until it grants itself read access. The principal is Owner of a resource group and is refused the Key Vault secret inside it - because Owner is a control-plane role and the vault authorises the data plane separately. But Owner can assign roles, so it grants itself Key Vault Secrets User and then reads the secret. Teaches section 2's control-plane / data-plane split better than any diagram.

Chain 07 - The capstone (from a web shell to the vault). The only chain that starts from a real web bug: a file-upload RCE on a public inventory app. Upload a .phtml web shell, reach the app's managed identity, read a Key Vault holding a user credential, authenticate as that user, mint a secret for an app the user owns, and read the final flag as that service principal. It threads sections 2, 3 and 4 together and rebuilds the spine of the CARTP exam report.


And the exam report itself, in the course material, walks a full nine-step chain that threads all of this together - from a file-upload RCE on a web app to a flag in a key vault. Once the words in this room feel ordinary rather than intimidating, that report reads like a story instead of a wall of GUIDs.

{# A reading room (no challenges) shows only its prose - no empty practice note, which would read as a misconfiguration rather than an intentional choice. #}