Closed HuskyHacks closed 4 months ago
Hey @dafthack, were you able to take a look at this? I'd like to land code in the repo if I can resolve how to get a token with the required permissions.
Hey, yes I've been looking into this but haven't had a chance to fully determine how to go about it yet. We've noticed a lot of strange issues around how user tokens get certain scopes applied and how sometimes it appears scopes that would appear to be normal actually aren't something we can request for a standard user token. For example, some of the Teams permissions and Mail.Read.Shared for whatever reason don't get scoped to a normal user token. For those I've been leaning on application permissions as you can apply some of those there as a non-admin. If you figure out a good way to request specific scopes like these for a standard user please let us know. We will keep looking too.
I encountered difficulties with permissions while implementing a backdoor in one of my experiments exploring the tool's practical applications. The process failed to proceed after granting the necessary permissions, which were supposed to generate the OAuth code. I want to know if its a general error .
Will review soon
As a small update on this, when I can procure a token by... erhm, alternative means and it has the MailBoxSettings.ReadWrite scope , I can run a POST against the API to create the inbox rule and it works! I did this by manually overriding the access token header so it only provides the JWT, great success:
Function Invoke-CreateInboxRule {
$access_token = [hard coded my stolen JWT here as a test]
$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"
}
...[snip snip rest of function logic]...
This got me thinking if there would be any interest in allowing GraphRunner functions to specify a JWT and use that as the Auth header instead of pulling from the $tokens variable after using Get-GraphToken. It might also already allow you to do this and maybe I just missed it.
In any case, it looks like this function would work if we can figure out how to make sure the requested Graph token has the right scope of permissions
Keep in mind that a delegated aka user token can only be used to do things that specific user can do. While a user with say Global Admin is perfectly capable of creating a team the user would need to add itself to the team first as owner/member to look at data in the team. The same goes for any other resources.
Using application permissions is taking out the proverbial sledgehammer.
@HuskyHacks Sorry for commenting on this old issue, although I stumbled upon this thread and wanted to clarify some misunderstandings I noticed here :)
For the v1 OAuth2 token endpoint (i.e. https://login.microsoftonline.com/common/oauth2/token
), the scopes that are available in a JWT token depend on the combination of the client ID and resource that you request in the body. You can't really request specific scopes manually with the v1 token endpoint, but you just seem to get all the scopes for which your specific application (i.e. Client ID) has consent over a specific resource. So when utilizing first-party Microsoft apps and resources, these scopes/permissions will already have been pre-consented.
The "new" v2 Oath2 token endpoint (i.e. https://login.microsoftonline.com/common/oauth2/v2.0/token
) does not use a resource + client id in the request body, but actually uses scope + client id instead. However, you still can't request scopes for which an application (client ID) does not have consent yet (unless the user does provide consent for that).
What you will notice for instance is that requesting a token (using the v1 endpoint) for the resource https://graph.microsoft.com
with a client ID d3590ed6-52b3-4102-aeff-aad2292ab01c
(i.e. Microsoft Office) will provide you with a token for a different scope than when you request a token for the same resource, but with client ID d326c1ce-6cc6-4de2-bebc-4591e5e13ef0
(i.e. SharePoint). The funny thing is, since these are all FOCI apps/clients, you can use the same refresh token to obtain access tokens with different client IDs, and therefore obtain different scopes/permissions.
So how do you know what Client ID you should use to get a specific scope? As far as I know, Microsoft has not documented this yet, so the best approach would be to do some trial and error.
You seem to be facing some of the same issues I encountered a while back when I wanted to implement a Microsoft Teams module in GraphSpy using the MS Graph API. I was desperately looking for a client which has consent to use the Chat.ReadWrite
scope, however, in my case there did not seem to be any first-party FOCI client that provided this sadly, so I ended up having to completely rely on the undocumented skype API (https://api.spaces.skype.com/
) to create the module.
You seem to have more luck though, since the specific scope you are looking for MailBoxSettings.ReadWrite
can be obtained by requesting an access token for the resource https://graph.microsoft.com
and using the Client ID of Microsoft Teams (1fec8e78-bce4-4aaf-ab1b-5451cc387264
).
This is a good resource if you want to check which specific combinations of resource/client ID provide a specific scope. However, note that I've recently repeated the process they used to create that map, and I noticed that the scope map is not 100% complete anymore, but it is still a very good resource to check something quickly as it will be over 95% accurate!
@HuskyHacks Sorry for commenting on this old issue, although I stumbled upon this thread and wanted to clarify some misunderstandings I noticed here :)
For the v1 OAuth2 token endpoint (i.e.
https://login.microsoftonline.com/common/oauth2/token
), the scopes that are available in a JWT token depend on the combination of the client ID and resource that you request in the body. You can't really request specific scopes manually with the v1 token endpoint, but you just seem to get all the scopes for which your specific application (i.e. Client ID) has consent over a specific resource. So when utilizing first-party Microsoft apps and resources, these scopes/permissions will already have been pre-consented.The "new" v2 Oath2 token endpoint (i.e.
https://login.microsoftonline.com/common/oauth2/v2.0/token
) does not use a resource + client id in the request body, but actually uses scope + client id instead. However, you still can't request scopes for which an application (client ID) does not have consent yet (unless the user does provide consent for that).What you will notice for instance is that requesting a token (using the v1 endpoint) for the resource
https://graph.microsoft.com
with a client IDd3590ed6-52b3-4102-aeff-aad2292ab01c
(i.e. Microsoft Office) will provide you with a token for a different scope than when you request a token for the same resource, but with client IDd326c1ce-6cc6-4de2-bebc-4591e5e13ef0
(i.e. SharePoint). The funny thing is, since these are all FOCI apps/clients, you can use the same refresh token to obtain access tokens with different client IDs, and therefore obtain different scopes/permissions.So how do you know what Client ID you should use to get a specific scope? As far as I know, Microsoft has not documented this yet, so the best approach would be to do some trial and error.
You seem to be facing some of the same issues I encountered a while back when I wanted to implement a Microsoft Teams module in GraphSpy using the MS Graph API. I was desperately looking for a client which has consent to use the
Chat.ReadWrite
scope, however, in my case there did not seem to be any first-party FOCI client that provided this sadly, so I ended up having to completely rely on the undocumented skype API (https://api.spaces.skype.com/
) to create the module.You seem to have more luck though, since the specific scope you are looking for
MailBoxSettings.ReadWrite
can be obtained by requesting an access token for the resourcehttps://graph.microsoft.com
and using the Client ID of Microsoft Teams (1fec8e78-bce4-4aaf-ab1b-5451cc387264
).This is a good resource if you want to check which specific combinations of resource/client ID provide a specific scope. However, note that I've recently repeated the process they used to create that map, and I noticed that the scope map is not 100% complete anymore, but it is still a very good resource to check something quickly as it will be over 95% accurate!
You may be referring to my flow with Invoke-BruteClientIDAccess. I tend to device phish with edge clientID, brute access to others, swap to what I need. I am starting to work on GR more soon, so I hope to tackle this and blog a bit.
You may be referring to my flow with Invoke-BruteClientIDAccess. I tend to device phish with edge clientID, brute access to others, swap to what I need. I am starting to work on GR more soon, so I hope to tackle this and blog a bit.
Oh nice, I didn't know about the Invoke-BruteClientIDAccess
command sadly haha. I just bruteforced it manually using Burp Suite and a big list of Client IDs 😅
Although no need to repeat the process every single time right? Since you should mostly get the same result for different users in different tenants for first-party clients unless Microsoft changes something I presume? Or have you noticed any differences here as well?
@RedByte1337 great to see you in this repo! And thank you for that writeup. I just reimplemented this inbox rule function and used the Teams client ID during token auth and it works! I opened this issue when I knew next to 0 about using OAuth for M365 (whereas now I know like .0005%). The conversation here has really helped me solidify some concepts regarding token scope, audience, and the difference between delegated permissions and application permissions.
I can submit the PR I intended to submit when I opened this issue! I'll try to get it in soon.
Thank you @RedByte1337 @dafthack @rvrsh3ll @rvdwegen, this has been immensely helpful to me!
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
edit: the specific error output