Dec 10 2024 |
SPA is for Single-Page Abuse! – Using Single-Page Application Tokens to Enumerate Azure
Author: Lance B. Cain
Overview
Microsoft Azure is a leading cloud provider offering technology solutions to companies, governments, and other organizations around the globe. As such, many entitles have begun adopting Azure for their technology needs to include identity, authentication, storage, application management, and web services. One of the most common methods for organizations to begin experimenting with and adopting Azure is by deploying web applications and an increasingly popular method for creating web applications is by designing Single-Page Applications (SPAs). Katie Lawson of bloomreach.com defines SPAs as, “a website or web application that dynamically rewrites a current web page with new data from the web server, instead of the default method of a web browser loading entire new pages”. SPAs typically utilize JavaScript integrations with API calls to dynamically update for a seamless user experience. Microsoft has multiple walkthroughs, guides, and documentation to aid organizations in deploying SPAs on Azure:
- Single-page application (SPA) documentation — https://learn.microsoft.com/en-us/entra/identity-platform/index-spa
- Single-page application: App registration — https://learn.microsoft.com/en-us/entra/identity-platform/scenario-spa-app-registration
- Configure authentication in a sample single-page application by using Azure AD B2C — https://learn.microsoft.com/en-us/azure/active-directory-b2c/configure-authentication-sample-spa-app
As a result, there are many new publicly hosted SPAs that communicate with Azure backend resources.
This blog post is intended to share insights learned from a prior security assessment about the attack surface of Single-Page Applications integrated with Azure, aid technology professionals in securing their Azure environments, and serve as a guide for enumerating Azure tenants using the additions of the pull request I submitted to Dirk-Jan’s ROADTools repository. This blog is not a comprehensive analysis of every SPA hosted on Azure, so your mileage may vary. Due to the widespread adoption of Microsoft’s Office products and web integrations for Azure, I will focus on two popular SPAs that Microsoft offers (i.e., WWW.OFFICE.COM and PORTAL.AZURE.COM); however, the workflow translates to many other SPAs as well.
A Brief Origin Story
My team and I were working on a collaborative cloud assessment for one of our clients targeting their Azure tenant. We were tasked with identifying methods of escalating privileges, assisting defenders in improving detections, and documenting attack paths in the client Azure environment using a compromised non-privileged user account on a Windows Virtual Desktop Image (VDI). We began by enumerating the available applications, Azure resources, Microsoft technologies, and client subscriptions. During the assessment, we learned that the client Azure environment was hardened with enforced multi-factor authentication (MFA), allow-listed MFA conditional access policies (CAPs), administrator restrictions on internal and external application enrollments, administrator approval for external invitations, continuous access evaluation, blocked access to Entra ID in the Azure portal, browser extension host security checks, blocked command-line interface (CLI) and PowerShell access, and time limitations on issued tokens forcing expiration after one hour.
The defensive configurations in the client environment impeded our work obtaining access and refresh tokens to enumerate the target tenant. The team began looking at the resources our user account had access to. We had successfully authenticated as the non-privileged user to the Azure portal and administrator pre-approved Microsoft Office web applications like Excel or Word on HTTPS://WWW.MICROSOFT365.COM. Within the Azure portal, we discovered that the user account had access to the Azure cloud shell which the team used to obtain an access token using the built-in PowerShell cmdlet Get-AzAccessToken and initiate some cursory enumeration.
Pro-Tip: If the cloud shell is available, it is possible to install pip Python modules, including ROADTools, in the Bash shell.
The team started collections to enumerate the client tenant using ROADTools and AzureHound with our initial access token; however, an hour later, we discovered that the tools had failed when the acquired token expired prior to completing their tasks. With the knowledge that the client tenant was so large that automated enumeration would exceed the one-hour lifetime for access tokens, we began looking into methods to update the access token for collections. After some research, we learned one of the likely intentional limitations of Get-AzAccessTokens for the Azure cloud shell that it would obtain access tokens, but it would not return a refresh token. My teammates started working on methods to script out obtaining a new access token in the cloud console, meanwhile I began looking at the access we had to authorized Microsoft web applications.
While investigating the Azure portal, I remembered something that Andy Robbins mentioned at SpecterOps’ Azure Security Fundamentals training; specifically, that the Azure portal was essentially a web application frontend which communicates with the Azure Graph APIs. Thinking about the ROADTools implemented authentication flow to obtain tokens with a Selenium browser; despite not being an option during the assessment due to the required extensions, I opened the inspector panel in the VDI client Google Chrome web browser to look for any type of token exchanges that occurred during authentication. While reviewing the network traffic in the inspector panel, I observed some requests named “token”. I discovered when filtering and inspecting traffic for the term “token” that there were multiple access and refresh tokens contained in the request payloads for resources like MANAGEMENT.CORE.WINDOWS.NET, GRAPH.MICROSOFT.COM, and most interesting GRAPH.MICROSOFT.NET. The access tokens for the client environment still had the one-hour expiration limit, but the refresh tokens were valid for up to four hours. I also discovered that upon selecting the payload tab the client-id used for the request was also captured.
Excited to have obtained a refresh token for our client environment, I attempted authentication over a proxied connection to the VDI with ROADTools using the refresh token, which resulted in the below error response from Azure regarding cross-origin requests.
I began researching the error message and quickly stumbled upon this Stack Overflow post where one of CajunCoding’s comments referenced a Microsoft page stating, “Microsoft identity platform returns an error if you attempt to use a spa redirect URI without an Origin header.”
Upon reviewing the Microsoft documentation, I spent the next hour issuing multiple web API requests to HTTPS://LOGIN.MICROSOFTONLINE.COM/{TENANT}/OAUTH2/V2.0/AUTHORIZE using Burp Suite and curl commands with varying origin HTTP headers. An access token was eventually returned for the supplied refresh token when entering the originating application URL in the request origin header. Upon successfully obtaining a new access token by hand, I updated my local instance of ROADTools to accommodate supplying an origin value when performing authentication with refresh tokens, which resulted in the addition of the origin parameter. Afterwards, my team and I successfully enumerated the client tenant using the available SPA refresh tokens and continued with the assessment. Our client was pleased to learn about the attack path where users with granted access to SPAs like the Azure Portal and Microsoft Online Office Applications could potentially obtain new tokens to enumerate their Azure tenant.
SPA Token Enumeration Workflow
Now with the origin argument added to the latest version of ROADTools, the most basic process of using SPA tokens to enumerate an Azure tenant is:
- On an Azure client workstation, open the inspector panel in a web browser and filter network traffic for “tokens”
- Authenticate to a SPA or re-open an existing browser session to an authenticated SPA
Note: Take notice of the origin URL
3. View exchanged tokens to obtain the refresh token contained in one of the MICROSOFT.NET, MICROSOFT.COM, or other Microsoft integrated resource token requests
Pro-Tip: For small tenants or tenants without short token expirations, access tokens for GRAPH.MICROSOFT.NET can be used from the browser instead of refresh tokens.
Pro-Tip: Some resource tokens have been found to provide only partial collections and will generate HTTP 403 errors like MANAGEMENT.CORE.WINDOWS.NET as demonstrated in the examples. I recommend targeting GRAPH.WINDOWS.NET or SharePoint resource tokens which have yielded the best results
4. Take note of the application client-id used to request tokens
Pro-Tip: For first-party Microsoft Azure applications like HTTPS://PORTAL.AZURE.COM, bookmark this page to quickly lookup the client-id: https://learn.microsoft.com/en-us/troubleshoot/entra/entra-id/governance/verify-first-party-apps-sign-in#application-ids-of-commonly-used-microsoft-applications
5. Authenticate to the Microsoft graph API by supplying all the captured values to ROADTools
roadrecon auth -c <Client-ID> --origin <SPA Base URL> --refresh-token <Browser Refresh Token>
Pro-Tip: If Azure continuous access evaluation is enforced, execute the ROADTools authentication from the original workstation or SOCKs proxy the request through the workstation.
6. Execute your collection
roadrecon gather
7. Review the information
roadrecon gui
Azure Portal Example — Partial Collection with MANAGEMENT.CORE.WINDOWS.NET Token
- Inspect
2. Authenticate
3. Obtain Tokens
4. Note Client ID
5. Authenticate with ROADRecon
6. Gather — 403 Errors = Partial Collection
7. Review — roadrecon gui and browse to HTTP://127.0.0.1:5000
Office 365 Example — Full Collection with SharePoint Token
- Inspect
2. Authenticate
3. Obtain Tokens
4. Note Client ID
5. Authenticate with ROADRecon
6. Gather
7. Review — roadrecon gui and browse to HTTP://127.0.0.1:5000
About
Lance Cain is a Senior Consultant in Adversary Simulation at SpecterOps Inc. He has over eight years of experience in information technology with six of those focused on Red Teaming and Penetration Testing. Lance formerly served as the Engineering Cell Lead of the Marine Corps’ Red Team tasked with payload and command and control (C2) channel development. He now performs a variety of security assessments for SpecterOps clients and specializes in cloud and macOS technologies.
SPA is for Single-Page Abuse! – Using Single-Page Application Tokens to Enumerate Azure was originally published in Posts By SpecterOps Team Members on Medium, where people are continuing the conversation by highlighting and responding to this story.