Microsoft Foundry Prompt or Completion Containing Credentials
Detects a Microsoft Foundry chat, sent through API Management, whose prompt or assistant reply contains a known credential pattern or an email address together with a password assignment. The model often refuses the request and Azure content filters stay clear, so the secret is only visible in the logged message text.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2026/10/01"
3integration = ["azure_ai_foundry"]
4maturity = "production"
5min_stack_version = "9.3.0"
6min_stack_comments = "The Microsoft Foundry integration is compatible with 9.3 and above."
7updated_date = "2026/10/05"
8
9[rule]
10author = ["Elastic"]
11description = """
12Detects a Microsoft Foundry chat, sent through API Management, whose prompt or assistant reply contains a known
13credential pattern or an email address together with a password assignment. The model often refuses the request and
14Azure content filters stay clear, so the secret is only visible in the logged message text.
15"""
16false_positives = [
17 """
18 Documentation and unit tests that paste example keys, such as an AWS access key ending in EXAMPLE, match the token
19 patterns. Confirm the value is live before rotating it.
20 """,
21 """
22 A prompt that contains both an email address and a sentence such as "the password is required" matches the
23 password assignment pattern. Read the message and drop test subscriptions that intentionally send fake secrets.
24 """,
25]
26from = "now-9m"
27language = "esql"
28license = "Elastic License v2"
29name = "Microsoft Foundry Prompt or Completion Containing Credentials"
30note = """## Triage and analysis
31
32### Investigating Microsoft Foundry Prompt or Completion Containing Credentials
33
34A GatewayLogs chat matched a credential regex in the user prompt or the assistant reply. Azure does not set a
35credential flag for this. Content-filter categories can all be safe, jailbreak.detected can be false, and
36message.refusal can be null while the secret still sits in the prompt. A refusal sentence only means the model
37declined to use the secret.
38
39#### Possible investigation steps
40
41- Read the prompt and the assistant reply on the alert. Decide whether the match is a live secret, an example key, or
42 a sentence that only looks like a password assignment.
43- Identify the caller from source.ip, user agent, and apim_subscription_id, and the target from the model, API, and
44 URL path.
45- If the assistant reply repeats the secret, treat that as exposure in the completion as well as the prompt.
46- Check whether the same subscription or IP has other GatewayLogs in the same window, including repeated refusals.
47
48### False positive analysis
49
50- Example and documentation keys match the prefixed patterns. The password clause also matches when an email and the
51 words "password is ..." appear in the same message for a non-secret reason.
52- Approved prompt-security tests. Exclude that source IP or API Management subscription.
53
54### Response and remediation
55
56- If the value is live, rotate or revoke it and review use of that credential after the prompt time.
57- Find the application path that placed the secret in the chat request.
58- Keep body logging restricted to the teams allowed to read these alerts, because the alert retains the message text.
59"""
60references = [
61 "https://www.elastic.co/docs/reference/integrations/azure_ai_foundry",
62 "https://learn.microsoft.com/en-us/azure/api-management/diagnostic-logs-reference",
63 "https://genai.owasp.org/llmrisk/llm06-sensitive-information-disclosure",
64]
65risk_score = 73
66rule_id = "cbaa94d2-1b4d-4198-9882-fafc9e813a5a"
67setup = """## Setup
68
69This rule needs the Microsoft Foundry integration collecting Azure API Management GatewayLogs with the backend
70request body. The prompt is where a pasted secret appears. Log the response body as well so a secret echoed by the
71assistant is included. Foundry RequestResponse logs do not contain message text.
72
73- Use an API Management tier that emits resource logs. Developer or higher works. Consumption does not.
74- Send the GatewayLogs category to the Event Hub the integration reads.
75- Log the backend request and response bodies in API diagnostics.
76
77https://www.elastic.co/docs/reference/integrations/azure_ai_foundry
78"""
79severity = "high"
80tags = [
81 "Data Source: Microsoft Foundry",
82 "Use Case: Threat Detection",
83 "Mitre Atlas: AML.T0055",
84 "Mitre Atlas: AML.T0057",
85 "Resources: Investigation Guide",
86 "Tactic: Credential Access",
87 "Rule Type: ES|QL",
88 "Platform: Azure",
89 "Domain: Cloud",
90 "Domain: GenAI",
91 "Service: Azure API Management"
92]
93timestamp_override = "event.ingested"
94type = "esql"
95
96query = '''
97from logs-azure_ai_foundry.logs-* metadata _id, _version, _index
98| eval
99 Esql.prompt_text = mv_concat(azure.ai_foundry.properties.backend_request_body.messages.content, " "),
100 Esql.reply_text = mv_concat(azure.ai_foundry.properties.backend_response_body.choices.message.content, " ")
101| where
102 data_stream.dataset == "azure_ai_foundry.logs" and
103 azure.ai_foundry.category == "GatewayLogs" and
104 (
105 Esql.prompt_text rlike """.*(AKIA|ASIA)[A-Z0-9]{16}.*""" or
106 Esql.reply_text rlike """.*(AKIA|ASIA)[A-Z0-9]{16}.*""" or
107 Esql.prompt_text rlike """.*gh[pousr]_[A-Za-z0-9]{36}.*""" or
108 Esql.reply_text rlike """.*gh[pousr]_[A-Za-z0-9]{36}.*""" or
109 Esql.prompt_text rlike """.*github_pat_[A-Za-z0-9_]+.*""" or
110 Esql.reply_text rlike """.*github_pat_[A-Za-z0-9_]+.*""" or
111 Esql.prompt_text rlike """.*-----BEGIN [A-Z ]*PRIVATE KEY-----.*""" or
112 Esql.reply_text rlike """.*-----BEGIN [A-Z ]*PRIVATE KEY-----.*""" or
113 Esql.prompt_text rlike """.*xox[baprs]-[0-9]+-[0-9]+-[A-Za-z0-9]+.*""" or
114 Esql.reply_text rlike """.*xox[baprs]-[0-9]+-[0-9]+-[A-Za-z0-9]+.*""" or
115 Esql.prompt_text rlike """.*sk_live_[A-Za-z0-9]+.*""" or
116 Esql.reply_text rlike """.*sk_live_[A-Za-z0-9]+.*""" or
117 Esql.prompt_text rlike """.*AIza[-A-Za-z0-9_]+.*""" or
118 Esql.reply_text rlike """.*AIza[-A-Za-z0-9_]+.*""" or
119 Esql.prompt_text rlike """.*npm_[A-Za-z0-9]+.*""" or
120 Esql.reply_text rlike """.*npm_[A-Za-z0-9]+.*""" or
121 Esql.prompt_text rlike """.*SG[.][-A-Za-z0-9_]+[.][-A-Za-z0-9_]+.*""" or
122 Esql.reply_text rlike """.*SG[.][-A-Za-z0-9_]+[.][-A-Za-z0-9_]+.*""" or
123 Esql.prompt_text rlike """.*(AccountKey=|SharedAccessKey=)[-A-Za-z0-9+/=]+.*""" or
124 Esql.reply_text rlike """.*(AccountKey=|SharedAccessKey=)[-A-Za-z0-9+/=]+.*""" or
125 Esql.prompt_text rlike """.*eyJ[-A-Za-z0-9_]+[.]eyJ[-A-Za-z0-9_]+[.][-A-Za-z0-9_]+.*""" or
126 Esql.reply_text rlike """.*eyJ[-A-Za-z0-9_]+[.]eyJ[-A-Za-z0-9_]+[.][-A-Za-z0-9_]+.*""" or
127 (
128 Esql.prompt_text rlike """.*[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+[.][A-Za-z]{2,}.*""" and
129 Esql.prompt_text rlike """.*([Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd]|[Pp][Aa][Ss][Ss][Ww][Dd]|[Pp][Ww][Dd])\s*(is|=|:)\s*\S+.*"""
130 ) or
131 (
132 Esql.reply_text rlike """.*[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+[.][A-Za-z]{2,}.*""" and
133 Esql.reply_text rlike """.*([Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd]|[Pp][Aa][Ss][Ss][Ww][Dd]|[Pp][Ww][Dd])\s*(is|=|:)\s*\S+.*"""
134 )
135 )
136| keep
137 _id,
138 _version,
139 _index,
140 @timestamp,
141 source.ip,
142 azure.ai_foundry.properties.user_agent,
143 azure.ai_foundry.properties.apim_subscription_id,
144 azure.ai_foundry.properties.api_id,
145 azure.ai_foundry.properties.operation_id,
146 azure.ai_foundry.properties.backend_response_body.model,
147 azure.ai_foundry.service_name,
148 azure.resource.group,
149 url.domain,
150 url.path,
151 source.geo.country_iso_code,
152 source.geo.city_name,
153 source.as.organization.name,
154 azure.ai_foundry.properties.backend_request_body.messages.content,
155 azure.ai_foundry.properties.backend_response_body.choices.message.content
156'''
157
158
159[[rule.threat]]
160framework = "MITRE ATT&CK"
161[[rule.threat.technique]]
162id = "T1552"
163name = "Unsecured Credentials"
164reference = "https://attack.mitre.org/techniques/T1552/"
165
166
167[rule.threat.tactic]
168id = "TA0006"
169name = "Credential Access"
170reference = "https://attack.mitre.org/tactics/TA0006/"
171
172[[rule.threat_mappings]]
173framework = "MITRE ATLAS"
174version = "2026.08"
175[[rule.threat_mappings.threat]]
176framework = "MITRE ATLAS"
177[[rule.threat_mappings.threat.technique]]
178id = "AML.T0057"
179name = "LLM Data Leakage"
180reference = "https://atlas.mitre.org/techniques/AML.T0057/"
181
182
183[rule.threat_mappings.threat.tactic]
184id = "AML.TA0010"
185name = "Exfiltration"
186reference = "https://atlas.mitre.org/tactics/AML.TA0010/"
187[[rule.threat_mappings.threat]]
188framework = "MITRE ATLAS"
189[[rule.threat_mappings.threat.technique]]
190id = "AML.T0055"
191name = "Unsecured Credentials"
192reference = "https://atlas.mitre.org/techniques/AML.T0055/"
193
194
195[rule.threat_mappings.threat.tactic]
196id = "AML.TA0013"
197name = "Credential Access"
198reference = "https://atlas.mitre.org/tactics/AML.TA0013/"
199
200[rule.investigation_fields]
201field_names = [
202 "source.ip",
203 "azure.ai_foundry.properties.user_agent",
204 "azure.ai_foundry.properties.apim_subscription_id",
205 "azure.ai_foundry.properties.api_id",
206 "azure.ai_foundry.properties.operation_id",
207 "azure.ai_foundry.properties.backend_response_body.model",
208 "azure.ai_foundry.service_name",
209 "azure.resource.group",
210 "url.domain",
211 "url.path",
212 "source.geo.country_iso_code",
213 "source.geo.city_name",
214 "source.as.organization.name",
215 "azure.ai_foundry.properties.backend_request_body.messages.content",
216 "azure.ai_foundry.properties.backend_response_body.choices.message.content",
217]
Triage and analysis
Investigating Microsoft Foundry Prompt or Completion Containing Credentials
A GatewayLogs chat matched a credential regex in the user prompt or the assistant reply. Azure does not set a credential flag for this. Content-filter categories can all be safe, jailbreak.detected can be false, and message.refusal can be null while the secret still sits in the prompt. A refusal sentence only means the model declined to use the secret.
Possible investigation steps
- Read the prompt and the assistant reply on the alert. Decide whether the match is a live secret, an example key, or a sentence that only looks like a password assignment.
- Identify the caller from source.ip, user agent, and apim_subscription_id, and the target from the model, API, and URL path.
- If the assistant reply repeats the secret, treat that as exposure in the completion as well as the prompt.
- Check whether the same subscription or IP has other GatewayLogs in the same window, including repeated refusals.
False positive analysis
- Example and documentation keys match the prefixed patterns. The password clause also matches when an email and the words "password is ..." appear in the same message for a non-secret reason.
- Approved prompt-security tests. Exclude that source IP or API Management subscription.
Response and remediation
- If the value is live, rotate or revoke it and review use of that credential after the prompt time.
- Find the application path that placed the secret in the chat request.
- Keep body logging restricted to the teams allowed to read these alerts, because the alert retains the message text.
References
Related rules
- Microsoft Foundry Subscription Key Impossible Travel
- Microsoft Foundry Jailbreak Detected
- AWS Bedrock AgentCore Runtime Prompt Containing Credentials
- AWS Bedrock Model Prompt or Completion Containing Credentials
- Microsoft Foundry High Request and Token Volume