Adversaries may embed hard-coded credentials in A365 AI agents to maintain persistent access and exfiltrate data undetected. Proactively hunting for this behavior in Azure Sentinel helps identify potential credential theft and unauthorized access vectors before they lead to data breaches.
KQL Query
let suspicious_patterns = @"(AKIA[0-9A-Z]{16})|(AIza[0-9A-Za-z_\-]{35})|(xox[baprs]-[0-9a-zA-Z]{10,48})|(ghp_[A-Za-z0-9]{36,59})|(sk_(live|test)_[A-Za-z0-9]{24})|(SG\.[A-Za-z0-9]{22}\.[A-Za-z0-9]{43})|(\d{8}:[\w\-]{35})|(eyJ[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+)|(Authorization\s*:\s*Basic\s+[A-Za-z0-9=:+]+)|([A-Za-z]+:\/\/[^\/\s]+:[^\/\s]+@[^\/\s]+)";
let IdentityIdtoUPN = materialize (
IdentityInfo
| distinct AccountObjectId, AccountUpn
| extend AccountObjectId = tostring(AccountObjectId)
| where isnotempty(AccountObjectId) and isnotempty(AccountUpn));
AIAgentsInfo
| summarize arg_max(Timestamp, *) by AIAgentId
| where RegistrySource == "A365"
| extend RawAgentInfoJson = parse_json(RawAgentInfo)
| extend DeclarativeCopilotMetadata = RawAgentInfoJson.declarativeCopilotMetadata[0]
| extend DeveloperName = RawAgentInfoJson.developerName
| extend OwnerId = tostring(RawAgentInfoJson.owners[0].entityId)
| extend CreatorId = tostring(RawAgentInfoJson.creatorId)
| join kind=leftouter IdentityIdtoUPN on $left.OwnerId == $right.AccountObjectId
| project-rename OwnerUpn = AccountUpn
| join kind=leftouter IdentityIdtoUPN on $left.CreatorId == $right.AccountObjectId
| project-rename CreatorUpn = AccountUpn
| where
AgentActionTriggers matches regex suspicious_patterns
or AgentToolsDetails matches regex suspicious_patterns
or DeclarativeCopilotMetadata matches regex suspicious_patterns
| project-away RawAgentInfoJson, OwnerId, CreatorId, AccountObjectId, AccountObjectId1
| project-reorder AgentCreationTime, AIAgentId, AIAgentName, AgentActionTriggers, AgentToolsDetails, DeclarativeCopilotMetadata, OwnerUpn, CreatorUpn, DeveloperName
id: 1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d
name: A365 AI Agents - Hard-coded credentials in Tools or Actions
description: |
This query identifies A365 AI agents that contain hard-coded credentials in their tools or actions.
Storing credentials in clear text within agent logic creates a security risk because these secrets can be exposed to unintended users or attackers.
If compromised, credentials could allow unauthorized access to external systems, APIs, or sensitive data.
Recommended Action: Avoid embedding credentials directly in Tools or Actions. Use secure alternatives
such as Azure Key Vault with environment variables or enable secured input options for sensitive fields.
Regularly audit agents for hard-coded secrets and rotate any exposed credentials immediately.
requiredDataConnectors: []
tactics:
- CredentialAccess
- InitialAccess
relevantTechniques:
- T1552
- T1078
query: |
let suspicious_patterns = @"(AKIA[0-9A-Z]{16})|(AIza[0-9A-Za-z_\-]{35})|(xox[baprs]-[0-9a-zA-Z]{10,48})|(ghp_[A-Za-z0-9]{36,59})|(sk_(live|test)_[A-Za-z0-9]{24})|(SG\.[A-Za-z0-9]{22}\.[A-Za-z0-9]{43})|(\d{8}:[\w\-]{35})|(eyJ[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+)|(Authorization\s*:\s*Basic\s+[A-Za-z0-9=:+]+)|([A-Za-z]+:\/\/[^\/\s]+:[^\/\s]+@[^\/\s]+)";
let IdentityIdtoUPN = materialize (
IdentityInfo
| distinct AccountObjectId, AccountUpn
| extend AccountObjectId = tostring(AccountObjectId)
| where isnotempty(AccountObjectId) and isnotempty(AccountUpn));
AIAgentsInfo
| summarize arg_max(Timestamp, *) by AIAgentId
| where RegistrySource == "A365"
| extend RawAgentInfoJson = parse_json(RawAgentInfo)
| extend DeclarativeCopilotMetadata = RawAgentInfoJson.declarativeCopilotMetadata[0]
| extend DeveloperName = RawAgentInfoJson.developerName
| extend OwnerId = tostring(RawAgentInfoJson.owners[0].entityId)
| extend CreatorId = tostring(RawAgentInfoJson.creatorId)
| join kind=leftouter IdentityIdtoUPN on $left.OwnerId == $right.AccountObjectId
| project-rename OwnerUpn = AccountUpn
| join kind=leftouter IdentityIdtoUPN on $left.CreatorId == $right.AccountObjectId
| project-rename CreatorUpn = AccountUpn
| where
AgentActionTriggers matches regex suspicious_patterns
or AgentToolsDetails matches regex suspicious_patterns
or DeclarativeCopilotMetadata matches regex suspicious_patterns
| project-away RawAgentInfoJson, OwnerId, CreatorId, AccountObjectId, AccountObjectId1
| project-reorder AgentCreationTime, AIAgentId, AIAgentName, AgentActionTriggers, AgentToolsDetails, DeclarativeCopilotMetadata, OwnerUpn, CreatorUpn, DeveloperName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: CreatorUpn
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: AIAgentName
version: 1.0.0
| Sentinel Table | Notes |
|---|---|
IdentityInfo | Ensure this data connector is enabled |
Scenario: A scheduled job uses a service account with a password stored in plain text for accessing a third-party API.
Filter/Exclusion: Exclude entries where the tool is a known third-party API integration (e.g., Microsoft Teams API, Outlook REST API) and the credential is part of a service principal configuration.
Scenario: An admin task script includes a hardcoded password for a database connection used by a reporting tool like Power BI.
Filter/Exclusion: Exclude entries where the tool is Power BI and the credential is part of a connection string used for report scheduling or data refresh.
Scenario: A custom AI agent uses a hardcoded admin username and password to authenticate with Azure DevOps for CI/CD pipeline integration.
Filter/Exclusion: Exclude entries where the tool is Azure DevOps and the credential is part of a service principal or managed identity configuration.
Scenario: A user-created AI agent for automating email responses includes a hardcoded SMTP password in the action configuration.
Filter/Exclusion: Exclude entries where the tool is Exchange Online and the credential is used for a user account with delegated permissions for email automation.
Scenario: A system maintenance script uses a hardcoded admin password to connect to a SQL Server instance for database backups.
Filter/Exclusion: Exclude entries where the tool is SQL Server and the credential is part of a scheduled backup job configured via SQL Agent or Azure SQL Managed Instance.