OAuth is an open authorization standard that facilitates unrelated servers and services working together, allowing access to their assets without sharing the initial, related, single logon credential. I have been thinking of it as a kind of Kerberos for external services, without a shared domain or forest.
A familiar instance would be authentication to any new service, when that service attempts to leverage the assets of another service. If you have ever seen prompts like the image below, you have been using OAuth:
OAuth transactions are generally called flows and are distinct based on their grant type. Many different flows exist and offer different benefits from an offensive security perspective. Let’s explore OAuth specific jargon and then dive into various flows and their pros and cons. For the sake of this blog post, we will be discussing OAuth in the context of Microsoft 365 and Azure AD. However, many opportunities exist for abuse in other service platforms’ implementations as well.
|General OAuth Terminology|
|Client_ID||The Client_ID is a unique identifier for the client involved in the authentication flow.|
|Code||Code refers to a response resulting from authorization – it can be used to query an endpoint for tokens for a set period of time before it expires.|
|Grant Type||Grant type is a term representing the resource owner’s authorization used by the client to obtain an access token in order to access its protected resources. The OAuth specification defines four grant types: authorization code, implicit, resource owner password credentials, and client credentials, as well as an extensibility mechanism for defining additional types. Microsoft has several more types available than the specification, see a full listing here.|
|Scope||Scope is defined as the specific rights expressed for the transaction. An example would include something like Mail.Read, which is somewhat self-explanatory, but full descriptions can be found via online documentation for various platforms. Microsoft lists theirs here.|
|Access Token||Access tokens give clients the ability to make API calls on the behalf of the user. The access token represents the authorization of a specific application to access parts of a user’s data as defined by the scope asserted during the grant process.|
|Bearer Token||The bearer token is simply a type of access token. It is generally defined as bearer because any party in possession of the token can leverage it for access to the application.|
|Refresh Token||The refresh token is a special token that can be leveraged to retrieve a new access or bearer token once they have expired.|
|Client Secret||The client secret is a piece of information only known to the application and the authorization server. It must by random and not guessable.|
|Redirect URL||Redirect URLs are critical to OAuth authentication processes. Once a user has successfully authorized an application, the authorization server will redirect the user back to this URL with either an authorization code or an access token dependent on the type of grant or flow being used.|
With the establishment of base level terms, we can now explore different authentication flows and their offensive benefits. For a fully detailed explanation of each flow related to Microsoft please see the reference here.
The needs of a developer often overlap with offensive operations. As a lazy (efficient?) attacker, I do not want to spend time categorizing domains to bypass web filters, and I don’t want my operations impeded if an Incident Response team realizes that an account is compromised and resets the password. So, how can various authentication flows related to OAuth help meet these requirements? Let’s explore a few different grant flows. First, let’s look at the OAuth 2.0 device authorization grant flow:
This flow was designed to allow devices that might not have browsers to authenticate via OAuth. You may have used a similar flow when you authenticate your TV to various subscription services. The user elects to begin this process, then requests a device_code with a simple post request. This first request and response looks like this (just using curl for demonstration purposes):
Note: Prior to this step, you will need to register an application with Azure, as that is what will provide two key attributes for this flow. One attribute will be your Client_ID and the other will be the redirect URL, which is where your precious tokens will be sent. Setting these up is out of the scope of this blog post, but the process is straight forward and can be found here.
Be aware of the items that have been included in the scope – these are the items you are trying to gain authorization to access. Additionally, for some flows this will determine if you receive a refresh_token. The refresh_token is the premiere credential material that you want, as it can be used to request access_tokens in perpetuity.
The response would look like this:
After this step, for offensive uses, you would provide the lure to your target with a pretext that would make sense for them to enter code into https://microsoft.com/devicelogin. After sending out the lure, you need to poll Microsoft to determine if the user has authenticated you. That post looks like this:
You will receive a response like this:
But if the user has authenticated as they were instructed, you will see this:
On the victim’s end, this attack would look like this:
After the victim enters their credentials, they would receive the following screen:
So, with this flow, you may notice several appealing aspects. You don’t have to leverage any of your own infrastructure for this attack, which means a lot less headache and preparation. The pages presented by Microsoft also only include the name of your custom Azure application, which may make it easier to fool a target. Also, you get a refresh_token, which means if the user’s password changes, you can still access the provisioned services by requesting an access_token with your refresh_token (covered in next blog post). Additionally, if the victim uses multi-factor authentication (MFA), it’s built into this process, and going forward using the tokens doesn’t require a second factor. This is all great, but there is one huge downside to this flow – the code that the user enters to authenticate with Microsoft expires in 15 minutes. So, what if we wanted to work around that? For that, let’s explore one more flow, before we get into using our wonderful tokens.
As I was looking through the documentation on grant flows, I came across this:
The warning above means that this method will be useful. If we were trying to set up a social engineering scenario, we could use this flow, but it only requires a password, so we can do much more with it. As it turns out, this endpoint is ripe for several attacks. First, you can simply password spray it. Just make sure that you build some tooling that makes each request come from a different IP address. Also, ensure that you randomize if you are spraying an organization’s user list – if you spray in alphabetical order, it may generate an alert. It also turns out that the responses from this endpoint allow for user enumeration, which is well known and utilized by many public tools already. Aside from all that, let’s move down our original scenario of social engineering. It helps to start with a visualization of this flow:
Notice that in many places where you see tenant in the URL, you can either put the tenant object ID or you can attempt authentication to endpoints where you put ‘common’ or ‘organization’ in that place instead. This is a good time to talk about some other benefits of attacking via OAuth. If an organization has implemented multi-factor authentication (MFA), most flows will require the second factor. By default, there are several Client_IDs that will allow ROPC (Resource Owner Password Credentials). An example worth noting are the Azure AD PowerShell cmdlets. These are also very handy for an incredible amount of enumeration. Authentication along these lines is sometimes referred to as ‘legacy authentication,’ see here for more details. Additionally, any organization that has Microsoft 365 in any capacity has many endpoints opened by default, regardless of services enabled. This configuration has surprised many organizations. Lastly, it is important to state that I have not compiled a list of all known Client_IDs, but I do know that an attacker can occasionally find ways around conditional access policies if they keep trying combinations of Client_IDs and grant flow types. In the next blog, we will cover how to leverage these tokens.