Skip to content

BeyondTrust/bedrock-keys-security

The AWS Bedrock API keys security toolkit

tl;dr: AWS Bedrock API keys behave nothing like regular AWS credentials. BKS includes an offline decoder, phantom user discovery, incident response, automated cleanup, preventive SCPs and SIEM-ready detection content.

License Python 3.10+ PyPI Black Hat Arsenal US 26 Twitter

Contents: Quickstart · Motivation · Installation · Usage · Prevention · Detection · Migration to STS · Talks · Contributing

Quickstart

pip install bedrock-keys-security
bks scan --profile your-aws-profile

bks scan discovers every BedrockAPIKey-* phantom user in the account, categorizes risk (AT RISK / ACTIVE / ORPHANED) and prints a summary table.

# Decode a leaked key offline (no AWS credentials needed)
bks decode-key "ABSKQmVkcm9ja..."

# Investigate a phantom user across every region with CloudTrail coverage
bks timeline BedrockAPIKey-xxxx --all-regions --days 30

# Emergency revocation: deny Bedrock + delete API keys + disable AKIA pivots
bks revoke-key BedrockAPIKey-xxxx

Detection content (Sigma, CloudTrail Lake, Athena, EventBridge, CloudWatch Insights) lives in detections/. SCP policies and their Terraform / CloudFormation modules live in scps/.

Motivation

AWS Bedrock API keys behave unlike regular AWS credentials. They authenticate via bearer tokens instead of SigV4, and they embed the AWS account ID and IAM username in plain base64.

The most damaging behavior: when a user creates a long-term Bedrock API key through the AWS Console, AWS silently provisions an IAM user named BedrockAPIKey-xxxx and attaches the AmazonBedrockLimitedAccess managed policy.

Despite its name, the policy is effectively administrative: 48 actions across bedrock:* (44) and bedrock-mantle:* (4) covering create / read / update / delete across all Bedrock resources, plus cross-service reconnaissance (iam:ListRoles, kms:DescribeKey, ec2:Describe{Vpcs,Subnets,SecurityGroups}). Full action list in the AWS doc linked above.

These phantom users are never automatically cleaned up. They accumulate over time, creating an expanding attack surface that most organizations don't know exists.

Attack Paths

Attack Paths Diagram

LLMjacking: An attacker who obtains a leaked key can spin up workers across all AWS regions to consume foundation model capacity. Worst-case exposure depends on the default Bedrock service quota and the model price; for Claude Opus 4.7 at list pricing (May 2026), this works out to roughly $18,000/day per region.

LLMjacking Attack Flow

Privilege Escalation: If an attacker creates an IAM access key on the phantom user, or if one already exists, they gain persistent IAM credentials (AKIA...) that extend well beyond Bedrock. From there, they can pivot to S3, Secrets Manager and other services, even after the original Bedrock key expires.

Deep-dive: AWS Bedrock API Keys Security Guide, Part 1: Risks, Vulnerabilities and Attack Techniques on the BeyondTrust Phantom Labs blog.

Installation

Install from PyPI:

pip install bedrock-keys-security

Or install from source:

git clone https://github.com/BeyondTrust/bedrock-keys-security.git
cd bedrock-keys-security
pip install .

Verify the installation:

bks --version

Required AWS permissions per command: see docs/permissions.md.

Usage

Scanning

Run a scan to discover all phantom IAM users in your account:

bks scan                      # scan with default profile
bks scan --profile prod       # use a specific AWS profile
bks scan --region eu-west-1   # override the default us-east-1 region
bks scan --json               # save JSON to output/
bks scan --csv                # save CSV to output/
bks scan --verbose            # detailed output
bks --quiet scan --json       # SOAR pipelines: only the saved-file path goes to stdout

Example output:

bks v1.1.0  BedrockAPIKey-* phantom user scanner
Account: 123456789012  Region: us-east-1

+--------------------+------------+-------------------+---------------+----------+
| Username           | Created    |   Active API Keys |   Access Keys | Status   |
+====================+============+===================+===============+==========+
| BedrockAPIKey-h42z | 2026-03-12 |                 1 |             2 | AT RISK  |
| BedrockAPIKey-x8q1 | 2026-04-22 |                 1 |             0 | ACTIVE   |
| BedrockAPIKey-aaa1 | 2025-11-08 |                 0 |             0 | ORPHANED |
+--------------------+------------+-------------------+---------------+----------+

Summary:
  Total phantom users: 3
  At Risk: 1 (IAM access keys found)
  Active: 1 (live Bedrock API keys)
  Orphaned: 1 (safe to cleanup)

⚠ AT RISK · 1 phantom user with persistent IAM credentials
   - BedrockAPIKey-h42z  (2 access keys)

   These keys inherit Bedrock admin + IAM/VPC/KMS reconnaissance from
   AmazonBedrockLimitedAccess, and persist after Bedrock key revocation.

   → bks revoke-key <username>   emergency containment
   → bks report     <username>   forensic report

▸ ORPHANED · 1 phantom user with no active credentials
   These accumulate over time as privilege-escalation pivots. Cleanup
   shrinks the attack surface; no live workflow is affected.

   → bks cleanup --dry-run   preview deletions
   → bks cleanup             delete with confirmation


Scan complete  127 IAM users  ·  3 phantoms  ·  1.4s

JSON / CSV reports are written to output/bks-scan-<account>-<UTC-timestamp>.<ext> (the directory is created automatically). Override the destination with the global --output-dir DIR flag (e.g. bks --output-dir /var/log/bks scan --json). The JSON shape:

{
  "scan_metadata": {
    "account_id": "123456789012",
    "region": "us-east-1",
    "scan_time": "2026-05-06T14:30:22+00:00",
    "caller_arn": "arn:aws:iam::123456789012:user/security"
  },
  "summary": { "total": 3, "active": 1, "orphaned": 1, "at_risk": 1 },
  "phantom_users": [
    { "username": "BedrockAPIKey-h42z", "status": "AT RISK", "created": "2026-03-12T09:14:08+00:00", "...": "..." }
  ]
}

Each phantom user is categorized by risk level:

  • AT RISK: Has IAM access keys with bedrock:* and recon permissions. Revoking the Bedrock key does not disable them.
  • ACTIVE: Has valid Bedrock API credentials
  • ORPHANED: No active credentials remaining (safe to delete)

Scan Example

Cleanup

Remove orphaned phantom users that no longer have active credentials:

bks cleanup --dry-run         # preview what would be deleted
bks cleanup                   # delete with confirmation prompt
bks cleanup --force           # skip confirmation (use with caution)
bks cleanup --json            # save cleanup result as JSON to output/

Only ORPHANED users are affected. ACTIVE and AT RISK users are never deleted automatically.

Incident Response

When a key is compromised, bks provides emergency response capabilities:

bks revoke-key BedrockAPIKey-xxxx                 # emergency key revocation
bks revoke-key BedrockAPIKey-xxxx --force         # skip confirmation
bks revoke-key BedrockAPIKey-xxxx --json          # save revocation result to output/
bks timeline BedrockAPIKey-xxxx                   # CloudTrail timeline (last 7 days, configured region)
bks timeline BedrockAPIKey-xxxx --days 30         # extended timeline
bks timeline BedrockAPIKey-xxxx --all-regions     # fan out across every region with CloudTrail coverage
bks timeline BedrockAPIKey-xxxx --json            # save events list as JSON to output/
bks report BedrockAPIKey-xxxx                     # full incident report
bks report BedrockAPIKey-xxxx --output report.txt # save text report to FILE
bks report BedrockAPIKey-xxxx --json              # save report data as JSON to output/

revoke-key applies an inline Deny: bedrock:* policy, deletes all Bedrock service-specific credentials and disables IAM access keys (AKIA*) on the phantom user, closing the privilege-escalation pivot in the same operation.

timeline --all-regions is recommended whenever LLMjacking is suspected. Bedrock data-plane events (InvokeModel, Converse, CallWithBearerToken) are recorded in the region they ran, not the home region; a single-region timeline misses cross-region fan-out by design.

Revoke Key

Key Decoding

Decode leaked Bedrock API keys offline, no AWS credentials required:

bks decode-key "ABSKQmVkcm9ja0FQSUtleS..."          # print analysis to terminal
bks decode-key "bedrock-api-key-YmVkcm9ja..." --json # save JSON to output/

Extracts the embedded IAM username, AWS account ID, region and key format. Useful for triaging keys found on GitHub, Pastebin or other public sources. With --json, writes output/bks-decode-<account>-<UTC-timestamp>.json.

Long-term Key Decode

Short-term Key Decode

Prevention with Service Control Policies

Four SCPs are provided for organizational enforcement. Apply via AWS Organizations.

SCP File Purpose
Block all keys (recommended) scps/1-block-all-keys.json Deny creation + usage org-wide
Enforce 90-day max scps/2-enforce-90day-max.json Limit damage window
Block long-term only scps/3-block-long-term-only.json Allow short-term, block ABSK
Block phantom escalation scps/4-block-phantom-access-keys.json Close privesc pivot

Deploy any SCP via:

aws organizations create-policy \
  --name <NAME> \
  --type SERVICE_CONTROL_POLICY \
  --content file://scps/<FILE>

aws organizations attach-policy \
  --policy-id p-xxxxx \
  --target-id <ROOT_OR_OU_ID>

Note: Always test SCPs on non-production OUs before applying broadly.

Infrastructure as Code

The same four SCPs are available as ready-to-deploy modules:

  • Terraform: scps/terraform/ wraps the four SCPs as aws_organizations_policy resources with optional OU attachment.
  • CloudFormation: scps/cloudformation/scps.yaml is a single template with conditional resources, StackSet-friendly.

Both default to enabling Block-Bedrock-API-Keys plus Block-Phantom-User-Escalation, the recommended baseline pair.

Detection Content

SIEM-ready detection rules for the full attack chain are in detections/: 6 Sigma rules, 2 CloudTrail Lake queries, 2 Athena queries, 5 EventBridge patterns and 1 CloudWatch Insights query. Coverage spans bearer-token usage, key creation, phantom-user creation, AKIA escalation, cross-region fan-out and suspicious user-agents.

Migration to STS

Most teams do not need Bedrock API keys. AWS STS temporary credentials are the recommended approach:

  • Automatically expire (1 to 12 hours)
  • No phantom users created
  • Standard AWS SigV4 signing (not bearer tokens)
  • No persistent credentials to leak
aws sts assume-role \
  --role-arn arn:aws:iam::ACCOUNT:role/BedrockRole \
  --role-session-name bedrock-session \
  --duration-seconds 3600

export AWS_ACCESS_KEY_ID=ASIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...

aws bedrock-runtime converse \
  --model-id us.anthropic.claude-opus-4-7 \
  --messages '[{"role":"user","content":[{"text":"hello"}]}]'

API keys may still be necessary for legacy applications hardcoded for bearer tokens, third-party tools without SigV4 support or vendor software lacking STS integration. In those cases, use short-term keys with a maximum 12-hour lifetime and enforce restrictions with the SCPs above.

Talks

  • Black Hat USA 2026 Arsenal (upcoming, August 2026): Bedrock Keys Security (BKS): Hunting Phantom IAM Users Created by AWS Bedrock API Keys (session)
  • BSides Seattle 2026: The Phantom of the Infrastructure: Investigating the Hidden IAM Risks in Bedrock API Keys (slides, video)
  • RootedCON Madrid 2026: The Phantom of the Infrastructure: The Invisible Threat in Bedrock API Keys

Contributing

See CONTRIBUTING.md for development setup, PR workflow and review requirements.

License

Apache 2.0. See LICENSE.

Contact

References

About

Detect phantom IAM users, decode leaked AWS Bedrock API keys, and prevent LLMjacking. CLI + SCPs + SIEM detection rules.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors