.github/skills/ca-policy-investigation/SKILL.md
Use this skill when asked to investigate Conditional Access policy changes, sign-in failures related to CA policies (error codes 53000, 50074, 530032), or suspected policy bypass/manipulation. Triggers on keywords like "Conditional Access", "CA policy", "device compliance", "policy bypass", "53000", "50074", or when investigating why a user was blocked then suddenly unblocked. This skill provides forensic analysis of CA policy modifications correlated with sign-in failures.
npx skillsauth add scstelz/security-investigator ca-policy-investigationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill investigates Conditional Access (CA) policy changes in correlation with sign-in failures to detect:
The key distinction is whether policy changes were authorized and necessary vs self-service bypass of security controls.
When investigating sign-in failures (error codes 53000, 50074) with CA policy correlation:
⚠️ MANDATORY STEPS - DO NOT SKIP:
Key Questions to Answer:
| Error Code | Description | Typical Cause | |------------|-------------|---------------| | 53000 | Device not compliant | Device not enrolled in Intune or failing compliance checks | | 50074 | Strong authentication required | MFA not satisfied | | 50074 | User must enroll in MFA | MFA not configured for user | | 530032 | Blocked by CA policy | Generic CA policy block | | 65001 | User consent required | Application consent needed | | 53003 | Access blocked by CA policy | Explicit block condition met | | 70044 | Session expired | User needs to re-authenticate |
| Priority | Error Codes | Investigation Focus | |----------|-------------|---------------------| | HIGH | 53000, 530032, 53003 | Device compliance, CA policy blocks - check for policy manipulation | | MEDIUM | 50074 | MFA requirements - check if MFA was bypassed | | LOW | 65001, 70044 | Consent/session issues - usually not security-related |
| State | What It Means | Security Impact | |-------|---------------|----------------| | enabled | Policy actively enforcing | Blocks non-compliant access (intended behavior) | | disabled | Policy not enforcing | Security control bypassed - all access allowed | | enabledForReportingButNotEnforced | Report-only mode | Logs violations but doesn't block - defeats purpose |
| Transition | Risk Level | Interpretation |
|------------|------------|----------------|
| enabled → disabled | HIGH | Complete security bypass |
| enabled → enabledForReportingButNotEnforced | MEDIUM-HIGH | Partial bypass (monitoring only) |
| disabled → enabled | LOW | Security restored (good) |
| enabledForReportingButNotEnforced → enabled | LOW | Security strengthened (good) |
Query sign-in failures with CA context:
// Get failures with CA context
union isfuzzy=true SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated between (datetime(<START>) .. datetime(<END>))
| where UserPrincipalName =~ '<UPN>'
| where ResultType != '0'
| where AppDisplayName has '<APPLICATION>' // e.g., "Visual Studio Code"
| project TimeGenerated, IPAddress, Location, ResultType, ResultDescription,
ConditionalAccessStatus, UserAgent
| order by TimeGenerated asc
What to Look For:
ResultType values: 53000, 50074, 530032, 53003ConditionalAccessStatus: "failure", "notApplied"CRITICAL: Query ±2 days from the first failure time
let failure_time = datetime(<FIRST_FAILURE_TIME>);
let start = failure_time - 2d;
let end = failure_time + 2d;
AuditLogs
| where TimeGenerated between (start .. end)
| where OperationName has_any ("Conditional Access", "policy")
| where Identity =~ '<UPN>' or tostring(InitiatedBy) has '<UPN>'
| extend InitiatorUPN = tostring(parse_json(InitiatedBy).user.userPrincipalName)
| extend InitiatorIPAddress = tostring(parse_json(InitiatedBy).user.ipAddress)
| extend TargetName = tostring(parse_json(TargetResources)[0].displayName)
| project TimeGenerated, OperationName, Result, InitiatorUPN, InitiatorIPAddress,
TargetName, CorrelationId
| order by TimeGenerated asc // CRITICAL: Chronological order
Critical Analysis Points:
For each CorrelationId from Step 2, get detailed changes:
// Get detailed property changes for a specific policy modification
AuditLogs
| where CorrelationId == "<CORRELATION_ID>"
| extend ModifiedProperties = parse_json(TargetResources)[0].modifiedProperties
| mv-expand ModifiedProperties
| extend PropertyName = tostring(ModifiedProperties.displayName)
| extend OldValue = tostring(ModifiedProperties.oldValue)
| extend NewValue = tostring(ModifiedProperties.newValue)
| project TimeGenerated, PropertyName, OldValue, NewValue
Key Properties to Extract:
"state" property in the JSONOldValue and NewValue for state transitionsenabled → disabled → enabledForReportingButNotEnforcedManual JSON Parsing:
The OldValue and NewValue fields contain JSON. Look for the "state" field:
{
"state": "enabled",
"conditions": { ... },
"grantControls": { ... }
}
Build the Timeline:
"state" from each OldValue and NewValueenabled → disabled → enabledForReportingButNotEnforcedCompare timelines and assess intent:
| Pattern | Interpretation | Risk Level | |---------|----------------|------------| | Failures → Policy Disabled | User bypassed security control to unblock self | HIGH - Privilege abuse | | Failures → Policy Changed to Report-Only | User weakened security control | MEDIUM-HIGH - Partial bypass | | Policy Disabled → Failures Continue | Cached tokens (5-15 min propagation delay) | INFO - Expected behavior | | Policy Changed → No More Failures | Policy change resolved issue | Context-dependent - May be legitimate troubleshooting | | Different user made change | Admin assisted with access issue | LOW - Likely legitimate (verify authorization) |
Risk Escalation Criteria:
| Criteria | Risk Level | |----------|------------| | Same user blocked AND made policy change | HIGH | | Policy disabled within 30 minutes of first failure | HIGH | | Multiple policies modified | HIGH | | Change made outside business hours | MEDIUM-HIGH | | No change request ticket/approval | MEDIUM-HIGH | | Admin made change for blocked user (with ticket) | LOW |
Scenario: User blocked by device compliance policy, then modifies policy
| Time | Event | Details |
|------|-------|---------|
| 19:05 | Sign-in failure | Error 53000: device not compliant |
| 19:06 | Sign-in failure | Error 53000: device not compliant |
| 19:07 | Sign-in failure | Error 53000: device not compliant |
| 19:09 | Policy change | enabled → disabled |
| 19:09 | Policy change | disabled → enabledForReportingButNotEnforced |
| 19:12 | Sign-in failure | Error 53000 (cached token) |
| 19:14 | Sign-in success | Access granted |
✅ Policy was correctly blocking non-compliant device
🚨 User disabled security control to bypass block
⚠️ User partially reversed by enabling report-only
❌ Report-only mode is NOT a valid security posture
| Field | Value | |-------|-------| | Risk Level | MEDIUM-HIGH | | Finding | Self-service security bypass using privileged role | | Root Cause | User's device is non-compliant (not enrolled/failing compliance) | | Policy Impact | Device compliance checks now ineffective for all users |
Immediate Actions:
enabled stateUser-Specific:
Process Improvements:
| Mistake | Why It's Wrong |
|---------|----------------|
| Query only ONE policy change event | You'll miss the sequence of changes |
| Read policy changes in reverse chronological order | Confuses cause/effect relationship |
| Assume policy was already disabled | Must check starting state from OldValue |
| Skip verifying "does this make logical sense?" | Disabled policies can't block users |
| Ignore the initiator identity | Same user = suspicious, different admin = verify authorization |
| Focus only on final state | The transition sequence reveals intent |
| Best Practice | Why It Matters | |---------------|----------------| | Query ALL policy changes in the timeframe | Complete picture of modifications | | Order chronologically (oldest first) | See cause/effect sequence | | Parse the full JSON for state transitions | Extract exact policy states | | Cross-check: blocked user → policy must be enabled | Logical consistency verification | | Ask: "Why would user disable this policy?" | Usually to bypass a legitimate block | | Check if initiator had authorization | Ticket, approval, documented reason |
| Action | Priority |
|--------|----------|
| Restore policy to enabled state if unauthorized | IMMEDIATE |
| Investigate root cause (why was user blocked?) | HIGH |
| Fix underlying issue (device compliance, MFA enrollment) | HIGH |
| Review who has permission to modify CA policies | MEDIUM |
| Implement approval workflows for policy changes | MEDIUM |
| Create alerts for future CA policy modifications | MEDIUM |
| Improvement | Benefit | |-------------|---------| | Use PIM for Security Administrator role | Requires approval for elevated access | | Implement CA policy change alerts | Real-time notification of modifications | | Require multi-admin approval for state changes | Prevents single-person bypass | | Document approved procedures | Clear guidance for legitimate troubleshooting | | Regular access reviews | Ensure only necessary users have CA admin rights |
This skill requires:
mcp_sentinel-data_query_lake: Execute KQL queriesmcp_sentinel-data_search_tables: Discover table schemasTo view CA policy changes in AuditLogs, ensure:
CA Policy Investigation often follows a user-investigation:
Key Integration Points:
development
Use this skill when asked to investigate a computer, device, endpoint, or machine for security issues, suspicious activity, malware, or compliance review. Triggers on keywords like "investigate computer", "investigate device", "investigate endpoint", "check machine", "device security", "endpoint investigation", or when a device name/hostname is mentioned with investigation context. This skill provides comprehensive device security analysis including Defender alerts, sign-in patterns, logged-on users, vulnerabilities, software inventory, compliance status, network activity, and automated investigation tracking for Entra Joined, Hybrid Joined, and Entra Registered devices.
development
Recommended starting point for new users and daily SOC operations. Quick 15-minute security posture scan across 7 domains: active incidents, identity (human + NonHuman), endpoint, email threats, admin & cloud ops, and exposure. 12 queries executed in parallel batches, producing a prioritized Threat Pulse Dashboard with color-coded verdicts (🔴 Escalate / 🟠 Investigate / 🟡 Monitor / ✅ Clear) and drill-down recommendations pointing to specialized skills. Trigger on getting-started questions like "what can you do", "where do I start", "help me investigate". Supports inline chat and markdown file output
development
Use this skill when asked to investigate a user account for security issues, suspicious activity, or compliance review. Triggers on keywords like "investigate user", "security investigation", "user investigation", "check user activity", "analyze sign-ins", or when a UPN/email is mentioned with investigation context. This skill provides comprehensive Entra ID user security analysis including sign-in anomalies, MFA status, device compliance, audit logs, security incidents, Identity Protection risk, and automated reports (HTML, markdown file, or inline chat).
development
Use this skill when asked to generate SVG data visualization dashboards from investigation data or skill reports. Triggers on keywords like "generate SVG dashboard", "create a visual dashboard", "visualize this report", "SVG from the report", "visualize results", "create SVG chart", "SVG from this data". Supports two modes: manifest-driven structured dashboards (from skill reports with svg-widgets.yaml) and freeform adaptive visualizations from ad-hoc investigation data. Component library includes KPI cards, score cards, bar charts, line charts, donut charts, waterfall charts, tables, recommendation cards, assessment banners. SharePoint Dark Theme default palette.