Detection rules › Splunk

AWS Bedrock Claude High Risk Filesystem and Exec Tool Invocation

Status
production
Severity
medium
Group by
modelId, region, user_session
Author
Rod Soto
Source
github.com/splunk/security_content

Detects identities invoking high-risk filesystem and execution tools via AWS Bedrock Claude. For each identity, monitors the usage of potentially dangerous commands and flags any anomalous activity that deviates from their historical baseline. This may indicate attempts to escalate privileges, exfiltrate data, or execute unauthorized commands.

Known false positives

  • This detection may produce false positives for legitimate developer activity where authorized users invoke shell execution, file write, or network tools through Claude Code as part of normal software development, deployment automation, or infrastructure management workflows.

MITRE ATT&CK coverage

TacticTechniques
Privilege Escalation
Stealth

Rule body

name: AWS Bedrock Claude High Risk Filesystem and Exec Tool Invocation
id: 989b3702-1df3-4076-9e2f-8dcbcc2b4bef
version: 1
creation_date: '2026-07-06'
modification_date: '2026-07-06'
author: Rod Soto
status: production
type: Anomaly
description: Detects identities invoking high-risk filesystem and execution tools via AWS Bedrock Claude. For each identity, monitors the usage of potentially dangerous commands and flags any anomalous activity that deviates from their historical baseline. This may indicate attempts to escalate privileges, exfiltrate data, or execute unauthorized commands.
data_source:
    - AWS Bedrock Claude
search: |-
    `aws_bedrock_claude`
    | spath output="tool_called"       path="output.outputBodyJson{}.content_block.toolUse.name"
    | spath output="out_tokens_stream" path="output.outputBodyJson{}.usage.output_tokens"
    | where tool_called IN ("bash","curl","edit","write","webfetch","grep","read","read_file")
    | eval out_tokens    = coalesce(mvindex('out_tokens_stream', -1), 'output.outputTokenCount', 0)
    | eval in_tokens     = coalesce('input.inputTokenCount', 0)
    | eval user_session  = replace('identity.arn', ".*/", "")
    | eval identity_type = case(
        'identity.arn' LIKE "%botocore-session%", "automated",
        'identity.arn' LIKE "%assumed-role%",     "assumed_role",
        'identity.arn' LIKE "%user%",             "iam_user",
        true(),                                    "other")
    | stats
        count                  AS invocations,
        dc(tool_called)        AS unique_tools_used,
        sum(in_tokens)         AS total_input_tokens,
        sum(out_tokens)        AS total_output_tokens,
        values(tool_called)    AS tools_invoked,
        values('identity.arn') AS caller_list,
        values(identity_type)  AS identity_types,
        dc('identity.arn')     AS unique_callers,
        min(_time)             AS first_seen,
        max(_time)             AS last_seen
      BY modelId, region, user_session
    | eval tokens_per_call = round(total_output_tokens / invocations, 0)
    | eval first_seen = strftime(first_seen - 28800, "%Y-%m-%d %H:%M:%S")
    | eval last_seen  = strftime(last_seen  - 28800, "%Y-%m-%d %H:%M:%S")
    | eval user = user_session
    | eval severity = case(
        mvfind(tools_invoked, "bash") >= 0 AND mvfind(tools_invoked, "curl") >= 0, "critical",
        mvfind(tools_invoked, "bash") >= 0 AND mvfind(tools_invoked, "write") >= 0, "critical",
        mvfind(tools_invoked, "curl") >= 0 AND mvfind(tools_invoked, "edit") >= 0, "critical",
        mvfind(identity_types, "assumed_role") >= 0 AND mvfind(tools_invoked, "bash") >= 0, "critical",
        mvfind(tools_invoked, "bash") >= 0, "high",
        mvfind(tools_invoked, "curl") >= 0, "high",
        mvfind(tools_invoked, "edit") >= 0, "high",
        mvfind(tools_invoked, "write") >= 0, "high",
        mvfind(tools_invoked, "webfetch") >= 0, "high",
        unique_tools_used >= 3, "high",
        true(), "medium")
    | eval risk_reason = case(
        mvfind(tools_invoked, "bash") >= 0 AND mvfind(tools_invoked, "curl") >= 0, "bash and curl invoked together - possible download and execute chain",
        mvfind(tools_invoked, "bash") >= 0 AND mvfind(tools_invoked, "write") >= 0, "bash and write invoked together - possible execution and persistence chain",
        mvfind(tools_invoked, "curl") >= 0 AND mvfind(tools_invoked, "edit") >= 0, "curl and edit invoked together - possible remote content retrieval and file modification",
        mvfind(identity_types, "assumed_role") >= 0 AND mvfind(tools_invoked, "bash") >= 0, "Human identity invoking bash via AI model - direct shell execution risk",
        mvfind(tools_invoked, "bash") >= 0, "bash invoked via Bedrock Claude - shell execution through AI model",
        mvfind(tools_invoked, "curl") >= 0, "curl invoked via Bedrock Claude - network call through AI model",
        mvfind(tools_invoked, "edit") >= 0, "edit invoked via Bedrock Claude - file modification through AI model",
        mvfind(tools_invoked, "write") >= 0, "write invoked via Bedrock Claude - file write through AI model",
        mvfind(tools_invoked, "webfetch") >= 0, "webfetch invoked via Bedrock Claude - remote content retrieval through AI model",
        unique_tools_used >= 3, "Broad filesystem and execution tool set invoked via AI model - reconnaissance pattern",
        true(), "Filesystem tool invoked via Bedrock Claude")
    | where severity IN ("critical","high")
    | table severity, risk_reason, modelId, region, user,unique_tools_used, caller_list, identity_types, invocations, tools_invoked, total_input_tokens, total_output_tokens, tokens_per_call, first_seen, last_seen
    | sort -unique_tools_used, -invocations
    | `aws_bedrock_claude_high_risk_filesystem_and_exec_tool_invocation_filter`
how_to_implement: You must install and configure the Splunk Add-on for AWS (https://splunkbase.splunk.com/app/1876). Enable Amazon Bedrock model invocation logging in AWS so that Claude request/response payloads are delivered to S3 and/or CloudWatch Logs (see https://docs.aws.amazon.com/bedrock/latest/userguide/model-invocation-logging.html for setup steps), then ingest those logs into Splunk via the AWS TA. Configure the `aws_bedrock_claude` macro to point to the index and sourcetype (`json_no_timestamp`) where these logs land.
known_false_positives: This detection may produce false positives for legitimate developer activity where authorized users invoke shell execution, file write, or network tools through Claude Code as part of normal software development, deployment automation, or infrastructure management workflows.
references:
    - https://aws.amazon.com/blogs/apn/unlocking-the-power-of-splunk-with-amazon-bedrock-an-agentic-ai-approach-to-build-customized-splunk-assistants-using-bedrock-agents/
    - https://help.splunk.com/en/splunk-observability-cloud/observability-for-ai/splunk-ai-infrastructure-monitoring/set-up-ai-infrastructure-monitoring/amazon-bedrock
    - https://research.splunk.com/stories/aws_bedrock_security/
    - https://docs.aws.amazon.com/bedrock/latest/userguide/model-invocation-logging.html
intermediate_findings:
    entities:
        - field: user
          type: user
          score: 50
          message: Identity $user$ invoked high-risk filesystem and execution tools $tools_invoked$ via AWS Bedrock Claude with severity $severity$ and reason $risk_reason$.
analytic_story:
    - Suspicious AWS Bedrock Claude Activities
asset_type: Web Application
mitre_attack_id:
    - T1055
product:
    - Splunk Enterprise
    - Splunk Enterprise Security
    - Splunk Cloud
category: application
security_domain: endpoint

Stages and Predicates

Stage 1: search

`aws_bedrock_claude`

Stage 2: spath

| spath output="tool_called"       path="output.outputBodyJson{}.content_block.toolUse.name"

Stage 3: spath

| spath output="out_tokens_stream" path="output.outputBodyJson{}.usage.output_tokens"

Stage 4: where

| where tool_called IN ("bash","curl","edit","write","webfetch","grep","read","read_file")

Stage 5: eval

| eval out_tokens    = coalesce(mvindex('out_tokens_stream', -1), 'output.outputTokenCount', 0)

Stage 6: eval

| eval in_tokens     = coalesce('input.inputTokenCount', 0)

Stage 7: eval

| eval user_session  = replace('identity.arn', ".*/", "")

Stage 8: eval

| eval identity_type = case(
    'identity.arn' LIKE "%botocore-session%", "automated",
    'identity.arn' LIKE "%assumed-role%",     "assumed_role",
    'identity.arn' LIKE "%user%",             "iam_user",
    true(),                                    "other")
identity_type =
iflike('identity.arn', "%botocore-session%")"automated"
eliflike('identity.arn', "%assumed-role%")"assumed_role"
eliflike('identity.arn', "%user%")"iam_user"
else"other"

Stage 9: stats

| stats
    count                  AS invocations,
    dc(tool_called)        AS unique_tools_used,
    sum(in_tokens)         AS total_input_tokens,
    sum(out_tokens)        AS total_output_tokens,
    values(tool_called)    AS tools_invoked,
    values('identity.arn') AS caller_list,
    values(identity_type)  AS identity_types,
    dc('identity.arn')     AS unique_callers,
    min(_time)             AS first_seen,
    max(_time)             AS last_seen
  BY modelId, region, user_session

Stage 10: eval

| eval tokens_per_call = round(total_output_tokens / invocations, 0)

Stage 11: eval

| eval first_seen = strftime(first_seen - 28800, "%Y-%m-%d %H:%M:%S")

Stage 12: eval

| eval last_seen  = strftime(last_seen  - 28800, "%Y-%m-%d %H:%M:%S")

Stage 13: eval

| eval user = user_session

Stage 14: eval

| eval severity = case(
    mvfind(tools_invoked, "bash") >= 0 AND mvfind(tools_invoked, "curl") >= 0, "critical",
    mvfind(tools_invoked, "bash") >= 0 AND mvfind(tools_invoked, "write") >= 0, "critical",
    mvfind(tools_invoked, "curl") >= 0 AND mvfind(tools_invoked, "edit") >= 0, "critical",
    mvfind(identity_types, "assumed_role") >= 0 AND mvfind(tools_invoked, "bash") >= 0, "critical",
    mvfind(tools_invoked, "bash") >= 0, "high",
    mvfind(tools_invoked, "curl") >= 0, "high",
    mvfind(tools_invoked, "edit") >= 0, "high",
    mvfind(tools_invoked, "write") >= 0, "high",
    mvfind(tools_invoked, "webfetch") >= 0, "high",
    unique_tools_used >= 3, "high",
    true(), "medium")
severity =
if`macro` `macro`"critical"
elif`macro` `macro`"critical"
elif`macro` `macro`"critical"
elif`macro` `macro`"critical"
elif`macro`"high"
elif`macro`"high"
elif`macro`"high"
elif`macro`"high"
elif`macro`"high"
elifunique_tools_used >= 3"high"
else"medium"

Stage 15: eval

| eval risk_reason = case(
    mvfind(tools_invoked, "bash") >= 0 AND mvfind(tools_invoked, "curl") >= 0, "bash and curl invoked together - possible download and execute chain",
    mvfind(tools_invoked, "bash") >= 0 AND mvfind(tools_invoked, "write") >= 0, "bash and write invoked together - possible execution and persistence chain",
    mvfind(tools_invoked, "curl") >= 0 AND mvfind(tools_invoked, "edit") >= 0, "curl and edit invoked together - possible remote content retrieval and file modification",
    mvfind(identity_types, "assumed_role") >= 0 AND mvfind(tools_invoked, "bash") >= 0, "Human identity invoking bash via AI model - direct shell execution risk",
    mvfind(tools_invoked, "bash") >= 0, "bash invoked via Bedrock Claude - shell execution through AI model",
    mvfind(tools_invoked, "curl") >= 0, "curl invoked via Bedrock Claude - network call through AI model",
    mvfind(tools_invoked, "edit") >= 0, "edit invoked via Bedrock Claude - file modification through AI model",
    mvfind(tools_invoked, "write") >= 0, "write invoked via Bedrock Claude - file write through AI model",
    mvfind(tools_invoked, "webfetch") >= 0, "webfetch invoked via Bedrock Claude - remote content retrieval through AI model",
    unique_tools_used >= 3, "Broad filesystem and execution tool set invoked via AI model - reconnaissance pattern",
    true(), "Filesystem tool invoked via Bedrock Claude")
risk_reason =
if`macro` `macro`"bash and curl invoked together - possible download and execute chain"
elif`macro` `macro`"bash and write invoked together - possible execution and persistence chain"
elif`macro` `macro`"curl and edit invoked together - possible remote content retrieval and file modification"
elif`macro` `macro`"Human identity invoking bash via AI model - direct shell execution risk"
elif`macro`"bash invoked via Bedrock Claude - shell execution through AI model"
elif`macro`"curl invoked via Bedrock Claude - network call through AI model"
elif`macro`"edit invoked via Bedrock Claude - file modification through AI model"
elif`macro`"write invoked via Bedrock Claude - file write through AI model"
elif`macro`"webfetch invoked via Bedrock Claude - remote content retrieval through AI model"
elifunique_tools_used >= 3"Broad filesystem and execution tool set invoked via AI model - reconnaissance pattern"
else"Filesystem tool invoked via Bedrock Claude"

Stage 16: where

| where severity IN ("critical","high")

Stage 17: table

| table severity, risk_reason, modelId, region, user,unique_tools_used, caller_list, identity_types, invocations, tools_invoked, total_input_tokens, total_output_tokens, tokens_per_call, first_seen, last_seen

Stage 18: sort

| sort -unique_tools_used, -invocations

Stage 19: search

| `aws_bedrock_claude_high_risk_filesystem_and_exec_tool_invocation_filter`

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
severityin
  • "critical"
  • "high"
field:"severity" kind:in
sourcetypeeq
  • json_no_timestamp
field:"sourcetype" kind:eq value:"json_no_timestamp"
tool_calledin
  • "bash"
  • "curl"
  • "edit"
  • "grep"
  • "read"
  • "read_file"
  • "webfetch"
  • "write"
field:"tool_called" kind:in