Device code phishing is nothing new. In fact it has been around for some years now. There are many good resources that explain the phishing attack in great detail:

There are also a number of tools available to facilitate device code phishing attempts and the subsequent misuse of the gained access and refresh tokens:

Despite the awesome capabilities of device code phishing, it is not widely used by threat actors. Therefore, it probably does not receive much attention from Microsoft. To date, it is still not possible to disable the device code flow (or more precisely the Oauth2 Device Authorization Grant flow) in Entra ID for Microsoft’s first-party applications. But according to this blog post by Dirk-Jan, Microsoft is working on a solution to finally drive a nail in the not existing device code phishing’s coffin .

Nevertheless, Compass Security is releasing two tools that can work hand in hand in a device code phishing exercise or even your next red team engagement.

Token Phisher

Device code phishing has some drawbacks. Two of them are:

  1. The user code is only valid for 15 minutes. Meaning the time between sending a phishing mail and a successful login is quite narrow.
  2. There is no redirect or other action after a user finished the device code flow. Resulting in a “bad” user experience.

Token Phisher is designed to overcome these two limitations. In a nutshell it’s a simple web application that tracks user sessions via a cookie.

  1. Setup Token Phisher in the same region as your victim. This is important since Token Phisher will initiate the device code flow and hence it’s geolocation will be shown to the victim
  2. Define a URL where the user will be redirected too, after a re-visit of the Token Phisher website and a previously successful device code flow
  3. Send your phishing mail with a link to Token Phisher
  4. Victim visits Token Phisher which will initiate the device code flow
  5. Victim completes the flow and closes the browser as instructed
  6. If the victim visits Token Phisher again, a redirect will be performed to the specified resource. So the victim has a better user experience
  7. Use the access and refresh tokens to your liking…ideally with Token Tormentor
Phishing sequence with Token Phisher

You can also customize the entry page to style it according to the customer. Here is an example.

Token Tormentor

Token Tormentor has been written to demonstrate the capabilities of device code phishing in Entra ID. More specifically, it demonstrates the capabilities of the Family of Client ID 1 (FOCI1). Normally a refresh token is bound to the client and audience and cannot be used to obtain access tokens for other clients or audiences. However, if the client is associated with the FOCI1 family, its refresh tokens can be used to obtain other access and refresh tokens from clients in the same family. Also the audience can be changed to get access tokens for other resources. See the research at for a list of known FOCI1 clients and their permissions.

Token Tormentor’s Features

Token Tormentor exchanges refresh tokens for you to a client and the required audience for the task at hand. The following features have been added to Token Tormentor:

  • Convert refresh token to use with ROADtools (
  • Convert refresh token to use with AzureHound (
  • Interact with E-Mail
    • Download all E-Mails of the user as EML
    • Send a Mail as the user
    • Configure forwarding rules
  • Interact with Teams
    • Send chat messages in existing conversations
    • Download recent chat messages as HTML
  • Interact with OneDrive
    • Download all files of the default drive of the user
    • Upload files to the user’s default drive (path is hardcoded and set to ‘Desktop’ for now)
  • Interact with Azure Graph
    • Read BitLocker recovery keys of the user’s machines


Token Tormentor takes as input a file containing the HTTP JSON response from when a device code is exchanged for tokens or when tokens are refreshed.

"access_token": "eyJ0eXAiOiJKV1Qi[cut]",
"expires_in": 1199,
"ext_expires_in": 1199,
"foci": "1",
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGci[cut]",
"refresh_token": "0.AXkA2CxcknehpUGT8xJX_[cut]",
"scope": "email openid profile [cut]",
"token_type": "Bearer"

Interaction with Token Tormentor is implemented via a small menu. Command line arguments are not supported yet. For example, choose option 3 to interact with MS Teams.

> token
1 -- Interact with RoadTools
2 -- Run AzureHound
3 -- Interact with Teams
4 -- Interact with E-Mail
5 -- Interact with OneDrive
6 -- Interact with Azure Graph
7 -- Return
8 -- Exit
Enter your choice: 3

From here you can tell Token Tormentor to download all recent conversations from the Skype API. Token Tormentor will exchange the available refresh token in the file for a token that can be used with the Skype API. After that the recent chats will be fetched and downloaded.

1 -- Download recent conversations
2 -- Send Chat Message in Teams
3 -- Return
4 -- Exit
Enter your choice: 1

[+] Getting recent conversations
[+] Found conversation with ID 48:notifications
[+] Created folder ./teams/48:notifications
[+] Messages downloaded for conversation ID 48:notifications
[+] Found conversation with ID 19:1k0bM_ccwfal8Xo1tXdymfCRtq0Z7LmRhF3IoMU0ifM1@thread.tacv2
[+] Created folder ./teams/19:1k0bM_ccwfal8Xo1tXdymfCRtq0Z7LmRhF3IoMU0ifM1@thread.tacv2
[+] Messages downloaded for conversation ID 19:1k0bM_ccwfal8Xo1tXdymfCRtq0Z7LmRhF3IoMU0ifM1@thread.tacv2
[+] Found conversation with ID 19:6767882a-b65b-4b6c-84b7-34394d806783_c47dab3d-bfee-4318-86bf-4e937af0fa06@unq.gbl.spaces                                                                                        
[+] Created folder ./teams/19:6767882a-b65b-4b6c-84b7-34394d806783_c47dab3d-bfee-4318-86bf-4e937af0fa06@unq.gbl.spaces                                                                                            
[+] Messages downloaded for conversation ID 19:6767882a-b65b-4b6c-84b7-34394d806783_c47dab3d-bfee-4318-86bf-4e937af0fa06@unq.gbl.spaces                                                                           
[+] Downloaded file AMS 0-nch-d1-03eaf0487327c18052dd8c9e5281f24c
[+] Downloaded file AMS 0-nch-d4-1dbdfea208f35496d3bdc3ecd7583192
[+] Downloaded file AMS 0-nch-d3-4d94b00677fc64c20a21b51fd61cbb11
[+] Found conversation with ID 19:16c96815-a234-42b9-8136-eedfa502e079_c47dab3d-bfee-4318-86bf-4e937af0fa06@unq.gbl.spaces                                                                                        
[+] Created folder ./teams/19:16c96815-a234-42b9-8136-eedfa502e079_c47dab3d-bfee-4318-86bf-4e937af0fa06@unq.gbl.spaces                                                                                            
[+] Messages downloaded for conversation ID 19:16c96815-a234-42b9-8136-eedfa502e079_c47dab3d-bfee-4318-86bf-4e937af0fa06@unq.gbl.spaces                                                                           
[+] Downloaded file AMS 0-wch-d2-2293500e6217403a5aa44d107b21e4e3

The output should be a ./teams folder containing all of the user’s recent conversations. For readability, the conversation is rendered into an HTML file:

Prevention, Detection and Mitigation of Device Code Phishing

As mentioned above, it is not currently possible to disable device code flow for Microsoft’s first-party applications.

What you can do is set restrictive Conditional Access (CA) policies. The most effective way is to require that only MDM/MAM managed devices are allowed to connect. Another option is to restrict logins from known IP addresses. This works because the IP address of the flow initiator is checked against the CA.

Detection, on the other hand, is fairly straightforward. Monitor the Sign-In logs for authentication protocol = device code

Mitigate Access Tokens

Access tokens from a successful phishing attack cannot be revoked. By default, they are valid for 60-90 minutes.

If you have a P1 license, you can use a preview feature to reduce the default lifetime of access tokens to a minimum of 10 minutes. This would further reduce the time an attacker has to misuse the tokens they have obtained.

Mitigate Refresh Tokens

Changing the user’s password would be enough to revoke password based refresh tokens, but to ensure that all refresh tokens are revoked we recommend the methods listed below.