Recently I did the “Offensive Entra ID (Azure AD) and hybrid AD security” training during which Dirk-jan did a demo where he used a modified EvilGinx phislet to perform AiTM phish directly for an access- and refreshtoken. Which is nicer than stealing ESTS cookies and swapping them later. As far as I know there’s no public version and i wanted to be able to do this too.
This year Wesley published “Building an AITM attack tool in Cloudflare Workers (174 LOC)”. So this is a great starting point, no need to build my own AITM tool. We can modify the existing tool to see if we can leverage it to AiTM for access tokens.
OAuth 2.0 authorization code flow
The OAuth 2.0 code flow is used often for consent where a backend requires access to MSGraph, OneDrive or other Microsoft resources. It also works for the M365 native apps. The replyurls are obviously not under our control but we dont need them to be as with AiTM we are in control of the conversation between victim and the Microsoft backend.
What we want is to guide the user through the auth portion and the moment an authorization code is returned we want to steal it and exchange that for a access- and refreshtoken. Essentially we only need to emulate the first 3 steps of the above diagram. If you want to experience the flow yourself you can test this by visiting and logging in using the following url:
https://login.microsoftonline.com/common/oauth2/authorize?response_type=code&client_id=1fec8e78-bce4-4aaf-ab1b-5451cc387264&resource=https://graph.microsoft.com&redirect_uri=https://login.microsoftonline.com/common/oauth2/nativeclient'
If your login is successfull you will end up on the redirect_uri page with the authorization code as a GET parameter.

Its pretty similar to to the default AiTM flow but authorization code flow instead of openid connect.
swapping code for access/refreshtoken
So what we need to do is let the user go through the initial steps, and we interupt the auth flow if we discover a response from our upstream server (Microsoft) that has a location header containing: “nativeclient?code='” in the headers. We can then use the code to request access and refreshtokens from the “/oauth2/token” endpoint. The user, we redirect to portal.office.com. Who should be none the wiser. A slightly editorialised version of the described flow:

In our example we ask access to MSGraph using the Teams client (1fec8e78-bce4-4aaf-ab1b-5451cc387264) which is part of the family of client ids. We can pivot to any client in that family, but we dont need to. The Teams clientid has access to 64 different recoures for example: MSGraph, OneDrive, Exchange and obviously: Teams. Allowing us to pivot to other clients and other resources. After receiving the refresh and accesstoken we can use roadtx to switch clients and resources using our stolen refreshtoken we switch to Microsoft Azur Powershell and the DevOps resource:

Demo
A short demo video showing the worker in action:
Source code
We’ve added another version of the original AITMWorker to the repository. This one doesn’t handle cookies but focuses on this method. To be clear its in no way shape or form production-ready. It’s a POC to show thats its possible 🙂
Detection
Detection
We thought of the following options to detect the AiTMWorker in its current version. If you have aditional ideas be sure to send us a message, we would be happy to add them:
- Cloudflare IP Ranges: Logins originate from Cloudflare IP addresses:
union isfuzzy=true SigninLogs, AADNonInteractiveUserSignInLogs
| where AutonomousSystemNumber == "13335"
- Unusual User-Agent Strings: Logins for mobile apps or desktop clients show browser-like user agents:
union isfuzzy=true SigninLogs, AADNonInteractiveUserSignInLogs
| where ResultType == 0
| where ClientAppUsed == "Mobile Apps and Desktop clients"
| where UserAgent contains "Mozilla/"