Microsoft Foundry Prompt or Completion Containing Credentials


Description

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.

Query · esql

from logs-azure_ai_foundry.logs-* metadata _id, _version, _index
| eval
    Esql.prompt_text = mv_concat(azure.ai_foundry.properties.backend_request_body.messages.content, " "),
    Esql.reply_text = mv_concat(azure.ai_foundry.properties.backend_response_body.choices.message.content, " ")
| where
    data_stream.dataset == "azure_ai_foundry.logs" and
    azure.ai_foundry.category == "GatewayLogs" and
    (
        Esql.prompt_text rlike """.*(AKIA|ASIA)[A-Z0-9]{16}.*""" or
        Esql.reply_text rlike """.*(AKIA|ASIA)[A-Z0-9]{16}.*""" or
        Esql.prompt_text rlike """.*gh[pousr]_[A-Za-z0-9]{36}.*""" or
        Esql.reply_text rlike """.*gh[pousr]_[A-Za-z0-9]{36}.*""" or
        Esql.prompt_text rlike """.*github_pat_[A-Za-z0-9_]+.*""" or
        Esql.reply_text rlike """.*github_pat_[A-Za-z0-9_]+.*""" or
        Esql.prompt_text rlike """.*-----BEGIN [A-Z ]*PRIVATE KEY-----.*""" or
        Esql.reply_text rlike """.*-----BEGIN [A-Z ]*PRIVATE KEY-----.*""" or
        Esql.prompt_text rlike """.*xox[baprs]-[0-9]+-[0-9]+-[A-Za-z0-9]+.*""" or
        Esql.reply_text rlike """.*xox[baprs]-[0-9]+-[0-9]+-[A-Za-z0-9]+.*""" or
        Esql.prompt_text rlike """.*sk_live_[A-Za-z0-9]+.*""" or
        Esql.reply_text rlike """.*sk_live_[A-Za-z0-9]+.*""" or
        Esql.prompt_text rlike """.*AIza[-A-Za-z0-9_]+.*""" or
        Esql.reply_text rlike """.*AIza[-A-Za-z0-9_]+.*""" or
        Esql.prompt_text rlike """.*npm_[A-Za-z0-9]+.*""" or
        Esql.reply_text rlike """.*npm_[A-Za-z0-9]+.*""" or
        Esql.prompt_text rlike """.*SG[.][-A-Za-z0-9_]+[.][-A-Za-z0-9_]+.*""" or
        Esql.reply_text rlike """.*SG[.][-A-Za-z0-9_]+[.][-A-Za-z0-9_]+.*""" or
        Esql.prompt_text rlike """.*(AccountKey=|SharedAccessKey=)[-A-Za-z0-9+/=]+.*""" or
        Esql.reply_text rlike """.*(AccountKey=|SharedAccessKey=)[-A-Za-z0-9+/=]+.*""" or
        Esql.prompt_text rlike """.*eyJ[-A-Za-z0-9_]+[.]eyJ[-A-Za-z0-9_]+[.][-A-Za-z0-9_]+.*""" or
        Esql.reply_text rlike """.*eyJ[-A-Za-z0-9_]+[.]eyJ[-A-Za-z0-9_]+[.][-A-Za-z0-9_]+.*""" or
        (
            Esql.prompt_text rlike """.*[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+[.][A-Za-z]{2,}.*""" and
            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+.*"""
        ) or
        (
            Esql.reply_text rlike """.*[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+[.][A-Za-z]{2,}.*""" and
            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+.*"""
        )
    )
| keep
    _id,
    _version,
    _index,
    @timestamp,
    source.ip,
    azure.ai_foundry.properties.user_agent,
    azure.ai_foundry.properties.apim_subscription_id,
    azure.ai_foundry.properties.api_id,
    azure.ai_foundry.properties.operation_id,
    azure.ai_foundry.properties.backend_response_body.model,
    azure.ai_foundry.service_name,
    azure.resource.group,
    url.domain,
    url.path,
    source.geo.country_iso_code,
    source.geo.city_name,
    source.as.organization.name,
    azure.ai_foundry.properties.backend_request_body.messages.content,
    azure.ai_foundry.properties.backend_response_body.choices.message.content

Investigation fields

Pivot points the source recommends for triage.

  • source.ip
  • azure.ai_foundry.properties.user_agent
  • azure.ai_foundry.properties.apim_subscription_id
  • azure.ai_foundry.properties.api_id
  • azure.ai_foundry.properties.operation_id
  • azure.ai_foundry.properties.backend_response_body.model
  • azure.ai_foundry.service_name
  • azure.resource.group
  • url.domain
  • url.path
  • source.geo.country_iso_code
  • source.geo.city_name
  • source.as.organization.name
  • azure.ai_foundry.properties.backend_request_body.messages.content
  • azure.ai_foundry.properties.backend_response_body.choices.message.content

Implementation guide

This rule needs the Microsoft Foundry integration collecting Azure API Management GatewayLogs with the backend request body. The prompt is where a pasted secret appears. Log the response body as well so a secret echoed by the assistant is included. Foundry RequestResponse logs do not contain message text.

  • Use an API Management tier that emits resource logs. Developer or higher works. Consumption does not.
  • Send the GatewayLogs category to the Event Hub the integration reads.
  • Log the backend request and response bodies in API diagnostics.

https://www.elastic.co/docs/reference/integrations/azure_ai_foundry

Known false positives

  • Documentation and unit tests that paste example keys, such as an AWS access key ending in EXAMPLE, match the token patterns. Confirm the value is live before rotating it.
  • A prompt that contains both an email address and a sentence such as "the password is required" matches the password assignment pattern. Read the message and drop test subscriptions that intentionally send fake secrets.

Analyst notes

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.
Raw source Microsoft Foundry Prompt or Completion Containing Credentials · Elastic TOML
Esc
Published by elastic/detection-rules ↗, licensed under Elastic License 2.0 ↗. Reproduced here unmodified.
[metadata]
creation_date = "2026/10/01"
integration = ["azure_ai_foundry"]
maturity = "production"
min_stack_version = "9.3.0"
min_stack_comments = "The Microsoft Foundry integration is compatible with 9.3 and above."
updated_date = "2026/10/05"

[rule]
author = ["Elastic"]
description = """
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.
"""
false_positives = [
    """
    Documentation and unit tests that paste example keys, such as an AWS access key ending in EXAMPLE, match the token
    patterns. Confirm the value is live before rotating it.
    """,
    """
    A prompt that contains both an email address and a sentence such as "the password is required" matches the
    password assignment pattern. Read the message and drop test subscriptions that intentionally send fake secrets.
    """,
]
from = "now-9m"
language = "esql"
license = "Elastic License v2"
name = "Microsoft Foundry Prompt or Completion Containing Credentials"
note = """## 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 = [
    "https://www.elastic.co/docs/reference/integrations/azure_ai_foundry",
    "https://learn.microsoft.com/en-us/azure/api-management/diagnostic-logs-reference",
    "https://genai.owasp.org/llmrisk/llm06-sensitive-information-disclosure",
]
risk_score = 73
rule_id = "cbaa94d2-1b4d-4198-9882-fafc9e813a5a"
setup = """## Setup

This rule needs the Microsoft Foundry integration collecting Azure API Management GatewayLogs with the backend
request body. The prompt is where a pasted secret appears. Log the response body as well so a secret echoed by the
assistant is included. Foundry RequestResponse logs do not contain message text.

- Use an API Management tier that emits resource logs. Developer or higher works. Consumption does not.
- Send the GatewayLogs category to the Event Hub the integration reads.
- Log the backend request and response bodies in API diagnostics.

https://www.elastic.co/docs/reference/integrations/azure_ai_foundry
"""
severity = "high"
tags = [
    "Data Source: Microsoft Foundry",
    "Use Case: Threat Detection",
    "Mitre Atlas: AML.T0055",
    "Mitre Atlas: AML.T0057",
    "Resources: Investigation Guide",
    "Tactic: Credential Access",
    "Rule Type: ES|QL",
    "Platform: Azure",
    "Domain: Cloud",
    "Domain: GenAI",
    "Service: Azure API Management"
]
timestamp_override = "event.ingested"
type = "esql"

query = '''
from logs-azure_ai_foundry.logs-* metadata _id, _version, _index
| eval
    Esql.prompt_text = mv_concat(azure.ai_foundry.properties.backend_request_body.messages.content, " "),
    Esql.reply_text = mv_concat(azure.ai_foundry.properties.backend_response_body.choices.message.content, " ")
| where
    data_stream.dataset == "azure_ai_foundry.logs" and
    azure.ai_foundry.category == "GatewayLogs" and
    (
        Esql.prompt_text rlike """.*(AKIA|ASIA)[A-Z0-9]{16}.*""" or
        Esql.reply_text rlike """.*(AKIA|ASIA)[A-Z0-9]{16}.*""" or
        Esql.prompt_text rlike """.*gh[pousr]_[A-Za-z0-9]{36}.*""" or
        Esql.reply_text rlike """.*gh[pousr]_[A-Za-z0-9]{36}.*""" or
        Esql.prompt_text rlike """.*github_pat_[A-Za-z0-9_]+.*""" or
        Esql.reply_text rlike """.*github_pat_[A-Za-z0-9_]+.*""" or
        Esql.prompt_text rlike """.*-----BEGIN [A-Z ]*PRIVATE KEY-----.*""" or
        Esql.reply_text rlike """.*-----BEGIN [A-Z ]*PRIVATE KEY-----.*""" or
        Esql.prompt_text rlike """.*xox[baprs]-[0-9]+-[0-9]+-[A-Za-z0-9]+.*""" or
        Esql.reply_text rlike """.*xox[baprs]-[0-9]+-[0-9]+-[A-Za-z0-9]+.*""" or
        Esql.prompt_text rlike """.*sk_live_[A-Za-z0-9]+.*""" or
        Esql.reply_text rlike """.*sk_live_[A-Za-z0-9]+.*""" or
        Esql.prompt_text rlike """.*AIza[-A-Za-z0-9_]+.*""" or
        Esql.reply_text rlike """.*AIza[-A-Za-z0-9_]+.*""" or
        Esql.prompt_text rlike """.*npm_[A-Za-z0-9]+.*""" or
        Esql.reply_text rlike """.*npm_[A-Za-z0-9]+.*""" or
        Esql.prompt_text rlike """.*SG[.][-A-Za-z0-9_]+[.][-A-Za-z0-9_]+.*""" or
        Esql.reply_text rlike """.*SG[.][-A-Za-z0-9_]+[.][-A-Za-z0-9_]+.*""" or
        Esql.prompt_text rlike """.*(AccountKey=|SharedAccessKey=)[-A-Za-z0-9+/=]+.*""" or
        Esql.reply_text rlike """.*(AccountKey=|SharedAccessKey=)[-A-Za-z0-9+/=]+.*""" or
        Esql.prompt_text rlike """.*eyJ[-A-Za-z0-9_]+[.]eyJ[-A-Za-z0-9_]+[.][-A-Za-z0-9_]+.*""" or
        Esql.reply_text rlike """.*eyJ[-A-Za-z0-9_]+[.]eyJ[-A-Za-z0-9_]+[.][-A-Za-z0-9_]+.*""" or
        (
            Esql.prompt_text rlike """.*[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+[.][A-Za-z]{2,}.*""" and
            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+.*"""
        ) or
        (
            Esql.reply_text rlike """.*[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+[.][A-Za-z]{2,}.*""" and
            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+.*"""
        )
    )
| keep
    _id,
    _version,
    _index,
    @timestamp,
    source.ip,
    azure.ai_foundry.properties.user_agent,
    azure.ai_foundry.properties.apim_subscription_id,
    azure.ai_foundry.properties.api_id,
    azure.ai_foundry.properties.operation_id,
    azure.ai_foundry.properties.backend_response_body.model,
    azure.ai_foundry.service_name,
    azure.resource.group,
    url.domain,
    url.path,
    source.geo.country_iso_code,
    source.geo.city_name,
    source.as.organization.name,
    azure.ai_foundry.properties.backend_request_body.messages.content,
    azure.ai_foundry.properties.backend_response_body.choices.message.content
'''


[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1552"
name = "Unsecured Credentials"
reference = "https://attack.mitre.org/techniques/T1552/"


[rule.threat.tactic]
id = "TA0006"
name = "Credential Access"
reference = "https://attack.mitre.org/tactics/TA0006/"

[[rule.threat_mappings]]
framework = "MITRE ATLAS"
version = "2026.08"
[[rule.threat_mappings.threat]]
framework = "MITRE ATLAS"
[[rule.threat_mappings.threat.technique]]
id = "AML.T0057"
name = "LLM Data Leakage"
reference = "https://atlas.mitre.org/techniques/AML.T0057/"


[rule.threat_mappings.threat.tactic]
id = "AML.TA0010"
name = "Exfiltration"
reference = "https://atlas.mitre.org/tactics/AML.TA0010/"
[[rule.threat_mappings.threat]]
framework = "MITRE ATLAS"
[[rule.threat_mappings.threat.technique]]
id = "AML.T0055"
name = "Unsecured Credentials"
reference = "https://atlas.mitre.org/techniques/AML.T0055/"


[rule.threat_mappings.threat.tactic]
id = "AML.TA0013"
name = "Credential Access"
reference = "https://atlas.mitre.org/tactics/AML.TA0013/"

[rule.investigation_fields]
field_names = [
    "source.ip",
    "azure.ai_foundry.properties.user_agent",
    "azure.ai_foundry.properties.apim_subscription_id",
    "azure.ai_foundry.properties.api_id",
    "azure.ai_foundry.properties.operation_id",
    "azure.ai_foundry.properties.backend_response_body.model",
    "azure.ai_foundry.service_name",
    "azure.resource.group",
    "url.domain",
    "url.path",
    "source.geo.country_iso_code",
    "source.geo.city_name",
    "source.as.organization.name",
    "azure.ai_foundry.properties.backend_request_body.messages.content",
    "azure.ai_foundry.properties.backend_response_body.choices.message.content",
]

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.