Requesting Entra ID Tokens with Entra ID SSO Cookies

Jun 27 2025
Share
By: Antero Guy • 6 min read

TL;DR This post explains how to request OAuth tokens and enumerate an Entra ID tenant by using an SSO cookie from a non cloud-joined device.

Introduction

In this post, I’m going to cover a method for requesting Entra ID access tokens using a browser Single Sign-On (SSO) cookie. This is especially useful in scenarios where you’ve compromised a host that isn’t cloud-joined or hybrid-joined. On these types of systems, you won’t have access to Primary Refresh Tokens (PRTs). But if the user is actively signed into Entra ID resources in their browser, you might still be able to retrieve session cookies. From there, you can request Entra ID tokens and use them to enumerate the tenant.

This isn’t a new technique, but I figured it was worth documenting in case others weren’t aware of it. Alternatively, if you’re working with a cloud-joined device, Matt Creel has a great blog that demonstrates how to use PRTs to access and enumerate Entra ID resources. He also covers how to determine if a device is cloud joined or not, I recommend checking it out  here.

Microsoft Cookies and OAuth

When a user signs into Microsoft Entra ID resources through a browser, Microsoft sets several cookies to maintain session state. One of the more interesting ones is ESTSAUTHPERSISTENT, which represents a persistent single sign-on session. If you can extract this cookie from the browser it can be used  as proof of authentication within the OAuth 2.0 Authorization Code flow to obtain Entra ID access and refresh tokens.

Note: On hybrid-joined or cloud-joined devices using PRTs, the ESTSAUTHPERSISTENT cookie will still be present, but it isn’t usable as far as I can tell

The Authorization Code flow is a standard OAuth 2.0 mechanism where a client first requests an authorization code, then exchanges it for tokens that let you interact with Entra ID protected resources like the Microsoft Graph API. I recommend reading Microsoft’s documentation on the Authorization Code flow for more details. 

The remainder of this write-up  walks through that flow using the ESTSAUTHPERSISTENT cookie as authentication material. With valid tokens, you can then use tools like RoadRecon to enumerate the target Entra ID tenant

Performing the Flow with ESTSAUTHPERSISTENT

Before diving into the OAuth 2.0 flow, you’ll need to extract the ESTSAUTHPERSISTENT cookie from the user’s browser. This post doesn’t cover that step in detail, but tools like cookie-monster by KingOfTheNOPs can help with retrieving and decrypting browser cookies from a compromised host.

Once you have a valid cookie, you can begin the OAuth 2.0 Authorization Code flow. This process consists of two steps:

  1. Send a GET request to the /authorize endpoint to obtain an authorization code.
  2. Send a POST request to the /token endpoint to exchange that code for access and refresh tokens.

Let’s start with the authorization request. Make a GET request to the following endpoint:

    https://login.microsoftonline.com/<tenantID>/oauth2/authorize

Include the ESTSAUTHPERSISTENT cookie in the request headers to prove authentication. You’ll also need to include the following parameters in the URL

  • client_id: The public client you want to impersonate (e.g. Azure CLI, PowerShell, etc…). In this example, I will use the client ID for the Microsoft Azure PowerShell application (i.e., 1950a258-227b-4e31-a9cf-717495945fc2). 
  • redirect_uri: This must match the URI registered with the client application
  • response_type: Set this to code
  • response_mode: Set this to query

If the request is successful and the cookie is valid, Microsoft will respond with an authorization code.

With the authorization code in hand, you can now exchange it for tokens. Make a POST request to the following endpoint:

    https://login.microsoftonline.com/<tenantID>/oauth2/token

Include the following parameters in the request body:

  • client_id: Same as the first request
  • redirect_uri: Same as the first request
  • grant_type: Set this to authorization_code
  • code: The authorization code from the previous step
  • resource: The resource you want to access. Since the goal is to use RoadRecon for enumeration, we’ll specify https://graph.windows.net as the resource.

If all goes well, the response will include both an access token and refresh token. These can now be used to access the desired resource in the context of the compromised user.

With a valid access token and refresh token in hand, we can feed them into roadrecon and begin enumerating the tenant for users, groups, applications, and more. Below is an example of how this can be achieved.

roadrecon auth --access-token <access_token>
roadrecon gather

Automating the Flow: GetAccessTokenWithEntraCookie

To automate this process, I built a tool that handles the OAuth authorization code flow using the ESTSAUTHPERSISTENT cookie. It’s available here. With this script, you can request Entra ID tokens to resources within the target tenant using a stolen Entra ID SSO cookie. See below for command line usage 

Note: Requesting an authorization code is still considered an authentication attempt and will be evaluated against Entra ID Conditional Access Policies (CAPs). For example, if a CAP is based on IP restrictions, you may need to proxy this tool into the appropriate network. Alternatively, you can use the BOF included in the GitHub repository.

CLI Usage

python get-AccessTokenWithSSOCookie.py <client_id> <tenant_id> <resource> <estsAuthP>
    - client_id: Public client you want to impersonate (e.g., Azure CLI or Microsoft Teams)
    - tenant_id: Target tenant GUID
    - resource: The resource you're requesting tokens for (e.g., https://graph.windows.net)
    - estsAuthP: The ESTSAUTHPERSISTENT cookie value

Example

Here’s an example of using the tool to impersonate the Azure PowerShell client application to request tokens to the https://graph.windows.net resource:

python3 get-AccessTokenWithSSOCookie.py 1950a258-227b-4e31-a9cf-717495945fc2 00000000-0000-0000-0000-000000000000 https://graph.windows.net 1.AVEA..
Successfully requested an auth code!

Successfully requested an access token code!

access_token: eyJ0e...
access_token: eyJ0e...
id_token: eyJ0e…

Final Thoughts

Hopefully this brief post helps demonstrate the technique and gives you another option to work with on non Entra joined devices. It’s a solid fallback when PRTs aren’t available, and a good way to access and enumerate Entra ID resources. Thanks for reading my blog!