// CyberDefenders  ·  Cloud Forensics

ConsentStorm

CyberDefenders Medium Entra ID Sign-in Logs, Entra ID Audit Logs, Azure Activity Logs, Office 365 Audit Logs, Azure Diagnostics Logs, Microsoft Sentinel, KQL Query Editor
Initial Access, Persistence, Privilege Escalation, Defense Evasion, Credential Access, Discovery, Lateral Movement, Collection, Exfiltration

Scenario

On January 21, 2026, NexGen Energy’s SOC received alerts for suspicious activity across Microsoft Entra ID and Azure. A Finance department employee interacted with what appeared to be a legitimate internal email, triggering unusual OAuth consent activity and unauthorised access patterns across multiple accounts and Azure resources. Investigation reveals a full multi-stage cloud intrusion attributed to Storm-0558.


Methodology

All log sources in this lab are ingested as Custom Logs with a _CL suffix — AuditLogs_CL, SigninLogs_CL, OfficeActivity_CL, AzureActivity_CL, and AzureDiagnostics_CL. The first thing I did was orient myself with a table inventory:

search *
| summarize count() by Type
| order by count_ desc

This confirmed all five tables were populated with data. One lesson learned early: the _CL schema field names don’t always match what you’d expect from native Sentinel tables. I lost time on Q6 because I ran an external IP lookup on 51.89.156.153, got France (OVH SAS, Roubaix), and couldn’t understand why the lab rejected the answer. The lab has Beijing, CN hardcoded in the SigninLogs location field. The SIEM is the source of truth, not ipinfo. Two habits that would have saved significant time here: always check the full row schema with take 1 before building filters, and always copy-paste answers directly from query output rather than retyping.

The attack chain starts in AuditLogs_CL. Filtering for Email sent activity ordered by ActivityDateTime ascending surfaces the initial phishing email:

AuditLogs_CL
| where ActivityDisplayName == "Email sent"
| project ActivityDateTime, InitiatedByUser, InitiatedByUserPrincipalName, InitiatedByIpAddress, TargetResources
| order by ActivityDateTime asc

The sender is david@nexgenenrgy.com — a typosquat of the legitimate domain, missing the second e. The email landed in marcus.reid@nexgenenergy.com’s inbox at 01:38 AM with subject “URGENT: Verify Your Budget Planning App Access.” The urgency framing is deliberate — designed to push the target to act without scrutiny. The sending IP is 51.89.156.153, the attacker’s primary IP throughout the investigation.

Marcus interacted with the phishing link and granted consent to a malicious OAuth application. Filtering for consent activity confirms it:

AuditLogs_CL
| where ActivityDisplayName has_any ("consent", "Consent to application", "Add delegated permission grant")
| project ActivityDateTime, InitiatedByUser, ActivityDisplayName, TargetResources
| order by ActivityDateTime asc

The app is BudgetPlannerApp — named to blend with the phishing lure. Five permissions were granted: User.Read.All, Files.Read.All, Mail.Read, Notes.Read.All, and Files.ReadWrite.All. That scope gives the attacker full read access to the mailbox, all OneDrive files, all notes, and write access to files — everything needed to operate as the compromised user via Microsoft Graph API without ever needing their password again.

Lateral Phishing from Compromised Account

With OAuth access to marcus.reid’s mailbox, the attacker immediately sent internal phishing emails to three additional targets. The same Email sent query shows marcus.reid@nexgenenergy.com as sender at 02:10, 02:16, and 02:26 AM — all originating from 51.89.156.153, all with subject “Action Required: Verify Budget Planning App Access.”

The attacker is spreading the consent grant attack inward, using a trusted internal address to increase the chance of further victims clicking through.

SharePoint Upload and OneDrive Enumeration

At 02:25 AM, marcus.reid uploaded Immediate_Review.doc to the Finance SharePoint site. The filename is another urgency lure — likely used to deliver the OAuth phishing link to additional internal targets via a trusted document repository.

OfficeActivity_CL
| where OperationName == "FileUploaded"
| where UserId == "marcus.reid@nexgenenergy.com"
| take 1

The more valuable discovery came from enumerating marcus.reid’s OneDrive. The attacker accessed multiple files, and one stood out:

OfficeActivity_CL
| where OperationName == "FileAccessed"
| where UserId == "marcus.reid@nexgenenergy.com"
| order by ActivityDateTime asc

Provisioning-Script.ps1 — sitting in the user’s personal OneDrive documents folder at /personal/marcus_reid_nexgenenergy_com/Documents/. A PowerShell provisioning script with hardcoded service account credentials. This is one of the most common credential exposure patterns in cloud environments: a script written for convenience, never intended to be shared, sitting in a personal cloud drive that an OAuth-compromised account can read silently.

Service Account Pivot — svc-provisioner

The credentials from Provisioning-Script.ps1 belonged to svc-provisioner@nexgenenergy.com. The attacker authenticated with this account from the same IP and began Azure resource group enumeration:

SigninLogs_CL
| where UserPrincipalName == "marcus.reid@nexgenenergy.com"

AzureActivity_CL
| where OperationName has "resourceGroups"
| where Caller has "svc-provisioner"
| summarize count() by ActivityStatus, ResourceGroup
| order by ActivityStatus asc

Two resource groups read successfully — RG-WebServices and RG-SharedOps. Two blocked — RG-FinanceCore and RG-Networking. The failures weren’t generic 403s though. Filtering on the ActivitySubStatus field revealed that RG-FinanceCore and its Key Vault kv-financecore returned Forbidden - ABAC condition not met:

AzureActivity_CL
| where ActivitySubStatus has "ABAC"

ABAC (Attribute-Based Access Control) in Azure allows access conditions to be tied to resource attributes — in this case, a resource tag. The attacker couldn’t read kv-financecore yet, but now knew the target existed.

Automation Account — Credential Escalation

Within RG-SharedOps — one of the accessible resource groups — the attacker found the AutomateOps Automation Account and read its runbooks:

AzureActivity_CL
| where OperationName has "automationAccounts"

Runbooks frequently contain hardcoded credentials for the service accounts they provision. The attacker read the Provision-Resources runbook output and extracted credentials for a second service account: svc-automation@nexgenenergy.com. This is visible in SigninLogs_CL — the new account authenticating from 51.89.156.153 shortly after:

SigninLogs_CL
| where IPAddress == "51.89.156.153"
| where UserPrincipalName has "svc"

Privilege Escalation — Group Membership and ABAC Bypass

With svc-provisioner still active, the attacker added it to the Finance-Operations group at 03:48 AM:

AuditLogs_CL
| where ActivityDisplayName has "Add member to group"
| where TargetResources has "svc-provisioner"

This group membership was the key that unlocked the ABAC condition on kv-financecore. The ABAC policy was conditioned on a resource tag — specifically CostCenter = FIN001. The attacker wrote this tag directly to the Key Vault resource:

AzureActivity_CL
| where Caller has "svc-provisioner"

Once the tag was applied, the ABAC condition was satisfied and kv-financecore became readable. This is a subtle but critical attack path — Azure ABAC policies that rely on resource tags are only as secure as the permissions around who can write tags. If a service account can modify tags, it can potentially satisfy its own access conditions.

Key Vault Secret Retrieval

With ABAC bypassed, svc-provisioner retrieved the secret jessica-turner-cred from kv-financecore:

AzureDiagnostics_CL
| where OperationName == "SecretGet"
| where CallerIpAddress == "51.89.156.153"

The operation name recorded in AzureDiagnostics for a successful secret retrieval is SecretGet — useful to know for detection rules.

Persistence — Client Secret and Temporary Access Pass

Before moving to the high-privilege account, svc-automation added a new client secret to BudgetPlannerApp with Key ID 0309ccc7-c9ea-4b95-a8ee-e886b6c25422:

AuditLogs_CL
| where InitiatedByUser has "svc-automation"

This ensures that even if the original OAuth consent grant is discovered and revoked, the attacker retains a working credential for the application and can re-authenticate independently.

Using jessica-turner-cred, the attacker authenticated as jessica.turner@nexgenenergy.com — confirmed in SigninLogs_CL:

SigninLogs_CL
| where UserPrincipalName has "jessica"
| where IPAddress == "51.89.156.153"

Jessica Turner is a high-privilege account. The attacker used it to create a Temporary Access Pass for david.chen@nexgenenergy.com at 04:56 AM:

AuditLogs_CL
| where ActivityDisplayName has_any ("Create temporaryAccessPass", "Temporary access pass")

A TAP is a time-limited, PIN-based credential that bypasses normal authentication requirements including MFA. The default maximum validity period is 8 hours. By creating a TAP for david.chen, the attacker gained a clean authentication path to a second high-value account without needing to know or crack their password.

Final Stage — Data Exfiltration

The attacker authenticated as david.chen@nexgenenergy.com from a second IP, 176.31.90.129, indicating a separate egress point for the final exfiltration phase:

SigninLogs_CL
| where UserPrincipalName has "david.chen"

Seven files were accessed from the Finance SharePoint site, starting with Investor-Presentation-2026.pptx:

OfficeActivity_CL
| where UserId == "david.chen@nexgenenergy.com"
| where OperationName == "FileAccessed"

All seven files were from the Finance site — confidential financial documents. The attacker came in through a phishing email and walked out with investor-grade financial data without triggering a single password reset or MFA challenge.


Attack Summary

Phase Action
Initial Access Typosquat phishing email from david@nexgenenrgy.com to marcus.reid
Credential Access Illicit OAuth consent grant — BudgetPlannerApp granted 5 permissions
Lateral Phishing 3 internal phishing emails sent from compromised marcus.reid account
Collection Immediate_Review.doc uploaded to Finance SharePoint
Credential Access Provisioning-Script.ps1 found in OneDrive — svc-provisioner credentials extracted
Lateral Movement svc-provisioner authenticated from 51.89.156.153
Discovery Azure resource group enumeration — 2 read, 2 blocked by ABAC
Credential Access AutomateOps runbook read — svc-automation credentials extracted
Privilege Escalation svc-provisioner added to Finance-Operations group
Defense Evasion CostCenter=FIN001 tag written to kv-financecore to satisfy ABAC condition
Credential Access jessica-turner-cred retrieved from kv-financecore via SecretGet
Persistence Client secret 0309ccc7 added to BudgetPlannerApp by svc-automation
Lateral Movement jessica.turner authenticated — high-privilege account compromised
Persistence TAP created for david.chen@nexgenenergy.com
Exfiltration 7 Finance files accessed from david.chen account via 176.31.90.129

IOCs

Type Value
IP (Primary Attacker) 51[.]89[.]156[.]153
IP (Final Stage) 176[.]31[.]90[.]129
Email (Attacker) david@nexgenenrgy[.]com
App BudgetPlannerApp
App Client Secret Key ID 0309ccc7-c9ea-4b95-a8ee-e886b6c25422
Account (Compromised) marcus.reid@nexgenenergy[.]com
Account (Service) svc-provisioner@nexgenenergy[.]com
Account (Service) svc-automation@nexgenenergy[.]com
Account (High Privilege) jessica.turner@nexgenenergy[.]com
Account (Final Target) david.chen@nexgenenergy[.]com
File Provisioning-Script.ps1
File Immediate_Review.doc
File Investor-Presentation-2026.pptx
Key Vault kv-financecore
Secret jessica-turner-cred
Tag (ABAC Bypass) CostCenter = FIN001
Threat Actor Storm-0558

MITRE ATT&CK

Technique ID Description
Spearphishing Link T1566.002 Typosquat phishing email delivering OAuth consent link
Steal Application Access Token T1528 Illicit consent grant — BudgetPlannerApp OAuth token
Cloud Accounts T1078.004 Service account compromise via hardcoded credentials
Credentials in Files T1552.001 Provisioning-Script.ps1 with hardcoded service account creds
Cloud Service Dashboard T1538 Azure resource group and Automation Account enumeration
Additional Cloud Credentials T1098.001 Client secret added to BudgetPlannerApp for persistence
Modify Authentication Process T1556 Temporary Access Pass created for david.chen
Domain Policy Modification T1484.002 svc-provisioner added to Finance-Operations group
Data from Cloud Storage T1530 7 Finance SharePoint files accessed via david.chen
Credentials from Password Stores T1555 jessica-turner-cred retrieved from Azure Key Vault

Defender Takeaways

Disable user consent for OAuth applications — the entire attack chain begins because a standard user could grant a third-party application access to their mailbox and files without admin approval. Setting the tenant-wide consent policy to “Do not allow user consent” forces all app consent through an admin review workflow, eliminating the illicit consent grant vector entirely.

Audit Automation Account runbooks for hardcoded credentialsAutomateOps contained a runbook that exposed svc-automation credentials in plaintext job output. Runbooks should authenticate exclusively via Managed Identity or credential assets with restricted access, never via hardcoded strings. Regular audits of runbook content and job output logs should be part of the cloud security baseline.

Restrict tag write permissions on sensitive resources — the ABAC bypass worked because svc-provisioner had sufficient permissions to write resource tags to kv-financecore. ABAC policies conditioned on tags are only effective if tag modification is tightly controlled. Restricting Microsoft.Resources/tags/write on sensitive resources to dedicated privileged roles closes this bypass path.

Monitor for Temporary Access Pass creation — TAP creation by a service account or outside of a standard IT provisioning workflow is a strong indicator of compromise. AuditLogs activity Create Temporary Access Pass should trigger an alert whenever the initiating account is not a known IT admin identity. The 8-hour default validity window gives an attacker significant dwell time if this goes undetected.

Treat OAuth consent grant events as high-priority alertsAdd delegated permission grant in AuditLogs is one of the most reliable signals for this attack class. Alerting on any non-admin user granting permissions to an unverified publisher application, especially with scopes like Mail.Read or Files.ReadWrite.All, would have surfaced this attack within minutes of the initial phishing click.


What is the email address of the attacker in the initial phishing email?
Click flag to reveal david@nexgenenrgy.com
What is the display name of the malicious OAuth application that the user granted consent to, and how many permissions were requested?
Click to reveal answer BudgetPlannerApp, 5
After gaining initial access, how many phishing emails did the attacker send from the compromised account?
Click flag to reveal 3
What is the name of the malicious document that was uploaded to SharePoint?
Click to reveal answer Immediate_Review.doc
The attacker enumerated the compromised user's OneDrive files and discovered a PowerShell script containing credentials. What is the name of this script?
Click flag to reveal Provisioning-Script.ps1
What is the attacker's first IP address used in this attack, and from which country does this IP originate?
Click to reveal answer 51.89.156.153, China
Using the service account credentials discovered in the victim’s OneDrive script, the attacker attempted to enumerate Azure resource groups. How many unique resource groups were successfully read, and how many unique resource groups were blocked due to access restrictions?
Click flag to reveal 2, 2
Which resources were blocked by ABAC restrictions?
Click to reveal answer RG-FinanceCore, kv-financecore
The attacker explored the Automation Account to find credentials. What is the name of the Automation Account that was accessed?
Click flag to reveal AutomateOps
What is the name of the second service account that the attacker compromised using the credentials discovered in the runbook?
Click to reveal answer svc-automation@nexgenenergy.com
After bypassing ABAC restrictions, the attacker accessed the Key Vault and retrieved a secret. What is the name of the secret that was retrieved?
Click flag to reveal jessica-turner-cred
The attacker used the credentials from the Key Vault to authenticate as a high-privilege account. What is the user principal name of this account?
Click to reveal answer jessica.turner@nexgenenergy.com
The second service account added a new client secret to the BudgetPlannerApp to maintain persistent access. What is the Key ID of this secret?
Click flag to reveal 0309ccc7-c9ea-4b95-a8ee-e886b6c25422
The high-privilege account created a time-limited credential for another user account. At what time was this credential created? (Hint: ActivityDateTime)
Click to reveal answer 01/21/2026 04:56:50
The first service account was added to a high-privilege group to escalate privileges. What group was the account added to, and when did this group membership modification occur?
Click flag to reveal Finance-Operations, 01/21/2026 03:48:00
To bypass ABAC restrictions, the attacker modified a resource tag. What is the tag key and value that was added to the resource?
Click to reveal answer CostCenter, FIN001
What is the second IP address used by the attacker during the final phase of the attack?
Click flag to reveal 176.31.90.129
Using the temporary access pass, the attacker authenticated as the final target account. What is the name of the first file that was accessed?
Click to reveal answer Investor-Presentation-2026.pptx
How many files in total were accessed by the attacker on the final target account?
Click flag to reveal 7
Based on the IOCs (Indicators of Compromise) identified in this investigation, what threat actor group is responsible for this attack?
Click to reveal answer Storm-0558
What is the MITRE ATT&CK technique ID for the 'Illicit Consent Grant' attack pattern used in the initial compromise phase?
Click flag to reveal T1528
What is the MITRE ATT&CK technique ID for the 'Persistence' phase where the attacker created a Temporary Access Pass?
Click to reveal answer T1556
What is the operation name recorded in the AzureDiagnostics logs when a secret is successfully retrieved from a Key Vault?
Click flag to reveal SecretGet
What authentication control, if enabled on the initial compromised user's account, would have prevented the attacker from granting consent to the malicious application?
Click to reveal answer Do not allow user consent
What is the default maximum validity period (in hours) for a Temporary Access Pass in Microsoft Entra ID?
Click flag to reveal 8
🔒
// active lab
writeup locked
withheld in accordance with platform guidelines
to avoid spoiling live challenges.
password provided to recruiters on request.