Threat Detection and Incident Response for NHI in Microsoft Entra
In this article
Summary
Back in an earlier article, "Research-agent" gave up its last stored secret. Its SharePoint connector runs on a managed identity, its deployment pipeline runs on a federated credential, and nothing in its container image or configuration would let an attacker impersonate it by reading a file. None of that stops a live token. Every time "Research-agent" actually runs, it holds an access token in memory for as long as that token is valid, and a poisoned dependency slipped into its container image can read that token out of memory and ship it somewhere else without ever touching a secret store. That's the scenario this article: what it looks like to detect that theft, and what to actually do in the hour after someone finds it.
Logging the Estate
Service principal sign-ins live in a separate log from user sign-ins inside Microsoft Entra ID, and a team that only watches the user sign-in log has a blind spot exactly the shape of "Research-agent." Entra ID exposes service principal activity as its own event type, filterable through Microsoft Graph:
Sign-ins are only part of what's worth watching. Audit logs catch credential changes, someone adding a secret to an application that wasn't supposed to need a new one. Role assignment changes catch an identity that gained a permission it didn't have yesterday. Consent grants catch the quieter version of the same problem: an attacker getting a person to approve a malicious app rather than bothering to compromise an existing one.
None of these logs mean much sitting in isolation. The real value comes from correlating Microsoft Entra ID's own logs with Azure Monitor, Defender XDR, and whatever telemetry "Research-agent" own application emits, since its tool-call activity lives in application telemetry, not in Entra ID at all. Following one identity's behavior across all of that, instead of checking four consoles separately, is what actually turns logging into detection.
Detections and Baselines
Once the logs exist, Sentinel is where they turn into alerts. Four patterns come up often enough to build detections.
| Pattern | What it looks like | Why it matters |
|---|---|---|
| Impossible Travel | A service principal signs in from two distant regions minutes apart. | It should run from one datacenter. Anywhere else means the credential is likely being used from two places at once. |
| Credential Spray | Many failed authentications spread across many workload identities. | The machine version of password spraying, aimed at service principals instead of users. |
| Sudden Permission Escalation | An identity acquires a scope it never had before. | Standing permissions don't usually change on their own outside a deployment window. |
| Anomalous Token Issuance | A spike in tokens issued for an identity that's normally quiet. | A live token is what an attacker actually needs, so a burst of new ones is worth asking about. |
Table 1 - Describes the four patterns that Sentinel uses to detect
A rough version of the first pattern in KQL:
Fixed thresholds like these catch the obvious cases. The more durable approach is User and Entity Behavior Analytics, which baselines what's normal for each service principal individually and alerts on deviation from that identity's own pattern rather than a rule written for every identity at once. "Research-agent" calling the same SharePoint site and the same SQL endpoint every night for a year builds exactly the baseline UEBA needs. A call to anywhere else is the deviation worth flagging.
Agents create signals a traditional workload never did, and the most valuable one is tool-call logging. If every tool an agent invokes gets logged, a prompt injection leaves a trail even when nothing else about the identity looks wrong: a customer-support agent suddenly calling an administrative API, an agent attempting to read a credential store, a spike in calls from an agent's own service principal, or a pattern that looks like it's reading far more than the task required. For "Research-agent" specifically, the signal that would have mattered here isn't a sign-in at all. It's whichever endpoint the poisoned dependency tried to reach once it had a live token, coming from a workload that had never called that endpoint before. Microsoft Defender for Cloud Apps can apply policies scoped to AI workloads specifically, which is where this kind of tool-call visibility actually gets enforced rather than just logged.
Incident Response
The scenario worth walking through is the one this article is built around: "Research-agent" service principal credential gets exfiltrated through a supply-chain compromise, a poisoned dependency in its container image quietly shipping a live token out during a routine deployment. Four phases handle it, in order.
Contain
Revoke "Research-agent" federated credential trust and disable its service principal immediately, then rely on Continuous Access Evaluation to invalidate whatever token the poisoned dependency already exfiltrated. Revoking the credential stops "Research-agent" from getting a new token. It does nothing to the token already sitting in an attacker's hands, which under a standard token lifetime keeps working for up to an hour regardless of what was just revoked. CAE closes that gap by evaluating a small set of critical events, such as the identity being disabled, in near real time instead of waiting for the token to expire on its own.
Investigate
Once access is actually cut off, the question becomes what the stolen token could have reached. This is where the ownership record and the criticality tag from the governance work earlier in this series stop being paperwork. Knowing immediately who owns "Research-agent," what tier it's tagged, and which permissions were actually granted to its service principal turns a blast-radius question that could take days of Graph queries into something answered in minutes from a dashboard that already exists.
Eradicate
There's no stored secret to rotate here, which is exactly what the earlier hardening work bought. What still needs eradicating is the poisoned dependency itself: the container image gets rebuilt from a clean base, the compromised package gets pinned out of the dependency tree, and whatever the exfiltrated token could reach gets re-scoped narrower so the same theft is worth less if it happens again.
Recover
Redeploying "Research-agent" with a hardened identity sounds like the end state already reached in an earlier module, and mostly it is. The remaining piece is confirming the rebuild actually inherits the managed identity correctly, rather than someone quietly reintroducing a client secret under deadline pressure to get the redeployment out the door faster.
The Mindset Shift
Humans are noisy. A person's login pattern varies by time zone, mood, and whatever city they're traveling to that week, so a security team watching human accounts has to filter out most of what looks unusual, because most of it is nothing. "Research-agent" doesn't have that problem. It calls the same handful of APIs, from the same few locations, on a schedule that barely shifts week to week, which means almost everything about its behavior is either exactly what it did yesterday or worth asking about.
A service principal that does the same five things every day for a year and then does a sixth thing at three in the morning has just told you something.
The actual work behind that idea is not exciting. It's baselining what's normal for each identity, treating any deviation as suspicious until proven otherwise, building the logging that makes a baseline possible in the first place, and rehearsing the response before a real incident forces the rehearsal to happen live. None of that is a single tool anyone buys. It's closer to a habit a team either has or doesn't.
The tabletop in the section above is what that rehearsal looks like in practice. Walking through "Research-agent" compromise on an ordinary Tuesday afternoon, with no real damage happening, is what turns the actual version of that incident, whenever it comes, into muscle memory instead of a scramble.
Conclusion
"Research-agent" removed its last stored secret in an earlier module, and that still didn't make it invisible to an attacker willing to compromise a dependency instead of hunting for a leaked key. What catches that kind of theft isn't a single control. It's the separate log nobody thought to watch, the baseline that flags a sixth action nobody expected, and the tabletop that already walked through what to do before the real version of this ever happens.