Microsoft Teams has become even more popular in the current context (blah blah pandemic, blah blah work from home). It allows users to collaborate using Groups that they create and manage themselves. Groups can be public or private (on invitation), but many group owners use the public option, even though it allows anyone in the organization to access their chats, shared files and more! 😮 We will also see that even private Groups disclose their title, description, and members to anyone, and sometimes this alone is enough to disclose restricted information. 🕵️
How users create Teams 🔗
When we create a “team” in Microsoft Teams, before choosing a name and a description, we have to select a visibility level:
This is a seemingly benign choice, with no default option, that we quickly decide when we are excited and eager to create our team! Once the team is created, this choice is reminded by a small icon at the top:
- 🔒 “Private” team
- 🌐 “Public” team
⚠️ Notice how a “public” team is noted as “Org” which must not be confused with an “org-wide” team just below
- 📢 “Org-wide” team which is actually a “public” team to which all members of the tenant are automatically (re-)added:
We can change it afterwards:
Teams are based on M365 Groups behind the scenes… 🔗
Microsoft Teams is way more than a chat tool as it allows to share and collaborate on files (“Files” tab which uses SharePoint Online behind the hood), share notes (“Wiki” tab, powered by OneNote), and use various applications (“Forms”, “Lists”, “Power BI”, “Praise”, “SharePoint”, “Tasks by Planner”… and many 3rd party applications). All of this is made possible by a “Microsoft 365 Group” and all these features rely on it to give access to the team members.
This is why the first step when creating a Teams is to choose between “from scratch”, which will create the associated M365 Group, or “from a group or team”, which will reuse an existing M365 Group (not already having Teams enabled):
The choice between a “private” or a “public” Teams is actually saved in the M365 Group itself. We have the same options when creating a standalone M365 Group:
And, if we decide to create a Teams from an existing M365 Group, “private” Groups are denoted by a padlock 🔒:
To summarize, each Teams has an M365 Group supporting it, but not all M365 Groups are Teams-enabled.
Discovery of M365 Groups 🔗
Between Microsoft 365 and Teams, of course we have Azure AD in charge of the identities. And naturally the M365 Groups are stored in Azure AD.
First of all, let’s clarify something about groups in Azure AD / Microsoft 365 because there are three kinds which are not compatible.
- SharePoint Groups: used for SharePoint Online.
- Azure AD Security Groups: as their name indicate, they are used a little for security purposes in Azure AD. They look similar to classic on-prem Windows and Active Directory groups allowing to grant access to resources to groups of users. They are even compatible and organizations can synchronize their on-prem AD groups to Azure AD Security Groups.
- Microsoft 365 Groups: as their name indicate, they are mainly used with Microsoft 365 (new name of Office 365) to grant various access to groups of users. They are cloud-only and thus cannot be used to grant access to on-prem resources. You will also find them called “unified groups” in some documents and PowerShell cmdlets.
I recommend the Groups in Microsoft 365 and Azure, and Which is Right for You article if you want to know more.
Everything in this article is related to Microsoft 365 Groups, so you can ignore the others for our purpose!
Two roles are possible within an M365 Group:
- Owner: by default the person who created the group, and the users they may have added after. Owners can modify the group and its members. Privileged users at the tenant level (e.g. Global Admin role) also have those rights implicitly.
- Member: a simple member without any particular right, except benefiting from the group and leaving it.
You can list, create, edit and join M365 Groups by going to https://account.activedirectory.windowsazure.com/r#/groups
The Azure Active Directory portal offers a similar interface on: https://portal.azure.com/#blade/Microsoft_AAD_IAM/GroupsManagementMenuBlade/AllGroups
⚠️ Notice how the Azure AD portal shows both AAD Security Groups (“Everyone” here) and M365 Groups (all the others)
M365 Groups are also visible from Outlook (desktop or web), but only those which are not Teams-enabled apparently:
We can even see the emails and the files of a public group to which we do not belong:
As we said, an M365 Groups can have the Teams feature enabled, but not necessarily. And this is the same for other features such as Outlook (allows to use the group as a mailing-list, it’s similar to shared mailboxes but not exactly the same) and SharePoint (share files). Each of these feature adds attributes to the group such as its email address and SharePoint site URL.
Here, for example, we see the available features for this group and their links:
💣 Risks of public Teams / M365 Groups 🔗
Impacts 🔗
The risks of public Teams / M365 Groups are clear since anyone in the tenant can join those and get access to the:
- Teams chats
- shared Files (files shared within a Teams are actually stored in SharePoint online)
- OneDrive shared libraries
- Outlook emails (when using the Group as a mailing-list)
- OneNote
- Microsoft Planner and To-Do tasks
- Yammer (if the Group was created from there)
- Azure cloud resources (because Groups can get Azure role assignments)
- Many others depending on the applications “installed” in the Group or Teams. See Overview of Microsoft 365 Groups for administrators
Usually all group members have read & write access to those for easier collaboration.
The fact that a new potentially unauthorized user just joined a public Group is not easily visible! For example in Teams we have to click on this button:
We also have seen that it’s possible to read the emails of a public Group and access its shared files without even needing to join it. The SharePoint URL is usually https://<tenant_name>.sharepoint.com/sites/<group_name>
How to list public M365 Groups? 🔗
M365 Groups that are Teams-enabled are easily discoverable from the Teams application. Some of them will be suggested when clicking the link to “Join or create a Team”:
But if there are too many, we can use the search bar too to find all groups. We just have to iterate on each letter of the alphabet (search for “a” to find all Groups which name begins with “a”, then with “b”, etc.):
But what if we want all Groups including those which are not Teams-enabled?
The Azure AD portal would have been a nice option but it doesn’t seem to easily search for public VS private Groups…
The “join groups” feature of the Azure AD web app, on https://account.activedirectory.windowsazure.com/r#/joinGroups, is very helpful for this. It allows to list all groups with their name, description, visibility (private/public), members, etc.:
We can also list and manage Groups with the AzureAD PowerShell module
PS > Get-AzureADMSGroup -All $true | Select-Object displayname,description,visibility | ft -autosize
DisplayName Description Visibility
----------- ----------- ----------
Everyone Automatic group with everyone
Crisis management Handling of the explosion at the plant Public
test webhook test webhook Private
test test Public
Contoso Team Contoso Team Private
my standalone public group Public
All Company All Company Public
allhands2 Check here for organization announcements and important info. Public
Acquisition of ACME Private discussion about the acquisition of our competitor ACME. let's discuss due diligence and needed regulatory approval.... Private
PS > Get-AzureADGroup -SearchString "acquisition"
ObjectId DisplayName Description
-------- ----------- -----------
d593e3c7-58fb-4a5e-89c9-6a6a62a3529c Acquisition of ACME Private discussion about the acquisition of our competitor ACME. let's discuss due diligence and needed regulatory approval....
PS > Get-AzureADGroup -SearchString "acquisition" | Get-AzureADGroupOwner
ObjectId DisplayName UserPrincipalName UserType
-------- ----------- ----------------- --------
2aafba81-6213-46b2-bbd8-9db23235edcf Clément Notin [pwnz] cnotin@pwnz.onmicrosoft.com Member
115d78ba-a6cb-4a25-bf03-8aae8c5c096b Patti Fernandez PattiF@pwnz.onmicrosoft.com Member
PS > Get-AzureADGroup -SearchString "acquisition" | Get-AzureADGroupMember
ObjectId DisplayName UserPrincipalName UserType
-------- ----------- ----------------- --------
2aafba81-6213-46b2-bbd8-9db23235edcf Clément Notin [pwnz] cnotin@pwnz.onmicrosoft.com Member
37d8aa56-b3b7-4729-8faf-481670ef6f90 Nestor Wilke NestorW@pwnz.onmicrosoft.com Member
224b1765-e791-4cf9-ad4b-814c72bb4f46 Miriam Graham MiriamG@pwnz.onmicrosoft.com Member
115d78ba-a6cb-4a25-bf03-8aae8c5c096b Patti Fernandez PattiF@pwnz.onmicrosoft.com Member
dab92e84-7e1e-4685-998b-d7ab26cd6849 Lee Gu LeeG@pwnz.onmicrosoft.com Member
If you want to enumerate other kinds of objects of the tenants, like users, service principals, applications, devices, etc. I highly recommend to use the ROADRecon project from Dirk-jan Mollema. If you like this topic you should read also his article Introducing ROADtools - The Azure AD exploration framework
I shared a bit of code to make more visible the fact that a Group is public:
On my side, to make it easier and get just the data I desired in the format I wanted, I created the m365_groups_enum Python script that you can use too.
It enumerates all M365 Groups in a tenant with their metadata:
- name
- visibility: public or private
- description
- email address
- owners
- members
- Teams enabled?
- SharePoint URL (e.g. for Teams shared files)
The output is a JSON file, and there is a companion script to transform it to a CSV to easily filter the public Groups and their members and owners.
This script uses the Microsoft Graph API for Groups which allows to list Groups, with their members, and owners.
> python all_groups.py -u leeg@pwnz.onmicrosoft.com -p MyPassw0rd!
Authenticated as LeeG@pwnz.onmicrosoft.com
Getting all groups...
Got 8 groups
Getting groups details...
Bye!
> cat all_groups.json
[…]
{
"displayName": "Acquisition of ACME",
"visibility": "Private",
"id": "d593e3c7-58fb-4a5e-89c9-6a6a62a3529c",
"description": "Private discussion about the acquisition of our competitor ACME. let's discuss due diligence and needed regulatory approval.\nFor board members only!",
"mailEnabled": true,
"mail": "AcquisitionofACME@pwnz.onmicrosoft.com",
"mailNickname": "AcquisitionofACME",
"resourceProvisioningOptions": "['Team']",
"sharepoint": "https://pwnz.sharepoint.com/sites/AcquisitionofACME",
"hasTeams": true,
"owners": [
"cnotin@pwnz.onmicrosoft.com",
"PattiF@pwnz.onmicrosoft.com"
],
"members": [
"cnotin@pwnz.onmicrosoft.com",
"NestorW@pwnz.onmicrosoft.com",
"MiriamG@pwnz.onmicrosoft.com",
"PattiF@pwnz.onmicrosoft.com",
"LeeG@pwnz.onmicrosoft.com"
]
},
[…]
> python reporting.py
> cat all_groups.csv
Name;Visibility;Teams enabled;Owners;Members
[…]
Acquisition of ACME;Private;True;"cnotin@pwnz.onmicrosoft.com;PattiF@pwnz.onmicrosoft.com";"cnotin@pwnz.onmicrosoft.com;NestorW@pwnz.onmicrosoft.com;MiriamG@pwnz.onmicrosoft.com;PattiF@pwnz.onmicrosoft.com;LeeG@pwnz.onmicrosoft.com"
my standalone public group;Public;False;cnotin@pwnz.onmicrosoft.com;cnotin@pwnz.onmicrosoft.com
[…]
✔️ Recommendations 🔗
Now that we understood the risks of public M365 Groups, given the high impact and how easy it is to discover them and browse their content, how can we protect against this?
- The first option would be to block the creation of public Groups, but I did not find any documentation about this. We can fully block the creation of Groups by end-users and only allow administrators to do it. But this is not encouraged because the fact that users can self-organize, without going through their IT, is one of the most appealing aspects (see the discussion at the end).
- Another possibility is to simply let users do what they want, while running a script regularly to warn those who own public Groups (for example via the group’s email inbox, or directly in their Teams) to remind them of the risks. We can imagine a link to accept the risk and make it remembered. Organizations who are stricter can also have a similar script but to forcefully switch to private the identified public Groups. I did not try it but I think it could be a nice use-case for Microsoft Power Automate or an Azure Function using the Microsoft Graph API.
- If an organization is able to identify its “VIP users”, it can also have a closer scrutiny of the groups they own or are members of considering that they more probably handle sensitive information.
- I also hope that commercial Data Leak Prevention and Data Protection solutions are capable of identifying these public M365 Groups, directly, or not (e.g. via their SharePoint). I did not do any benchmark, so I have nothing to share, but you should challenge them!
💣 Risks of private Teams / M365 Groups 🔗
Now that we covered public M365 Groups, what about private ones? They shouldn’t have any issue, right?
If we assume that there is no vulnerability in their implementation (you can get a nice bounty otherwise!), and therefore non-members cannot join them or browse their data, what could go wrong?
Metadata!
Indeed, anyone in the organization can list private Groups and see their:
- Name
- Description
- Members (owners and normal members)
For example, even if unauthorized users may not see the Teams chat, nor the shared files, but when they see a private Group created by company board members about some acquisition or divestiture… this alone is a sensitive information!
Here is an example:
This private group looks really interesting especially when we look at the organizational chart of this fictive company:
We saw above that anyone can list those without any technical knowledge (for example on “join groups”, but note that Teams will not show private ones) nor particular permissions (apart from having an account). Sophisticated attackers can leverage the Microsoft Graph API, that we presented, to search more efficiently or in an automated fashion (for example waiting until the moment when a private Group is mistakenly switched to public!).
To enumerate all Groups in a tenant with their info, including the private ones, you can use my m365_groups_enum Python script, or other options presented above.
✔️ Recommendations 🔗
Some recommendations are similar as for public Groups: prevent their creation (but not ideal), or monitor closely the ones created by VIP users.
- A tenant can apply an M365 Groups naming policy which can contain custom blocked words thus allowing to block certain sensitive keywords such as “acquisition”. But it looks like it only applies to group names, and not their description, and we know that having an exhaustive list is impossible (especially in multi-lingual organizations!).
- Organizations can also raise awareness about this, even though I have no idea what would be the most friendly way to explain this precise concept.
- Regarding the specific case of sensitive topics, it is already common in some organizations to use code names for such operations when discretion is needed. It’s a good practice even against eavesdropping in public or shared places (think hallways, taxis, restaurants…)!
- Technically, Microsoft 365 admins can also apply options on private Groups to prevent non-member from listing their members (HiddenGroupMembershipEnabled, which can only be applied when creating the Group and not after!), and to hide them from search and Global Address List (HiddenFromAddressListsEnabled). Refer to “Creating a Secret Office 365 Group”. I did not test those and I cannot say how effective they are.
- Microsoft also mentioned in July 2020 that:
“The only other scenario currently under consideration where we would hide groups by default is “Secret Groups”. “Secret Groups” would be hidden from everywhere unless you are a member. “Secret groups” is on the Microsoft 365 Groups backlog, but there is no delivery date available at this time.”
🔮 Thoughts on the evolution of IT towards cloud self-serve solutions 🔗
Cloud solutions like Microsoft 365 (M365, previously Office 365 / O365) gives more power and delegation to end-users which they really appreciate. Instead of asking IT people to create resources, with cumbersome and lengthy process (you too know the pain, don’t you?), end-users can just self-serve and organize themselves. But in this situation the IT team isn’t there anymore to save people from themselves, which they must be made aware of especially in traditional organizations where security was historically handled by security people and where people are trained to think that “if the system allows me to do something, then I can assume it’s safe”.
Moreover, in large groups/conglomerates, end-users who are empowered with creating sharing spaces and setting their visibility, may not understand that the “public”, “organization” or “company” scopes, often means the whole conglomerate which means way more people than the usually small subsidiary that employs them. Internal IT folks know about the “tenant” and which companies it comprises, but this notion is not known by everyone.
In a sense, I welcome this evolution because it empowers people, and in the end, people are the best positioned to know who should access the data they own and the collaboration spaces they create. How can a traditional IT department really know if some request to add someone to a shared folder is legitimate? So, this increase in delegation can increase security, but only if people are made aware of their new responsibility, and if IT and cloud providers manage to make it easy and non-confusing.