Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .build/cspell-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ contoso
CTMM
Datacenter
dcom
devicecode
DMARC
Dsamain
DTLS
Expand Down Expand Up @@ -171,4 +172,5 @@ Webex
Weve
wevtutil
windir
wids
Xlsb
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@
If you provide the thumbprint, the script searches and exports the certificate with the thumbprint provided from the local machines certificate
store. If you provide the file path, the script uploads the certificate, which was specified.
This parameter allows you to run granular configurations. Note that some of the tasks depend on others and can't be run alone.
.PARAMETER UseAuthorizationCodeFlow
Use this switch parameter to force the Graph API access token to be acquired via the OAuth 2.0 authorization code
flow with PKCE instead of the default device code flow. The authorization code flow opens a local browser and
requires a redirect listener, so it only works on hosts with a browser and with an application that permits a
loopback (http://localhost) redirect URI (for example, a custom application passed via -CustomClientId).
Note that the default application does not permit a loopback redirect, so this switch requires a custom application.
.PARAMETER ScriptUpdateOnly
This optional parameter allows you to only update the script without performing any other actions.
.PARAMETER SkipVersionCheck
Expand Down Expand Up @@ -227,6 +233,13 @@ param(
[Parameter(Mandatory = $false, ParameterSetName = "Create")]
[string]$CertificateInformation,

[Parameter(Mandatory = $false, ParameterSetName = "FullyConfigureExchangeHybridApplication")]
[Parameter(Mandatory = $false, ParameterSetName = "FirstPartyKeyCredentialsCleanup")]
[Parameter(Mandatory = $false, ParameterSetName = "Create")]
[Parameter(Mandatory = $false, ParameterSetName = "Delete")]
[Parameter(Mandatory = $false, ParameterSetName = "RemovePermissions")]
[switch]$UseAuthorizationCodeFlow,

[Parameter(Mandatory = $true, ParameterSetName = "ScriptUpdateOnly")]
[switch]$ScriptUpdateOnly,

Expand All @@ -249,6 +262,7 @@ begin {
. $PSScriptRoot\..\..\Shared\Get-PSSessionDetails.ps1
. $PSScriptRoot\..\..\Shared\Get-ProtocolEndpointViaAutoDv2.ps1
. $PSScriptRoot\..\..\Shared\Show-Disclaimer.ps1
. $PSScriptRoot\..\..\Shared\Test-IsServerCoreOperatingSystem.ps1
. $PSScriptRoot\..\..\Shared\ActiveDirectoryFunctions\Get-ExchangeOrganizationGuid.ps1
. $PSScriptRoot\..\..\Shared\AzureFunctions\Get-Consent.ps1
. $PSScriptRoot\..\..\Shared\AzureFunctions\Get-CloudServiceEndpoint.ps1
Expand Down Expand Up @@ -348,6 +362,22 @@ begin {
return
}

# The authorization code flow opens a local browser and requires an application that permits a loopback
# (http://localhost) redirect URI. The default application does not permit such a redirect, so a custom
# application must be provided via the 'CustomClientId' parameter when this flow is used. Server Core does
# not include the components required to launch a browser, so the flow is ignored there and the script
# falls back to the default device code flow.
if ($UseAuthorizationCodeFlow) {
if (Test-IsServerCoreOperatingSystem) {
Write-Warning "The authorization code flow is not supported on Server Core - the device code flow will be used instead."
$UseAuthorizationCodeFlow = $false
} elseif ([System.String]::IsNullOrEmpty($Script:CustomClientId)) {
Write-Warning "The 'CustomClientId' parameter is required when the 'UseAuthorizationCodeFlow' parameter is used because the default application does not permit a loopback (http://localhost) redirect."

return
}
}

#region Pre-Configuration
# Gets the Fqdn of the local computer
$localServerFqdn = [System.Net.Dns]::GetHostEntry($env:COMPUTERNAME).HostName
Expand Down Expand Up @@ -622,6 +652,14 @@ begin {
$getGraphAccessTokenParams.Add("ClientId", $Script:CustomClientId)
}

# The device code flow is used by default to acquire the access token. Use the authorization code flow
# only when it was explicitly requested via -UseAuthorizationCodeFlow (requires a browser and an
# application that permits a loopback redirect URI, for example a custom application).
if ($UseAuthorizationCodeFlow) {
Write-Verbose "The authorization code flow will be used to acquire the access token"
$getGraphAccessTokenParams.Add("UseAuthorizationCodeFlow", $true)
}

$graphAccessToken = Get-GraphAccessToken @getGraphAccessTokenParams

if ($null -eq $graphAccessToken) {
Expand Down Expand Up @@ -1384,7 +1422,8 @@ begin {
$graphApiFeatureEnabledCount = 0

foreach ($o in $exchangeOnpremAsThirdPartyAppIdSettingOverrides) {
$match = [regex]::Match($o.Parameters, $settingOverridesEnabledRegex, "IgnoreCase")
$enabledParameter = @($o.Parameters) | Where-Object { $_ -match "^\s*Enabled\s*=" } | Select-Object -First 1
$match = [regex]::Match([string]$enabledParameter, $settingOverridesEnabledRegex, "IgnoreCase")
$featureIsEnabled = ($match.Success -and $match.Groups[1].Value -eq "true")
$featureSettingOverrideValue = if (-not $match.Success) { "Unknown" } else { $match.Groups[1].Value }

Expand All @@ -1398,7 +1437,8 @@ begin {
}

foreach ($o in $routeThroughMSGraphSettingOverrides) {
$match = [regex]::Match($o.Parameters, $settingOverridesEnabledRegex, "IgnoreCase")
$enabledParameter = @($o.Parameters) | Where-Object { $_ -match "^\s*Enabled\s*=" } | Select-Object -First 1
$match = [regex]::Match([string]$enabledParameter, $settingOverridesEnabledRegex, "IgnoreCase")
$featureIsEnabled = ($match.Success -and $match.Groups[1].Value -eq "true")
$featureSettingOverrideValue = if (-not $match.Success) { "Unknown" } else { $match.Groups[1].Value }

Expand Down Expand Up @@ -1525,7 +1565,7 @@ begin {
Name = "EnableRouteThroughMSGraphFeature"
Component = "SettingOverride"
Section = "RouteThroughMSGraph"
Parameters = @("Enabled=true")
Parameters = @("Enabled=true", "EnabledForMailTips=true", "EnabledForAutomaticReplies=false")
Reason = "Created by $($script:MyInvocation.MyCommand.Name) on $(Get-Date)"
}
# Execute the commands to create the new setting override and to refresh the variant configuration
Expand Down
61 changes: 57 additions & 4 deletions Security/src/CVE-2023-23397/CVE-2023-23397.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,17 @@
Skip the first pass of -UseSearchFolders and just check the existing search folders for results.
.PARAMETER TimeoutSeconds
This optional parameter lets you specify the timeout value for the ExchangeService object. Defaults to 5 minutes.
.PARAMETER CustomClientId
This optional parameter allows you to provide the Application (client) ID of a custom application in Microsoft Entra ID.
It is required when the -UseAuthorizationCodeFlow parameter is used because the default application does not permit a
loopback (http://localhost) redirect URI, which the authorization code flow relies on.
.PARAMETER UseAuthorizationCodeFlow
This optional switch parameter forces the script to acquire the Graph API access token via the OAuth 2.0 authorization
code flow with PKCE instead of the default device code flow. The authorization code flow opens a local browser and
requires a redirect listener, so it only works on hosts with a browser and with an application that permits a loopback
(http://localhost) redirect URI. Because the default application does not permit a loopback redirect, a custom
application must be provided via the -CustomClientId parameter when this flow is used. The switch is ignored on Server
Core (which cannot launch a browser), where the script falls back to the default device code flow.
.EXAMPLE
PS C:\> .\CVE-2023-23397.ps1 -CreateAzureApplication
This will run the tool to create a new Azure application with required permissions
Expand Down Expand Up @@ -183,7 +194,20 @@ param(
[Parameter(Mandatory = $false, ParameterSetName = "Audit")]
[Parameter(Mandatory = $false, ParameterSetName = "Cleanup")]
[ValidateRange(1, 2147483)]
[int]$TimeoutSeconds = 300
[int]$TimeoutSeconds = 300,

[Parameter(Mandatory = $false, ParameterSetName = "CreateAzureApplication")]
[Parameter(Mandatory = $false, ParameterSetName = "DeleteAzureApplication")]
[Parameter(Mandatory = $false, ParameterSetName = "Audit")]
[Parameter(Mandatory = $false, ParameterSetName = "Cleanup")]
[ValidatePattern("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")]
[string]$CustomClientId,

[Parameter(Mandatory = $false, ParameterSetName = "CreateAzureApplication")]
[Parameter(Mandatory = $false, ParameterSetName = "DeleteAzureApplication")]
[Parameter(Mandatory = $false, ParameterSetName = "Audit")]
[Parameter(Mandatory = $false, ParameterSetName = "Cleanup")]
[switch]$UseAuthorizationCodeFlow
)

dynamicparam {
Expand Down Expand Up @@ -247,6 +271,7 @@ begin {
. $PSScriptRoot\..\..\..\Shared\GraphApiFunctions\New-AzureApplicationAppSecret.ps1
. $PSScriptRoot\..\..\..\Shared\GraphApiFunctions\New-ExchangeAzureApplication.ps1
. $PSScriptRoot\..\..\..\Shared\Show-Disclaimer.ps1
. $PSScriptRoot\..\..\..\Shared\Test-IsServerCoreOperatingSystem.ps1

$loggerParams = @{
LogName = "CVE-2023-23397-$((Get-Date).ToString("yyyyMMddhhmmss"))-Debug"
Expand Down Expand Up @@ -627,8 +652,36 @@ begin {
Enable-TrustAnyCertificateCallback
}

# The authorization code flow opens a local browser and requires an application that permits a loopback
# (http://localhost) redirect URI. The default application does not permit such a redirect, so a custom
# application must be provided via the 'CustomClientId' parameter when this flow is used. Server Core does
# not include the components required to launch a browser, so the flow is ignored there and the script
# falls back to the default device code flow.
if ($UseAuthorizationCodeFlow) {
if (Test-IsServerCoreOperatingSystem) {
Write-Warning "The authorization code flow is not supported on Server Core - the device code flow will be used instead."
$UseAuthorizationCodeFlow = $false
} elseif ([System.String]::IsNullOrEmpty($CustomClientId)) {
Write-Host "The 'CustomClientId' parameter is required when the 'UseAuthorizationCodeFlow' parameter is used because the default application does not permit a loopback (http://localhost) redirect." -ForegroundColor Red
exit
}
}

# Shared parameters used for every Get-GraphAccessToken call. A custom application (ClientId) is only added
# when it was provided via the 'CustomClientId' parameter, otherwise the default application is used.
$getGraphAccessTokenParams = @{
AzureADEndpoint = $azureADEndpoint
GraphApiUrl = $graphApiEndpoint
UseAuthorizationCodeFlow = $UseAuthorizationCodeFlow
}

if (-not [System.String]::IsNullOrEmpty($CustomClientId)) {
Write-Verbose "CustomClientId $CustomClientId was provided and will be used"
$getGraphAccessTokenParams.Add("ClientId", $CustomClientId)
}

if ($CreateAzureApplication) {
$graphAccessToken = Get-GraphAccessToken -AzureADEndpoint $azureADEndpoint -GraphApiUrl $graphApiEndpoint
$graphAccessToken = Get-GraphAccessToken @getGraphAccessTokenParams

if ($null -eq $graphAccessToken) {
Write-Host "Failed to acquire an access token - the script cannot continue" -ForegroundColor Red
Expand All @@ -653,7 +706,7 @@ begin {
}

if ($DeleteAzureApplication) {
$graphAccessToken = Get-GraphAccessToken -AzureADEndpoint $azureADEndpoint -GraphApiUrl $graphApiEndpoint
$graphAccessToken = Get-GraphAccessToken @getGraphAccessTokenParams

if ($null -eq $graphAccessToken) {
Write-Host "Failed to acquire an access token - the script cannot continue" -ForegroundColor Red
Expand Down Expand Up @@ -726,7 +779,7 @@ begin {
([System.String]::IsNullOrEmpty($Organization)) -or
([System.String]::IsNullOrEmpty($CertificateThumbprint))) {
# We need to query the Azure application information from the Azure AD if not explicitly provided via parameter
$azAccountsObject = Get-GraphAccessToken -AzureADEndpoint $azureADEndpoint -GraphApiUrl $graphApiEndpoint
$azAccountsObject = Get-GraphAccessToken @getGraphAccessTokenParams

if ($null -eq $azAccountsObject) {
Write-Host "Failed to acquire an access token - the script cannot continue" -ForegroundColor Red
Expand Down
Loading