From 4421a7390c1442ecec358aaf424b967d807bacb2 Mon Sep 17 00:00:00 2001 From: Lukas Sassl Date: Wed, 26 Jul 2023 12:00:31 +0200 Subject: [PATCH 1/2] Get-NewOAuthToken added --- .../src/CVE-2023-23397/CVE-2023-23397.ps1 | 92 +++++++------------ Shared/AzureFunctions/Get-NewOAuthToken.ps1 | 85 +++++++++++++++++ 2 files changed, 117 insertions(+), 60 deletions(-) create mode 100644 Shared/AzureFunctions/Get-NewOAuthToken.ps1 diff --git a/Security/src/CVE-2023-23397/CVE-2023-23397.ps1 b/Security/src/CVE-2023-23397/CVE-2023-23397.ps1 index d2ee19e8f2..907f522625 100644 --- a/Security/src/CVE-2023-23397/CVE-2023-23397.ps1 +++ b/Security/src/CVE-2023-23397/CVE-2023-23397.ps1 @@ -239,6 +239,7 @@ begin { . $PSScriptRoot\..\..\..\Shared\AzureFunctions\Get-GraphAccessToken.ps1 . $PSScriptRoot\..\..\..\Shared\AzureFunctions\Get-CloudServiceEndpoint.ps1 . $PSScriptRoot\..\..\..\Shared\AzureFunctions\Get-NewJsonWebToken.ps1 + . $PSScriptRoot\..\..\..\Shared\AzureFunctions\Get-NewOAuthToken.ps1 . $PSScriptRoot\..\..\..\Shared\AzureFunctions\Invoke-GraphApiRequest.ps1 . $PSScriptRoot\..\..\..\Shared\Show-Disclaimer.ps1 @@ -440,51 +441,6 @@ begin { return [Microsoft.Exchange.WebServices.Data.Item]::Bind($ExchangeService, $Id, $ps); } - ## function to create OAuth token - function CreateOAUTHToken { - param( - [string]$TenantID, - [string]$ClientID, - [string]$AppSecret, - [string]$AzureADEndpoint, - [bool]$UseCertificateToAuthenticate = $false, - [string]$Scope - ) - - try { - $body = @{ - scope = $Scope - client_id = $ClientID - grant_type = "client_credentials" - } - - if ($UseCertificateToAuthenticate) { - $body.Add("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer") - $body.Add("client_assertion", $AppSecret) - } else { - $body.Add("client_secret", $AppSecret) - } - - $PostSplat = @{ - ContentType = 'application/x-www-form-urlencoded' - Method = 'POST' - - # Create string by joining bodyList with '&' - Body = $body - Uri = "$AzureADEndpoint/$TenantID/oauth2/v2.0/token" - } - - $Token = Invoke-RestMethod @PostSplat - } catch { - Write-Host "`nFailure creating EWS auth token, exiting Program. Please review the error message below and re-run the program:`n`n$_" -ForegroundColor Red - exit - } - - $script:tokenLastRefreshTime = (Get-Date) - - return $Token - } - function GetAzureApplication { param( $AzAccountsObject, @@ -837,11 +793,11 @@ begin { # if token is going to expire in next 5 min then refresh it if ($null -eq $script:tokenLastRefreshTime -or $script:tokenLastRefreshTime.AddMinutes(55) -lt (Get-Date)) { $createOAuthTokenParams = @{ - TenantID = $ApplicationInfo.TenantID - ClientID = $ApplicationInfo.ClientID - AzureADEndpoint = $AzureADEndpoint - UseCertificateToAuthenticate = (-not([System.String]::IsNullOrEmpty($ApplicationInfo.CertificateThumbprint))) - Scope = $EWSOnlineScope + TenantID = $ApplicationInfo.TenantID + ClientID = $ApplicationInfo.ClientID + Endpoint = $AzureADEndpoint + CertificateBasedAuthentication = (-not([System.String]::IsNullOrEmpty($ApplicationInfo.CertificateThumbprint))) + Scope = $EWSOnlineScope } # Check if we use an app secret or certificate by using regex to match Json Web Token (JWT) @@ -860,12 +816,20 @@ begin { exit } - $createOAuthTokenParams.Add("AppSecret", $jwt) + $createOAuthTokenParams.Add("Secret", $jwt) } else { - $createOAuthTokenParams.Add("AppSecret", $ApplicationInfo.AppSecret) + $createOAuthTokenParams.Add("Secret", $ApplicationInfo.AppSecret) } - $Token.Value = CreateOAUTHToken @createOAuthTokenParams + $oAuthReturnObject = Get-NewOAuthToken @createOAuthTokenParams + if ($oAuthReturnObject.Successful -eq $false) { + Write-Host "" + Write-Host "Unable to refresh EWS OAuth token. Please review the error message below and re-run the script:" -ForegroundColor Red + Write-Host $oAuthReturnObject.ExceptionMessage -ForegroundColor Red + exit + } + $Token.Value = $oAuthReturnObject.OAuthToken + $script:tokenLastRefreshTime = $oAuthReturnObject.LastTokenRefreshTime $EWSService.Value = EWSAuth -Environment $Environment -Token $Token.Value -EWSOnlineURL $EWSOnlineURL } } @@ -1110,16 +1074,24 @@ begin { } $createOAuthTokenParams = @{ - TenantID = $tenantID - ClientID = $clientID - AppSecret = $applicationInfo.AppSecret - Scope = $ewsOnlineScope - AzureADEndpoint = $azureADEndpoint - UseCertificateToAuthenticate = (-not([System.String]::IsNullOrEmpty($CertificateThumbprint))) + TenantID = $tenantID + ClientID = $clientID + Secret = $applicationInfo.AppSecret + Scope = $ewsOnlineScope + Endpoint = $azureADEndpoint + CertificateBasedAuthentication = (-not([System.String]::IsNullOrEmpty($CertificateThumbprint))) } #Create OAUTH token - $EWSToken = CreateOAUTHToken @createOAuthTokenParams + $oAuthReturnObject = Get-NewOAuthToken @createOAuthTokenParams + if ($oAuthReturnObject.Successful -eq $false) { + Write-Host "" + Write-Host "Unable to fetch an OAuth token for accessing EWS. Please review the error message below and re-run the script:" -ForegroundColor Red + Write-Host $oAuthReturnObject.ExceptionMessage -ForegroundColor Red + exit + } + $EWSToken = $oAuthReturnObject.OAuthToken + $script:tokenLastRefreshTime = $oAuthReturnObject.LastTokenRefreshTime $ewsService = EWSAuth -Environment $Environment -Token $EWSToken -EWSOnlineURL $ewsOnlineURL } else { #Server diff --git a/Shared/AzureFunctions/Get-NewOAuthToken.ps1 b/Shared/AzureFunctions/Get-NewOAuthToken.ps1 new file mode 100644 index 0000000000..dda6569a76 --- /dev/null +++ b/Shared/AzureFunctions/Get-NewOAuthToken.ps1 @@ -0,0 +1,85 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +function Get-NewOAuthToken { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [string]$TenantID, + + [Parameter(Mandatory = $true)] + [string]$ClientID, + + [Parameter(Mandatory = $true)] + [string]$Secret, + + [Parameter(Mandatory = $true)] + [string]$Endpoint, + + [Parameter(Mandatory = $false)] + [string]$TokenService = "oauth2/v2.0/token", + + [Parameter(Mandatory = $false)] + [switch]$CertificateBasedAuthentication, + + [Parameter(Mandatory = $true)] + [string]$Scope + ) + + <# + Shared function to create an OAuth token by using a JWT or secret. + If you want to use a certificate, set the CertificateBasedAuthentication switch and pass a JWT token as the Secret parameter. + You can use the Get-NewJsonWebToken function to create a JWT token. + If you want to use a secret, pass the secret as the Secret parameter. + This function returns a PSCustomObject with the OAuth token, status and the time the token was created. + If the request fails, the PSCustomObject will contain the exception message. + #> + + begin { + Write-Verbose "Calling $($MyInvocation.MyCommand)" + $oAuthTokenCallSuccess = $false + $exceptionMessage = $null + + Write-Verbose "TenantID: $TenantID - ClientID: $ClientID - Endpoint: $Endpoint - TokenService: $TokenService - Scope: $Scope" + $body = @{ + scope = $Scope + client_id = $ClientID + grant_type = "client_credentials" + } + + if ($CertificateBasedAuthentication) { + Write-Verbose "Function was called with CertificateBasedAuthentication switch" + $body.Add("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer") + $body.Add("client_assertion", $Secret) + } else { + Write-Verbose "Authentication is based on a secret" + $body.Add("client_secret", $Secret) + } + + $invokeRestMethodParams = @{ + ContentType = "application/x-www-form-urlencoded" + Method = "POST" + Body = $body # Create string by joining bodyList with '&' + Uri = "$Endpoint/$TenantID/$TokenService" + } + } + process { + try { + Write-Verbose "Now calling the Invoke-ResthMethod cmdlet to create an OAuth token" + $oAuthToken = Invoke-RestMethod @invokeRestMethodParams + Write-Verbose "Invoke-RestMethod call was successful" + $oAuthTokenCallSuccess = $true + } catch { + Write-Host "We fail to create an OAuth token - Exception: $($_.Exception.Message)" -ForegroundColor Red + $exceptionMessage = $_.Exception.Message + } + } + end { + return [PSCustomObject]@{ + OAuthToken = $oAuthToken + Successful = $oAuthTokenCallSuccess + ExceptionMessage = $exceptionMessage + LastTokenRefreshTime = (Get-Date) + } + } +} From 5de56041c1d117c1539ae07d13cee4012fc77138 Mon Sep 17 00:00:00 2001 From: Lukas Sassl Date: Wed, 26 Jul 2023 12:07:30 +0200 Subject: [PATCH 2/2] Typo fixed --- Shared/AzureFunctions/Get-NewOAuthToken.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shared/AzureFunctions/Get-NewOAuthToken.ps1 b/Shared/AzureFunctions/Get-NewOAuthToken.ps1 index dda6569a76..8d45738676 100644 --- a/Shared/AzureFunctions/Get-NewOAuthToken.ps1 +++ b/Shared/AzureFunctions/Get-NewOAuthToken.ps1 @@ -65,7 +65,7 @@ function Get-NewOAuthToken { } process { try { - Write-Verbose "Now calling the Invoke-ResthMethod cmdlet to create an OAuth token" + Write-Verbose "Now calling the Invoke-RestMethod cmdlet to create an OAuth token" $oAuthToken = Invoke-RestMethod @invokeRestMethodParams Write-Verbose "Invoke-RestMethod call was successful" $oAuthTokenCallSuccess = $true