functions/Connect-XdrByEstsCookie.ps1
|
function Connect-XdrByEstsCookie { <# .SYNOPSIS Establishes an authenticated session to the Microsoft Defender XDR portal. .DESCRIPTION Connects to security.microsoft.com using an ESTSAUTHPERSISTENT cookie value to establish an authenticated web session. This function creates global session and headers variables that can be used by other XDR cmdlets to interact with the portal APIs. You can provide the cookie value as either a plain string or as a secure string. .PARAMETER EstsAuthCookieValue The ESTSAUTHPERSISTENT cookie value from an authenticated browser session as a plain string. Use this parameter set when you have the cookie as a plain text value. .PARAMETER SecureEstsAuthCookieValue The ESTSAUTHPERSISTENT cookie value from an authenticated browser session as a secure string. Use this parameter set when you want to pass the cookie value securely (e.g., from credential object). .PARAMETER TenantId The Tenant ID to use for the connection. If not provided, the default tenant will be used. .PARAMETER UserAgent The User-Agent string to use for the web requests. Defaults to Edge browser user agent. .EXAMPLE Connect-XdrByEstsCookie -EstsAuthCookieValue "your_cookie_value_here" Connects to the XDR portal using the provided authentication cookie as plain text. .EXAMPLE $secureCookie = ConvertTo-SecureString -String "your_cookie_value_here" -AsPlainText -Force Connect-XdrByEstsCookie -SecureEstsAuthCookieValue $secureCookie Connects to the XDR portal using the provided authentication cookie as a secure string. .EXAMPLE Read-Host -AsSecureString "Enter ESTSAUTHPERSISTENT cookie" | Connect-XdrByEstsCookie Prompts for the cookie value securely via pipeline and connects to the XDR portal. .OUTPUTS String Returns a confirmation message when successfully connected. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '')] [CmdletBinding(DefaultParameterSetName = 'PlainText')] param ( [Parameter(Mandatory, ParameterSetName = 'PlainText', ValueFromPipeline)] [string]$EstsAuthCookieValue, [Parameter(Mandatory, ParameterSetName = 'SecureString', ValueFromPipeline)] [System.Security.SecureString]$SecureEstsAuthCookieValue, [Parameter(ParameterSetName = 'PlainText')] [Parameter(ParameterSetName = 'SecureString')] [string]$TenantId, [Parameter(ParameterSetName = 'PlainText')] [Parameter(ParameterSetName = 'SecureString')] [string]$UserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0' ) begin { # Clear cache if existing Clear-XdrCache } process { # Convert secure string to plain text if provided if ($PSCmdlet.ParameterSetName -eq 'SecureString') { #$EstsAuthCookieValue = [System.Net.NetworkCredential]::new('', $SecureEstsAuthCookieValue).Password $ssPtr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureEstsAuthCookieValue) try { $EstsAuthCookieValue = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($ssPtr) } finally { [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ssPtr) } } $session = New-Object Microsoft.PowerShell.Commands.WebRequestSession $session.UserAgent = $UserAgent # Bootstrap the session by making an initial request to login.microsoftonline.com $null = Invoke-WebRequest -UseBasicParsing -MaximumRedirection 99 -ErrorAction SilentlyContinue -WebSession $session -Method Get -Uri "https://login.microsoftonline.com/error" -Verbose:$false $cookie = [System.Net.Cookie]::new("ESTSAUTHPERSISTENT", $EstsAuthCookieValue) $session.Cookies.Add('https://login.microsoftonline.com/', $cookie) $SessionCookies = $session.Cookies.GetCookies('https://login.microsoftonline.com') | Select-Object -ExpandProperty Name Write-Verbose "Session cookies: $( $SessionCookies -join ', ' )" # Invoke a GET request to security.microsoft.com to initiate the authentication flow if ($TenantId) { $SecurityPortalUri = "https://security.microsoft.com/" + "?tid=$TenantId" Set-XdrCache -CacheKey "XdrTenantId" -Value $TenantId -TTLMinutes 3660 } else { $SecurityPortalUri = "https://security.microsoft.com/" } Write-Verbose "Initiating authentication flow to $SecurityPortalUri" $SecurityPortal = Invoke-WebRequest -UseBasicParsing -ErrorAction SilentlyContinue -WebSession $session -Method Get -Uri $SecurityPortalUri -Verbose:$false # Error handling for missing for edge cases if ( $SecurityPortal.InputFields.name -notcontains "code" ) { try { $SecurityPortal.Content -match '{(.*)}' | Out-Null $SessionInformation_SecurityPortal = $Matches[0] | ConvertFrom-Json } catch { throw "Failed to complete authentication flow. Please verify the ESTSAUTHPERSISTENT cookie value." } if ($SessionInformation_SecurityPortal.sErrorCode -eq "50058") { throw "Session information is not sufficient for single-sign-on. Please use a incognito/private browsing session to obtain a new ESTSAUTHPERSISTENT cookie value." } elseif ($SessionInformation_SecurityPortal.sErrorCode) { throw "Authentication flow failed with error code: $($SessionInformation_SecurityPortal.sErrorCode). Please verify the ESTSAUTHPERSISTENT cookie value." } else { throw "Authentication flow failed. Please verify the ESTSAUTHPERSISTENT cookie value." } } $requiredFields = @("code", "id_token", "state", "session_state", "correlation_id") Write-Verbose "Input fields received: $($SecurityPortal.InputFields.name -join ', ')" # Check if all required fields are present in returned input fields foreach ($field in $requiredFields) { if (-not ($SecurityPortal.InputFields.name -contains $field)) { $SecurityPortal.Content -match '{(.*)}' | Out-Null $SessionInformation = $Matches[0] | ConvertFrom-Json Write-Verbose "Session information received: $($SessionInformation | ConvertTo-Json -Depth 5)" throw "Required field '$field' is missing from the response." } } $SessionCookies = $session.Cookies.GetCookies('https://security.microsoft.com') | Select-Object -ExpandProperty Name Write-Verbose "Session cookies: $( $SessionCookies -join ', ' )" Write-Host "Successfully signed into to XDR portal using ESTSAUTHPERSISTENT cookie." Write-Host "Exchange the received authorization code for session cookies." # Invoke a POST request to get the session cookies for security.microsoft.com $Body = @{ code = $SecurityPortal.InputFields | Where-Object { $_.name -eq "code" } | Select-Object -ExpandProperty value id_token = $SecurityPortal.InputFields | Where-Object { $_.name -eq "id_token" } | Select-Object -ExpandProperty value state = $SecurityPortal.InputFields | Where-Object { $_.name -eq "state" } | Select-Object -ExpandProperty value session_state = $SecurityPortal.InputFields | Where-Object { $_.name -eq "session_state" } | Select-Object -ExpandProperty value correlation_id = $SecurityPortal.InputFields | Where-Object { $_.name -eq "correlation_id" } | Select-Object -ExpandProperty value } Write-Verbose "POST Headers: $($Headers | Out-String)" $null = Invoke-WebRequest -UseBasicParsing -ErrorAction SilentlyContinue -WebSession $session -Method Post -Uri $SecurityPortalUri -Body $Body -Verbose:$false $SessionCookies = $session.Cookies.GetCookies('https://security.microsoft.com') | Select-Object -ExpandProperty Name Write-Verbose "Session cookies: $( $SessionCookies -join ', ' )" Write-Host "Successfully obtained XDR session cookies." # Save session and headers in script scope Set-XdrConnectionSettings -WebSession $session } } |