15.4 Assessing What the Application Accessed

3-5 hours · Module 15

Assessing What the Application Accessed

If the application’s service principal sign-in logs show activity, determine what data was exfiltrated.


Step 1: Identify accessed resources

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Resources accessed by the malicious application
AADServicePrincipalSignInLogs
| where TimeGenerated > ago(7d)
| where AppId == "MALICIOUS-APP-ID"
| where ResultType == "0"
| summarize
    AccessCount = count(),
    FirstAccess = min(TimeGenerated),
    LastAccess = max(TimeGenerated),
    IPs = make_set(IPAddress, 5)
    by ResourceDisplayName
| order by AccessCount desc

What to examine: Which M365 services did the application access? Exchange Online = email data. SharePoint Online / OneDrive = files. Microsoft Graph = directory data, email, calendar, contacts (Graph is the gateway to everything).


Step 2: Determine per-user data exposure

The application has delegated permissions — it acts on behalf of each consenting user. Determine what each user’s data exposure is:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Per-user: email access by the malicious application
// Requires CloudAppEvents or unified audit log correlation
CloudAppEvents
| where TimeGenerated > ago(7d)
| where Application == "Microsoft Exchange Online"
| where ActionType == "MailItemsAccessed"
| extend ClientAppId = tostring(parse_json(RawEventData).ClientAppId)
| where ClientAppId == "MALICIOUS-APP-ID"
| summarize
    EmailsAccessed = count(),
    FirstAccess = min(TimeGenerated),
    LastAccess = max(TimeGenerated)
    by AccountDisplayName
| order by EmailsAccessed desc

If CloudAppEvents does not contain the ClientAppId field: Use the unified audit log via Purview Compliance portal → Audit → search for activities by the application’s AppId. This is a known limitation — not all application API calls populate CloudAppEvents with the calling application’s identity.


Step 2b: File access analysis

If the application was granted Files.ReadWrite.All, it can access every file the consenting user can access — including SharePoint sites, OneDrive, and Teams files.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// File access by the malicious application
CloudAppEvents
| where TimeGenerated > ago(7d)
| where Application in ("Microsoft SharePoint Online", "Microsoft OneDrive for Business")
| where ActionType in ("FileDownloaded", "FileAccessed", "FilePreviewed")
| extend ClientAppId = tostring(parse_json(RawEventData).ClientAppId)
| where ClientAppId == "MALICIOUS-APP-ID"
| extend FileName = tostring(parse_json(RawEventData).SourceFileName)
| extend SiteUrl = tostring(parse_json(RawEventData).SiteUrl)
| project TimeGenerated, AccountDisplayName, FileName, SiteUrl, ActionType
| order by TimeGenerated desc

If the application accessed SharePoint sites containing HR files, financial records, or client contracts: the data exposure scope expands significantly beyond email.


Step 2c: Directory data access

If the application was granted Directory.ReadWrite.All or User.ReadWrite.All:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Graph API calls by the malicious application
AADServicePrincipalSignInLogs
| where TimeGenerated > ago(7d)
| where AppId == "MALICIOUS-APP-ID"
| where ResourceDisplayName == "Microsoft Graph"
| summarize
    CallCount = count(),
    FirstCall = min(TimeGenerated),
    LastCall = max(TimeGenerated)
    by IPAddress

Graph API access with directory permissions means the application could read: the complete user directory (names, emails, phone numbers, job titles), group memberships (including security groups), and organisational structure. This is a significant PII exposure even if no email was accessed.


Data categorisation for breach notification

Compile the exposure into a categorised table:

UserEmailsFilesDirectoryData CategoriesPII?
j.morrison3400Business comms, contractsAssess
s.chen12045Financial data, employee recordsYes
a.patel850Payment details, bank infoYes
All 12 usersFull directoryNames, emails, phone numbersYes

The directory exposure applies to all consenting users if the permission was granted — regardless of how many emails or files were individually accessed.


Step 3: Assess data breach notification requirements

For each consenting user, determine: what data categories were accessible via the granted permissions (email = business communications, possibly PII; files = potentially sensitive documents; contacts = personal data), how many items were accessed, and the time window of access.

If the application accessed email containing PII, health data, or financial data: Data breach notification may be required under GDPR (72 hours to ICO) or other applicable regulations. Consult legal counsel.

Compliance mapping: NIST CSF RS.AN-2 (Impact understood). ISO 27001 A.5.25 (Assessment of security events).

Subsection artifact: The data exposure assessment queries and the breach notification decision framework. These feed the IR report.


Knowledge check


Determining what the application can access vs what it did access

The permissions granted tell you what the application CAN access. The service principal sign-in logs tell you what it DID access. The gap matters for the IR report: “The application was granted Mail.ReadWrite permission for 12 users. It actually accessed email for 8 of the 12. The remaining 4 users’ data was at risk but may not have been accessed.”

Compliance implication: For GDPR breach notification, the question is whether personal data was accessed — not whether it could have been accessed. If the application was granted Mail.ReadWrite for 12 users but only accessed 8: the data breach scope is 8 users (confirmed access), with 4 additional users at risk (permission granted, access not confirmed). Consult legal counsel on whether the “at risk” users require notification.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Per-user access determination: granted vs actual
let ConsentedUsers = AuditLogs
| where OperationName == "Consent to application"
| where tostring(TargetResources[0].displayName) == "Microsoft Security Update Tool"
| extend ConsentUser = tostring(InitiatedBy.user.userPrincipalName)
| distinct ConsentUser;
let AccessedUsers = CloudAppEvents
| where ActionType == "MailItemsAccessed"
| extend ClientAppId = tostring(parse_json(RawEventData).ClientAppId)
| where ClientAppId == "MALICIOUS-APP-ID"
| distinct AccountDisplayName;
ConsentedUsers
| extend AccessStatus = iff(ConsentUser in (AccessedUsers), "ACCESSED", "AT RISK — not confirmed")
| project ConsentUser, AccessStatus

This query produces the definitive scope table for the IR report: which users’ data was confirmed accessed vs which were only at risk. The distinction matters for regulatory notification decisions and for the accuracy of the incident impact assessment.

Check your understanding

1. The application accessed Exchange Online for 8 of the 12 consenting users. The remaining 4 show zero access. Why might 4 users show no access?

Several possibilities: the attacker has not yet accessed those 4 users' data (they may be processing users in sequence), the 4 users' consent may not have included Mail.ReadWrite (different permission sets), or the logging may be delayed. Do not assume the 4 are safe — revoke all 12 consents and assess after revocation. It is safer to revoke a consent that was not exploited than to leave one that was.
Those 4 users' data is safe
The consent failed for those 4 users
Only 8 users need investigation