// CyberDefenders  ·  Cloud Forensics

Shadow Token Symphony - APT29

CyberDefenders Medium Microsoft Sentinel
Initial Access, Execution, Persistence, Privilege Escalation, Defense Evasion, Credential Access, Discovery, Lateral Movement

Scenario

InfiniteTechSolutions recently experienced suspicious activity in their Azure environment. Using Microsoft Sentinel, the security team detected unusual login patterns, unauthorized service installations, and anomalous API calls targeting their Microsoft Graph endpoint. Multiple user accounts appear to have been compromised, and there are signs of privilege escalation and persistent access mechanisms being established. The incident occurred in July 2025, with activities spanning across various systems including workstations and cloud services.


Methodology

This lab spans five log sources ingested as custom tables into a Log Analytics workspace: Event_CL (Windows Security events), SigninLogs_CL (Azure AD interactive sign-ins), AADNonInteractiveUserSignInLogs_CL (token-based silent auths), MicrosoftGraphActivityLogs_CL (Graph API calls), and AzureDiagnostics_CL / AuditLogs_CL (Azure resource and directory audit events). All tables use the custom log _CL suffix, meaning native Sentinel field names don’t apply — schema needs to be confirmed via getschema before querying, and familiar fields like TimeGenerated may not reflect the actual event time. CreatedDateTime is the authoritative timestamp in the Azure AD log tables.

Phase 1 — On-Prem Reconnaissance and Initial Access

The first pivot is Windows event logs. Querying Event_CL for EventID 4625 (failed logon) across all machines reveals the breadth of the initial brute force — four distinct computers with login failures, with ITWS01 taking the heaviest volume at 17 failed attempts.

Event_CL
| where EventID == "4625"
| summarize dcount(Computer)

Event_CL
| where EventID == "4625"
| summarize FailedAttempts = count() by Computer
| order by FailedAttempts desc

The failed logon events on ITWS01 contain TargetUserName: ADMINISTRATOR sourced from 80.94.95.75 via NTLM LogonType 3 — a network brute force against the local administrator account from an external IP. Successful 4624 events from the same IP confirm the attacker eventually got in.

Phase 2 — Lateral Movement via PsExec-Style Service Install

With the ADMINISTRATOR account compromised, the attacker used PsExec-style execution to establish a foothold on ITWS01. EventID 7045 (service installed) surfaces a service named c316a11 — a random hex string characteristic of PsExec’s auto-generated service naming. The image path \\127.0.0.1\ADMIN$\c316a11.exe confirms execution via the admin share against the local machine, indicating the attacker was running PsExec from an already-authenticated session.

Event_CL
| where EventID == "7045"
| where Computer == "ITWS01"
| extend ServiceName = extract(@"Service Name:\s*(.*?)(\.|$)", 1, RenderedDescription)
| project TimeGenerated, Computer, ServiceName, RenderedDescription
| order by TimeGenerated asc

The 7045 event only records LocalSystem as the service run-as account — it does not capture who issued the install. The installing account lives in the UserName field on the raw event record, not inside the EventData XML. Expanding the full row for the c316a11 event reveals ITWS01/infinitetechadmin as the account that ran the PsExec session.

Event_CL
| where EventID == "7045"
| where Computer == "ITWS01"
| where EventData contains "c316a11"

Phase 3 — Azure AD Password Spray

With a foothold established on the endpoint, the attacker pivoted to Azure AD. Querying SigninLogs_CL for ResultType == "50126" (invalid username or password) gives the total spray volume — 63 failed authentication attempts across the tenant.

SigninLogs_CL
| where ResultType == "50126"
| summarize count()

Two accounts hit the lockout threshold during the spray. ResultType == "50053" (account locked out) identifies them as Sarah Miles and Alice Jones — both generating lockout events that signal the spray was running fast enough to exceed the tenant’s lockout policy.

SigninLogs_CL
| where ResultType == "50053"
| summarize by Identity

To count the unique IPs involved in the spray, the correct approach is to exclude successes rather than enumerate specific failure codes. Using ResultType != 0 captures 50126 failures and 50053 lockout events in a single pass — the same logic as EventID != 4624 in Windows logon monitoring. Filtering only on 50126 undercounts by missing the lockout IPs, which is how we ended up one short repeatedly before flipping the logic. The correct count is 52 unique IPs.

SigninLogs_CL
| where ResultType != 0
| summarize dcount(IPAddress)

Phase 4 — Initial Cloud Compromise

Filtering SigninLogs_CL for ResultType == "0" and ordering by CreatedDateTime ascending identifies the first successful authentication. Maya Wilson authenticated from 35.158.160.255 at 2025-07-01 19:21. Alice Jones also shows successful logins but from the 3.72.100.x range — consistent with the corporate subnet visible throughout the logs. The attacker IP 35.158.160.255 is the distinguishing factor.

SigninLogs_CL
| where ResultType == "0"
| project TimeGenerated, CreatedDateTime, Identity, IPAddress, UserPrincipalName
| order by CreatedDateTime asc
| take 10

Filtering specifically on Maya Wilson and projecting UserAgent alongside CreatedDateTime confirms the timestamp and surfaces the attacker’s browser fingerprint — a modern Edge build on Windows 10.

SigninLogs_CL
| where ResultType == "0"
| where Identity == "Maya Wilson"
| project TimeGenerated, CreatedDateTime, Identity, IPAddress, UserAgent
| order by CreatedDateTime asc
| take 1

Phase 5 — Token Abuse and Silent Authentication

Four minutes after the initial interactive login, the attacker began non-interactive authentication — silent token reuse that leaves a much lighter footprint than repeated interactive logins. AADNonInteractiveUserSignInLogs_CL confirms the first silent auth from 35.158.160.255 at 2025-07-01T19:25:14Z. The token type leveraged is a refreshToken — obtained from the initial interactive session and reused silently across subsequent requests to Azure Resource Manager without triggering MFA or Conditional Access re-evaluation.

AADNonInteractiveUserSignInLogs_CL
| where UserDisplayName == "Maya Wilson"
| project TimeGenerated, CreatedDateTime, UserDisplayName, IPAddress, IncomingTokenType
| order by CreatedDateTime asc
| take 5

Pivoting to non-interactive auths after the compromise timestamp and excluding Maya Wilson surfaces a second compromised account — Tom Clarkson, with 12 silent auth events from the same attacker IP. This indicates the attacker either compromised Tom’s credentials separately or pivoted to his account via token theft.

AADNonInteractiveUserSignInLogs_CL
| where ResultType == "0"
| where CreatedDateTime > datetime(2025-07-01T19:21:19Z)
| where UserDisplayName != "Maya Wilson"
| summarize count() by UserDisplayName, IPAddress
| order by count_ desc

Phase 6 — Graph API Reconnaissance

With valid tokens in hand, the attacker queried Microsoft Graph for directory intelligence across two separate IPs. From 48.211.64.27, the delta sync endpoint was called — https://graph.microsoft.com/beta/users/microsoft.graph.delta(). The delta sync pattern is particularly efficient for directory harvesting: a single call returns the full user list, and the returned deltatoken enables incremental syncs to track any subsequent changes without re-pulling the entire directory.

MicrosoftGraphActivityLogs_CL
| where RequestUri contains "users"
| summarize count() by IPAddress, RequestUri
| order by count_ desc

From a separate IP, 51.11.96.191, the attacker queried the organization endpoint — pulling tenant-level configuration including verified domains and tenant ID, useful for downstream phishing infrastructure and identifying trust relationships.

MicrosoftGraphActivityLogs_CL
| where IPAddress != "48.211.64.27"
| where IPAddress !startswith "3.72.100"
| summarize count() by IPAddress, RequestUri
| order by count_ desc

Phase 7 — Automation Account Persistence

The attacker compromised the DAILYCHECKER Azure Automation account and created a runbook named UsersReminders, linked to a schedule named DailyUsersReminder. Unlike endpoint-based persistence mechanisms, Automation runbooks survive reimaging, password resets, and most IR playbooks — they execute in Azure’s compute infrastructure independently of any on-prem endpoint state.

AzureDiagnostics_CL
| where ResourceType contains "AUTOMATION" or OperationName contains "automation"
| project TimeGenerated, OperationName, Resource, ResourceGroup
| take 20

AzureDiagnostics_CL
| where Resource == "DAILYCHECKER"
| where targetResourcesResources == "Runbook"

AzureDiagnostics_CL
| where Resource == "DAILYCHECKER"
| order by TimeGenerated asc
| take 20

Phase 8 — Privilege Escalation via Application Ownership

Several hours post-compromise, Tom Clarkson’s account executed an Add owner to application operation against InfinivaultApp (application ID 9999-8888). Adding ownership to an Azure AD application grants full control over its credentials and API permissions — the owner can add new client secrets or certificates, effectively inheriting the application’s full permission scope. This is a low-noise escalation path compared to direct role assignments, appearing only in AuditLogs as a single event.

AuditLogs_CL
| where OperationName == "Add owner to application"
| project TimeGenerated, OperationName, InitiatedBy, TargetResources
| order by TimeGenerated asc

With elevated application permissions secured, the attacker returned to DAILYCHECKER and updated the UsersReminders runbook — embedding new payload or credentials reflecting the escalated privilege level. Tom Clarkson’s non-interactive auth sessions from 35.158.160.255 confirm that IP as the source of the automation updates.

AzureDiagnostics_CL
| where Resource == "DAILYCHECKER"
| where OperationName == "Update"
| project TimeGenerated, OperationName, targetResourcesResources, ResultDescription
| order by TimeGenerated asc

AADNonInteractiveUserSignInLogs_CL
| where UserDisplayName == "Tom Clarkson"
| where ResultType == "0"
| project TimeGenerated, CreatedDateTime, IPAddress, UserDisplayName
| order by CreatedDateTime asc

Phase 9 — Key Vault Exfiltration

The final phase targets secrets storage. AzureDiagnostics_CL filtered for Key Vault operations reveals three vaults accessed: CORP-KV-PROD, INFRA-BACKUP-KV, and FINANCE-KV-EU — spanning production credentials, infrastructure backup keys, and finance-scoped secrets. The breadth of vaults accessed suggests the attacker had mapped the tenant’s Key Vault inventory during the Graph API recon phase and targeted them systematically.

AzureDiagnostics_CL
| where OperationName contains "Secret" or OperationName contains "vault"
| summarize count() by Resource


Attack Summary

Phase Action
Reconnaissance NTLM brute force against ADMINISTRATOR from 80.94.95.75 across 4 workstations
Initial Access ADMINISTRATOR account compromised on ITWS01
Lateral Movement PsExec-style service c316a11 installed via \\127.0.0.1\ADMIN$ using infinitetechadmin
Credential Access Azure AD password spray — 63 failed attempts, 52 unique IPs, 2 accounts locked
Initial Cloud Compromise Maya Wilson compromised at 2025-07-01 19:21 from 35.158.160.255
Persistence (Token) refreshToken reuse via non-interactive auth — Maya Wilson then Tom Clarkson
Discovery Graph API delta sync from 48.211.64.27 — full user directory harvested
Discovery Organization endpoint queried from 51.11.96.191 — tenant config enumerated
Persistence (Cloud) DAILYCHECKER automation account — UsersReminders runbook + DailyUsersReminder schedule
Privilege Escalation Tom Clarkson added as owner of InfinivaultApp (9999-8888)
Defense Evasion Runbook updated post-escalation to embed elevated-privilege payload
Exfiltration CORP-KV-PROD, INFRA-BACKUP-KV, FINANCE-KV-EU secrets accessed

IOCs

Type Value
IP (Brute Force Source) 80[.]94[.]95[.]75
IP (Cloud Attacker C2) 35[.]158[.]160[.]255
IP (Graph Recon — Users) 48[.]211[.]64[.]27
IP (Graph Recon — Org) 51[.]11[.]96[.]191
Account (On-Prem Compromised) ITWS01/infinitetechadmin
Account (Cloud Compromised) maya.wilson@infinitechsolutions[.]xyz
Account (Cloud Lateral) tom.clarkson@infinitechsolutions[.]xyz
Service (Malicious) c316a11
Service Path \127[.]0[.]0[.]1\ADMIN$\c316a11.exe
Automation Account DAILYCHECKER
Runbook UsersReminders
Schedule DailyUsersReminder
Application InfinivaultApp (9999-8888)
Key Vault CORP-KV-PROD
Key Vault INFRA-BACKUP-KV
Key Vault FINANCE-KV-EU

MITRE ATT&CK

Technique ID Description
Valid Accounts: Cloud Accounts T1078.004 Maya Wilson and Tom Clarkson cloud accounts compromised and abused
Brute Force: Password Spraying T1110.003 52 unique IPs across 63 failed auth attempts against Azure AD
Create or Modify System Process: Windows Service T1543.003 c316a11 PsExec-style service installed on ITWS01
Steal Application Access Token T1528 refreshToken reused for non-interactive silent authentication
Use Alternate Authentication Material: Application Access Token T1550.001 Token reuse across Maya Wilson and Tom Clarkson sessions
Create Cloud Instance T1136.003 UsersReminders runbook created in DAILYCHECKER automation account
Account Manipulation: Additional Cloud Roles T1098.003 Tom Clarkson added as owner of InfinivaultApp
Data from Cloud Storage T1530 CORP-KV-PROD, INFRA-BACKUP-KV, FINANCE-KV-EU Key Vaults accessed
Cloud Service Discovery T1526 Graph API delta sync and organization endpoint enumerated
Account Discovery: Cloud Account T1087.004 Graph delta endpoint used to harvest full Azure AD user directory

Defender Takeaways

Monitor AADNonInteractiveUserSignInLogs for token reuse from anomalous IPs. This is a log source many orgs don’t actively alert on. Silent auth events from unexpected geolocations or IPs appearing shortly after an interactive login from the same source are a strong signal of token theft. The 4-minute gap between Maya Wilson’s interactive login and first non-interactive auth is exactly the window a defender needs to catch — a Sentinel analytic rule correlating interactive and non-interactive sign-ins from the same IP within a short time window provides durable coverage.

Use ResultType != 0 for spray detection, not specific error codes. Enumerating failure codes like 50126 misses lockout events (50053) and other failure conditions, undercounting the true attacker IP footprint. The correct pattern excludes successes rather than enumerating failures — identical logic to excluding EventID 4624 in Windows logon monitoring. A single where ResultType != 0 is more complete and resilient to novel error codes than a maintained list of known failure types.

Graph API delta sync is a high-fidelity recon indicator. Legitimate applications using delta sync have consistent, predictable calling patterns tied to a registered app identity. A one-off delta query from an IP with no prior Graph activity — particularly using the /beta/ endpoint — is a strong indicator of manual directory harvesting. MicrosoftGraphActivityLogs should be ingested into Sentinel and alerted on for delta token usage outside known application identities.

Azure Automation runbooks are a durable persistence mechanism that most IR playbooks miss. Unlike scheduled tasks or registry run keys, runbooks survive endpoint reimaging and execute independently of on-prem infrastructure. Any new runbook creation or schedule linkage in a production Automation account warrants an alert, with particular scrutiny on the create-then-schedule pattern seen here — the two events together constitute the full persistence chain.

Application ownership is a silent privilege escalation path. The Add owner to application audit event is easy to miss without a dedicated detection rule. An application owner can add new credentials to the app and inherit its full API permission scope — equivalent to a role assignment but with far less visibility. Alert on this operation for any application holding sensitive API permissions, and review application ownership as part of regular Azure AD hygiene.


Analyze the Windows Event logs to identify the scope of initial reconnaissance activities. How many distinct computer names experienced login failures?
Click flag to reveal 4
During the initial compromise phase, which machine appears to be the primary target based on the volume of failed authentication attempts?
Click to reveal answer ITWS01
Investigate service installation activities on the compromised machine. What is the name of the suspicious service that was installed?
Click flag to reveal c316a11
Examine the service installation event more closely. Which privileged account was used to install the malicious service on the target machine?
Click to reveal answer infinitetechadmin
Following the initial compromise, the attacker launched a password spray attack against Azure AD. How many total failed authentication attempts were recorded in the Azure sign-in logs after the service installation?
Click flag to reveal 63
During the password spray attack, some accounts became locked due to repeated failed attempts. Which user accounts were affected by account lockouts?
Click to reveal answer Sarah Miles, Alice Jones
Create an incident rule and set the authenticationThreshold to the correct value, then analyze the attack pattern. How many unique IP addresses were involved in the password spray attack?
Click flag to reveal 52
Determine the exact timestamp when the attackers achieved their first successful authentication after initiating the password spray attack.
Click to reveal answer 2025-07-01 19:21
Identify which user account was successfully compromised during the password spray attack.
Click flag to reveal Maya Wilson
Examine the technical details of the successful compromise. What User-Agent string was recorded, providing insights into the attacker's browser and operating system?
Click to reveal answer Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0 OS/10.0.14393
Analyze the non-interactive authentication method used by the attackers. What type of authentication token was leveraged for maintaining access?
Click flag to reveal refreshToken
Calculate the time interval between the initial successful compromise and the first non-interactive authentication event. How many minutes elapsed between these two events?
Click to reveal answer 4
Investigate privilege escalation patterns in the non-interactive authentication logs. Which additional privileged user account shows suspicious non-interactive authentication activity after the initial compromise?
Click flag to reveal Tom Clarkson
Examine Microsoft Graph API activity logs for reconnaissance behaviors. From which IP address were Graph API queries targeting user enumeration initiated?
Click to reveal answer 48.211.64.27
Identify the specific Graph API endpoint that was queried to perform bulk user enumeration with delta synchronization capabilities.
Click flag to reveal /beta/users/microsoft.graph.delta()
Analyze reconnaissance activities from alternative IP addresses. Besides user enumeration, what other component was targeted for reconnaissance from a different IP address?
Click to reveal answer organization
Investigate Azure resource abuse for persistence mechanisms. What is the name of the Azure Automation account that was compromised and repurposed by the attackers?
Click flag to reveal DAILYCHECKER
Examine the malicious automation components created within the compromised account. What is the name of the runbook that was established for persistent access?
Click to reveal answer UsersReminders
Analyze the scheduling mechanism for the malicious automation. What is the name of the schedule that was linked to ensure regular execution of the malicious runbook?
Click flag to reveal DailyUsersReminder
Several hours after the initial compromise, attackers performed a significant privilege escalation action. What specific administrative operation was executed to expand their access rights?
Click to reveal answer Add owner to application
Identify the target of the privilege escalation activity. Which user account was granted elevated permissions through the administrative action?
Click flag to reveal tom.clarkson@infinitechsolutions.xyz
Examine the application context of the privilege escalation. What unique identifier was assigned to the application that became associated with the newly privileged user?
Click to reveal answer 9999-8888
After obtaining elevated privileges, attackers attempted to modify existing persistence mechanisms. What specific Azure resource type was targeted for updates within the automation account?
Click flag to reveal Runbook
Trace the source of the persistence modification activities. From which IP address were the automation account updates initiated?
Click to reveal answer 35.158.160.255
Investigate the data exfiltration phase of the attack. Which Azure Key Vaults were accessed during the secret extraction activities?
Click flag to reveal CORP-KV-PROD, INFRA-BACKUP-KV, FINANCE-KV-EU
🔒
// active lab
writeup locked
withheld in accordance with platform guidelines
to avoid spoiling live challenges.
password provided to recruiters on request.