04 Sep, 2019

Azure Guest Users - Risks and Security Considerations

by Alex Useche

Microsoft Azure has a tenant-level feature that allows all Azure Active Directory (AAD) members to create and invite guest users. The official name for this feature is Azure Active Directory B2B. The idea is simple: say your company needs to collaborate with an external vendor or a consultant, and you need to show them some nifty app demos you have set up in your company’s Azure tenant. You don’t want to make the app available for everyone on the internet to see. An easy solution to this problem is to invite them into your tenant as a guest user. Once you have sent them an invite, you can pull the guest user’s profile in AAD and assign them access to subscriptions and grant them roles as needed. This is indeed very convenient. As it is often the cases with convenient features, this comes with its own set of risks.

By default, every AAD member in your tenant can create and invite guest users. This option is set under the “User Settings” section of your Azure Active Directory, under “External collaboration settings:”

External Collaboration Settings

If the inviter has sufficient IAM roles assigned, they can also assign guest users roles and privileges as needed. This sounds great, but there are some security concerns that you may want to keep in mind:

1) Giving every AAD user the ability to invite guest users can create a user administration nightmare. Maybe I am a bit of an alarmist, but keep in mind that you are essentially handing a right that was previously reserved to sysadmins to every user in your tenant.

2) It can be challenging to find out who created guest users and whether those accounts are still needed. Further, there are no options that allow you to grant only temporary access to guest users.

3) Guest users do not have access to any Azure resources unless expressly granted roles for subscriptions within your tenant. However, they do have the ability to look up users in your AAD by Principal Names.

In this post, we will focus on the third and last point. Microsoft states in their documentation that “a guest user can retrieve information about another user by providing the User Principal Name or objectId.” They are not very specific as to how users can pull AAD user information, so let’s test that out ourselves. We will start by creating a guest user in an Azure tenant called “Cyberpunk Enterprises.” We will invite the user but will not grant them access to any subscription.

To create a guest user:

1) Open Azure Active Directory. 2) Click on “Users.” 3) Click on “New Guest User” and enter the user’s email, along with a lovely welcome message to be sent with their invite.

This is what the AAD for Cyberpunk Enterprises looks like after inviting “codedharma,” our new guest user:

AAD with guest user

The guest user will get the invite in their email. Now, let’s pretend we are the guest user and accept the invite (assuming we have access to the email, of course), log in to the Azure Portal with the guest account, and try to access AAD. This is what the guest user sees:

no users for guest users

As you see, the guest user is not able to list users in AAD. However, if we take a look at the requests that the browser makes to Azure while navigating the AAD menus from the portal using a proxy, we see several interesting calls:

graph API calls

There is one request in particular that looks very useful from a security perspective: the graph API call highlighted above. Microsoft’s Graph API is advertised as a “gateway” to Azure AD (among other things). You can see some useful use cases for Graph API here. While digging in the Graph API documentation we find that there is an endpoint that could allow us to get a user by referencing either their object ID (very difficult to guess) or principal name (very easy to guess). The documentation for the Get User call can be found here.

This is when we put our OSINT hat and on and go to LinkedIn, look for employees of Cyberpunk Enterprises, and gather a list of potential names. In this case, let’s say that we collect the following names:

  • Alex Useche
  • Dean Moriarty
  • Carlo Marx
  • Old Bull Lee (who sounds like a very hip guy)

All we need to do now is figure out the principal name for each user, which is usually their corporate email address or a variation of the same with the format of <companyname>.onmicrosoft.com. Let’s try and see if we can pull the data for “Dean Moriarty” by using the principal name “dean.moriarty@cyberpunkenterprises.onmicrosoft.com.” Since we captured a request to the Graph API using a proxy (in this case Burp Suite), all the authentication headers are already set for us, and we only need to modify the path to /myorganization/users/dean.moriarty@cyberpunkenterprises.onmicrosoft.com:

burp request

That was (relatively) easy, wasn’t it? Granted, not all data can be pulled. For instance, the profile for Dean Moriarty looks like this to a non-guest AAD user from the portal:

Dean Mo-ri-ar-ty

However, the only data that is returned to guests is the following:

  • odata.metadata
  • odata.type
  • objectType
  • objectId
  • signInNames
  • cloudSecurityIdentifier
  • displayName
  • netId
  • thumbnailPhoto@odata.mediaEditLink
  • userType

All other fields, such as job title and address details, are returned with null values, as seen in the screenshot above. This is certainly not much information, but as a guest user, I now have a way to verify the existence of any user in AAD. I can also verify whether a user is removed, and gather other information (such as objectId values) for any user, which may become useful later as I formulate other attacks.

These can be, for many sysadmins sufficient reasons to disable the ability for all users to create guest users.

So what can you do? Simply do not allow your users to create and invite guest users unless strictly necessary. It is also a good idea to have well-documented onboarding and offboarding processes in place. Additionally, regular audits of guest users and service principals (which we will discuss in a future post) should be performed. This ensures that all unneeded accounts are removed. You can start by getting a list of your guest users in your tenant by using the following Azure CLI command:

$ az ad user list --filter "userType eq 'Guest'"

To list guest users who were created before a particular date, you can use the following command:

$ az ad user list --filter "userType eq 'Guest' and createdDateTime le datetime'2018-08-01T00:00:00Z'"

You will notice a field in the results called userState. This field indicates whether the guest user has accepted the invite or not. However, the following command to list all users who have not accepted the invite returns an error:

$ user list --filter "userType eq 'Guest' and userState le 'Accepted'"
Unsupported or invalid query filter clause specified for property 'userState' of resource 'User'.

This is likely because only guest users have the userState field. Instead, you can filter on the userStateChangedOn field, which will tell you when the userState field last changed. The following Azure CLI command lists guest users who accepted their invites or were invited before August 2018.

$ az ad user list --filter "userType eq 'Guest' and userStateChangedOn le datetime'2018-08-01T00:00:00Z'"

You can use the results to help you determine whether a guest account is still being used or not. Alternatively, you can leverage Graph API libraries for languages like Python to automate further analysis of AAD data.

This issue, in particular, highlights the need to question default settings for Azure (or any other service, really). The solution will, of course, depend on the needs of your organization. It is important, however, to make educated decisions and document any risks that exist when enabling features such as the one discussed in this post.

We will explore other Azure topics in upcoming posts, and will dig deeper into other Azure specific topics, such as security considerations of Azure Service Principals and App Registrations.