In a previous article, we covered the difference in behavior across various endpoints with regards to enforcing Microsoft 365 Group guest access restrictions. In particular, we pointed out that endpoints such as OWA rely on client-side verification, in contrast to endpoints such as My Groups, that offload the checks to the backend. You can probably guess where this is going – without the service side component, restrictions can be bypassed. Let’s see how.
In a nutshell, we can use OWA to perform a “regular” add member operation, then edit the request’s payload to instead add a guest user. Alternatively, you can “borrow” the access token obtained from OWA and issue a separate request using any tool that allows you to make HTTP calls. And if we generalize the usage scenario further, we can even create a custom application and use it to obtain a token for the required resource, then craft a HTTP request with the desired payload. Using the browser is however the easiest method, and something even the average Joe could achieve with basic instructions.
Thus, the requirements for reproducing this behavior as an end user are simple:
- Have a Microsoft365 Group with guest access blocked (AllowToAddGuests set to False)
- Have an existing guest user
- Have a regular user assigned as Owner of the M365 Group
With the above requirements met, you can head over to OWA and specifically the Group experience (Go to Groups). Select the Microsoft 365 Group in question, then Members. Herein, you can verify that the guest restriction settings are in place, by hitting the Add members button and trying to add a Guest user. You should see a tooltip along the lines of: “You cannot add a guest to this group due to company policy”, as well as the following infotip:
The above confirms that the Microsoft 365 Group has guest access restrictions in place, and that OWA is enforcing them. We can also confirm this by examining the requests being made when hitting the Add members button, in particular you should see a request being made to Graph’s /groups/{id}/settings/ endpoint. As it turns out however, the engineers decided to rely on the frontend to address guest user access scenarios… which is never a good practice. In other words, if we bypass the front end and issue a request to add a Guest user to the Microsoft 365 Group in question, the results is not what you’d except.
To make things easier, and to illustrate how any user that is an Owner of a Microsoft 365 Group could abuse this, we will use the browser’s built-in functionality to issue such request. The easiest way to do this is to add any “regular” user to the group, which you should be able to do as you have the Owner role assigned. Make sure to open the browser’s Dev Tools beforehand (hit F12 and then select Network). Adding a user will result in a POST request against the Outlook REST API, which you can locate by looking for /addmembers on the left pane.
Right-click said entry and select Edit and Resend to prepare the modified request. The only thing we need to modify is the GUID of the user we are trying to add as a member, which you can find under the Body tab. No further changes are needed, as we will reuse the same method, URL and set of headers as the original request, including the access token that was used for authentication. The example below show what the modified request will look like:
POST https://outlook.office.com/api/beta/groups/{id}/addmembers
{"Members":["guest_GUID_goes_here"]}
Hit the Send button to submit the request. The response received from the server seems to indicate that the request failed:
{"@odata.context":"https://outlook.office.com/api/beta/$metadata#Microsoft.OutlookServices.GroupMembershipUpdateResult","FailedMembers":[{"Name":"user@gmail.com","Value":"Unclassified"}]}
If you refresh the page however, you will see that the Guest user is now added to the Microsoft 365 Group’s membership list. You can confirm that this is indeed the case via the Get-UnifiedGroupLinks cmdlet. The Classic Outlook client will reflect the changed membership as well, including within the Group card and the Offline Address Book (after sufficient time has passed for synchronization). In addition, the Guest user we “failed” to add will receive the “welcome” email in his mailbox, as well as any new emails sent to the Microsoft 365 Group from this point forward (subject to subscriber settings).
So for all intents and purposes, adding the Guest user to a Microsoft 365 Group which has guest access restriction seems to be successful. At least when Exchange Online is concerned, that is. On Entra ID side of things, or the EAC for that matter, the change is never reflected and the guest is not listed as member. My guess would be that synchronization of the change is blocked since the Graph API/Entra ID does have service-side verification for guest access restrictions, as we discussed in our previous article. Of course, it might be something else altogether. Regardless of the reason, you will end up with a mismatch between the reported group membership in tools leveraging Outlook’s REST API and those relying on the Graph API.
Another important detail is that no audit trace was ever generated for this operation, even a full week later. As above, we can only guess why no audit record was captured, but the net effect is that not only any Owner can successfully bypass Microsoft 365 Group’s guest access restrictions, but they can do so undetected. Yes, the sample process we outlined above does involve adding a “regular” user as well, which is something that is audited and can be potentially used as circumstantial evidence. As you can probably guess however, the process can be replicated without this step or any reliance on OWA for that matter. The fact remains that the Outlook REST API endpoint we (ab)used above does not perform proper validation for this scenario.
Now, I have been writing in present tense, but the issue detailed above has since been addressed by Microsoft, and should no longer reproduce. I did the responsible thing by only publishing this article after receiving a confirmation from the OWA dev team. I did not receive a confirmation that the accompanying issues with replication and audit trail are also addressed, but I can no longer test this to check on their status, so we will have to trust Microsoft on this one.
Before closing the article, few additional notes. First, the scenario detailed above was only relevant to existing guest users, as in guests that are already present in the directory. Inviting a guest from another organization is subject to additional controls and is generally not the same thing as adding them to a Group. And again, we are talking specifically about guest users here, with userType value of Guest. A user object with userType value of Member is not subject to the group guest restrictions. On the same note, MailUser objects are treated differently from GuestMailUser ones, with group guest restrictions (and the potential bypass) only relevant to the latter.
Similarly, the issue was specific to the Outlook REST API and could not be reproduced via the Graph API. Lastly, there does not seem to be any change in the type of response received when attempting the same query after the fix rolled out. A quick test shows that the exact same response is received (with the same Unclassified message). The difference is that no change in the group’s membership list is now committed, as also confirmed by the Get-UnifiedGroupLinks cmdlet. Hopefully, this fix will prevent any similar unexpected behavior in the future!


1 thought on “Lack of service-side validation allows group guest user restrictions bypass via OWA”