Back to Intelligence

Qubole IAM Credential Compromise: Defending AWS Environments Against Trusted-Access Cloud Intrusions

SA
Security Arsenal Team
November 10, 2026
12 min read

A threat actor has used compromised AWS Identity and Access Management (IAM) credentials belonging to the Qubole big data platform to access and exfiltrate data from multiple customers' AWS environments. This is not a vulnerability in AWS itself, nor a software flaw waiting for a patch — it is a textbook abuse of trusted access: long-lived credentials held by a SaaS vendor, granted broad permissions inside customer accounts, that became the attacker's master key.

If your organization runs SaaS platforms, ETL pipelines, or managed big data tooling that assumes an IAM role or holds access keys in your AWS account, you are exposed to this exact pattern. The blast radius of a single vendor credential compromise now spans every customer environment that trusted it. This post breaks down the attack chain, gives you detection content you can deploy today, and lays out the hardening steps that actually reduce this risk.

Technical Analysis: How the Attack Works

Affected scope

  • Vendor platform: Qubole, a cloud-native big data / data lake platform that integrates deeply with customer AWS accounts to orchestrate Spark, Hive, Presto, and other workloads against S3 data.
  • Affected environments: Multiple downstream customer AWS accounts that had provisioned Qubole with IAM access (cross-account roles or IAM users with access keys).
  • Attack surface: Any AWS account granting programmatic access to third-party SaaS vendors — particularly where that access is over-permissioned (e.g., broad S3 read/write, iam:*, or wildcard resource policies).

Attack chain (defender's view)

  1. Credential acquisition. The actor obtained Qubole's AWS IAM credentials — most plausibly long-lived access keys (AKIA...) or the ability to assume Qubole's cross-account role. Long-lived vendor keys are the recurring failure mode here: they don't expire, they don't require MFA, and they're often created once during onboarding and never rotated.
  2. Reconnaissance with valid credentials. Using the stolen identity, the actor enumerated the target environments — GetCallerIdentity to confirm the account, ListBuckets / GetBucketLocation to map S3 data stores, and IAM enumeration (ListRoles, ListUsers, GetAccountAuthorizationDetails) to understand what else the identity could reach.
  3. Data access and exfiltration. With S3 permissions inherited from the vendor role, the actor read and staged data — GetObject calls at volume, often via the S3 sync/copy CLI pattern or programmatic batch reads — and moved data out to attacker-controlled infrastructure. Because the API calls come from a known, trusted identity, nothing in a default config alarms.
  4. Multi-customer pivot. The same compromised vendor identity was valid across multiple customer accounts, turning one credential theft into a multi-tenant breach. This is the defining characteristic of trusted-access compromise: the trust relationship itself is the lateral movement.

Why this defeats conventional controls

  • No exploit, no malware. There is no payload to signature, no CVE to patch, no CISA KEV entry to track. The actor used valid credentials and native AWS APIs — T1078.004 (Valid Accounts: Cloud Accounts) combined with T1530 (Data from Cloud Storage).
  • Source IP blind spots. Vendor API traffic legitimately originates from the vendor's infrastructure, so allowlisting "expected" source IPs often whitelists the attacker's initial calls too — until the actor operates from non-vendor infrastructure, which is a key detection pivot.
  • Over-permissioned vendor roles. Big data platforms frequently request broad S3 and IAM permissions "to make onboarding work." That means the compromised identity can read far more data than any single workload requires.

Exploitation status

Confirmed active, in-the-wild exploitation with data exfiltration across multiple customer environments. This is not theoretical. No CVE applies — this is credential abuse, and the relevant response is detection, rotation, and least-privilege re-architecture.

Detection & Response

The detection strategy for trusted-access compromise rests on four pillars: (1) anomalous use of vendor identities, (2) impossible/irregular source infrastructure for those identities, (3) S3 read volume anomalies consistent with exfiltration, and (4) enumeration patterns that precede data theft.

Sigma Rules

YAML
---
title: AWS Vendor IAM Identity Used From Anomalous Source IP
description: Detects a known third-party/vendor IAM identity or assumed role (e.g., Qubole integration roles) being used from source IP addresses outside the vendor's published infrastructure ranges, indicating possible credential compromise.
author: Security Arsenal
date: 2026/04/06
references:
  - https://attack.mitre.org/techniques/T1078/004/
  - https://www.bleepingcomputer.com/news/security/hackers-exploit-trusted-aws-access-to-breach-multiple-cloud-environments/
logsource:
  product: aws
  service: cloudtrail
detection:
  selection_identity:
    userIdentity.arn|contains:
      - ':role/Qubole'
      - ':user/qubole'
  filter_known_ips:
    sourceIPAddress|cidr:
      - 'VENDOR_PUBLISHED_RANGE_1/24'   # Replace with Qubole's documented egress ranges
      - 'VENDOR_PUBLISHED_RANGE_2/24'
  condition: selection_identity and not filter_known_ips
falsepositives:
  - Vendor infrastructure expansion or undocumented egress IPs — maintain an up-to-date vendor IP inventory
level: high
---
title: AWS Cloud Enumeration Burst Following Vendor Identity Authentication
description: Detects rapid IAM/S3 enumeration (GetCallerIdentity, ListBuckets, ListRoles, GetAccountAuthorizationDetails) performed by a programmatic identity, a pattern observed in the reconnaissance phase of cloud credential abuse.
author: Security Arsenal
date: 2026/04/06
references:
  - https://attack.mitre.org/techniques/T1580/
  - https://attack.mitre.org/techniques/T1069/003/
logsource:
  product: aws
  service: cloudtrail
detection:
  selection:
    eventSource:
      - 'iam.amazonaws.com'
      - 's3.amazonaws.com'
      - 'sts.amazonaws.com'
    eventName:
      - 'GetCallerIdentity'
      - 'ListBuckets'
      - 'ListRoles'
      - 'ListUsers'
      - 'ListAccessKeys'
      - 'GetAccountAuthorizationDetails'
      - 'ListAttachedRolePolicies'
  condition: selection
falsepositives:
  - Inventory/CSPM tooling and legitimate vendor orchestration — tune by identity ARN and require volume thresholds (3+ distinct enumeration events within 10 minutes) in your SIEM correlation layer
level: medium
---
title: AWS S3 Bulk Read Activity Consistent With Data Exfiltration
description: Detects high-volume S3 GetObject activity from a single identity consistent with mass data staging and exfiltration, as observed when attackers abused trusted IAM access to pull customer data.
author: Security Arsenal
date: 2026/04/06
references:
  - https://attack.mitre.org/techniques/T1530/
  - https://attack.mitre.org/techniques/T1567/002/
logsource:
  product: aws
  service: cloudtrail
detection:
  selection:
    eventSource: 's3.amazonaws.com'
    eventName: 'GetObject'
    userIdentity.type:
      - 'AssumedRole'
      - 'IAMUser'
  condition: selection
falsepositives:
  - Legitimate big data workloads (Spark/Presto jobs read S3 at volume by design) — deploy this as a SIEM correlation rule keyed on reads to buckets outside the vendor's documented working prefixes, or read volume exceeding baseline by 3x
level: high

Tuning guidance: the S3 bulk-read rule will be noisy against big data identities by design — those workloads read S3 constantly. The signal is not "vendor role read S3"; it is "vendor role read S3 objects in buckets/prefixes it has never touched, at a rate far above baseline, from an unusual source." Correlate in your SIEM with per-identity baselines rather than firing per event.

KQL (Microsoft Sentinel)

Assumes the AWS CloudTrail data connector (table AWSCloudTrail). If you ingest via S3/CEF instead, adapt field names to CommonSecurityLog.

KQL — Microsoft Sentinel / Defender
// Hunt 1: Vendor identity activity from anomalous infrastructure + enumeration burst
let VendorIdentities = dynamic([":role/Qubole", ":user/qubole"]); // extend with your third-party integration ARNs
let VendorRanges = dynamic(["VENDOR_RANGE_1/24", "VENDOR_RANGE_2/24"]); // replace with documented vendor egress CIDRs
let EnumEvents = dynamic(["GetCallerIdentity","ListBuckets","ListRoles","ListUsers","ListAccessKeys","GetAccountAuthorizationDetails","ListAttachedRolePolicies"]);
AWSCloudTrail
| where TimeGenerated > ago(30d)
| where UserIdentityArn has_any (VendorIdentities)
| extend IsKnownIP = ipv4_is_in_any_range(SourceIpAddress, VendorRanges)
| summarize EventCount = count(),
            DistinctAPIs = dcount(EventName),
            EnumHits = countif(EventName in (EnumEvents)),
            S3Reads = countif(EventName == "GetObject"),
            APIs = make_set(EventName, 20),
            IPs = make_set(SourceIpAddress),
            UserAgents = make_set(UserAgent)
  by UserIdentityArn, IsKnownIP, bin(TimeGenerated, 1h)
| where IsKnownIP == false or EnumHits >= 3 or S3Reads > 500
| sort by TimeGenerated desc;

// Hunt 2: S3 reads to never-before-touched buckets by vendor identity (exfil staging)
let VendorIdentities = dynamic([":role/Qubole", ":user/qubole"]);
let HistoricalBuckets =
    AWSCloudTrail
    | where TimeGenerated between (ago(90d) .. ago(7d))
    | where UserIdentityArn has_any (VendorIdentities) and EventName == "GetObject"
    | summarize by tostring(RequestParameters.bucketName);
AWSCloudTrail
| where TimeGenerated > ago(7d)
| where UserIdentityArn has_any (VendorIdentities) and EventName == "GetObject"
| extend Bucket = tostring(RequestParameters.bucketName)
| where Bucket !in (HistoricalBuckets)
| summarize ReadCount = count(), Buckets = make_set(Bucket), SourceIPs = make_set(SourceIpAddress)
  by UserIdentityArn, bin(TimeGenerated, 1h)
| where ReadCount > 100
| sort by ReadCount desc;

Velociraptor VQL

If the intrusion path included attacker tooling landing on any endpoint (e.g., an operator workstation with harvested credentials, or AWS CLI driven from an internal jump box), hunt for AWS CLI usage and credential artifacts outside expected paths:

VQL — Velociraptor
-- Hunt for AWS credential files and CLI executions on endpoints outside expected admin tooling locations
LET creds = SELECT FullPath, Size, Mtime
  FROM glob(globs=['C:/Users/*/.aws/credentials', '/home/*/.aws/credentials', '/root/.aws/credentials'])

LET procs = SELECT Pid, Name, CommandLine, Exe, Username, CreateTime
  FROM pslist()
  WHERE CommandLine =~ '(?i)aws\s+(s3|sts|iam)\s+(cp|sync|get-caller-identity|list-|create-access-key)'
    AND Exe !~ '(?i)Program Files|Amazon'

SELECT * FROM creds
UNION ALL
SELECT FullPath=NULL, Size=NULL, Mtime=CreateTime, Pid=Pid, Name=Name,
       CommandLine=CommandLine, Exe=Exe, Username=Username FROM procs

Remediation Script (Bash)

The following audit-and-rotate script enumerates third-party IAM users and roles, reports key age and last use, and disables keys older than your rotation threshold. Run it read-only first, then with ROTATE=1 to disable stale keys.

Bash / Shell
#!/bin/bash
# third-party-iam-audit.sh — Audit and rotate third-party/vendor IAM credentials
# Usage: ./third-party-iam-audit.sh            (read-only audit)
#        ROTATE=1 ./third-party-iam-audit.sh   (disable keys older than 90 days)
set -euo pipefail

KEY_AGE_DAYS=${KEY_AGE_DAYS:-90}
VENDOR_PATTERN=${VENDOR_PATTERN:-"qubole|vendor|partner|integration|svc-"}
NOW=$(date +%s)

echo "=== IAM USERS MATCHING THIRD-PARTY PATTERN ==="
aws iam list-users --query 'Users[].UserName' --output text | tr '\t' '\n' | \
while read -r user; do
  if echo "$user" | grep -qiE "$VENDOR_PATTERN"; then
    echo "[USER] $user"
    aws iam list-access-keys --user-name "$user" --output json | \
    jq -c '.AccessKeyMetadata[]' | while read -r key; do
      KEY_ID=$(echo "$key" | jq -r '.AccessKeyId')
      STATUS=$(echo "$key" | jq -r '.Status')
      CREATED=$(echo "$key" | jq -r '.CreateDate')
      AGE=$(( (NOW - $(date -d "$CREATED" +%s)) / 86400 ))
      LAST_USED=$(aws iam get-access-key-last-used --access-key-id "$KEY_ID" \
                  --query 'AccessKeyLastUsed.LastUsedDate' --output text 2>/dev/null || echo "Never")
      echo "  Key: $KEY_ID | Status: $STATUS | Age: ${AGE}d | LastUsed: $LAST_USED"
      if [[ "${ROTATE:-0}" == "1" && "$AGE" -gt "$KEY_AGE_DAYS" && "$STATUS" == "Active" ]]; then
        echo "  >>> DISABLING stale key $KEY_ID (age ${AGE}d > ${KEY_AGE_DAYS}d)"
        aws iam update-access-key --user-name "$user" --access-key-id "$KEY_ID" --status Inactive
      fi
    done
  fi
done

echo ""
echo "=== CROSS-ACCOUNT ROLES WITH EXTERNAL TRUST (no ExternalId or stale) ==="
aws iam list-roles --query 'Roles[].RoleName' --output text | tr '\t' '\n' | \
while read -r role; do
  DOC=$(aws iam get-role --role-name "$role" \
        --query 'Role.AssumeRolePolicyDocument' --output json 2>/dev/null || echo "{}")
  if echo "$DOC" | grep -q '"AWS"' && echo "$DOC" | grep -qiE 'arn:aws:iam::[0-9]{12}:(root|user|role)'; then
    HAS_EXTID=$(echo "$DOC" | jq '[.. | .ExternalId? // empty] | length')
    echo "[ROLE] $role | trusts external principal | ExternalId conditions: $HAS_EXTID"
    if [[ "$HAS_EXTID" == "0" ]]; then
      echo "  >>> WARNING: no ExternalId in trust policy — confused-deputy exposure"
    fi
  fi
done

echo ""
echo "=== CLOUDTRAIL: TOP API CALLERS (last 24h) — eyeball vendor identities ==="
aws cloudtrail lookup-events --max-results 50 \
  --query 'Events[].{User:Username,Event:EventName,Time:EventTime}' --output table

echo ""
echo "DONE. If ROTATE=1 was set, stale keys are now Inactive — create replacement keys,"
echo "distribute via your secrets manager, verify vendor service health, then delete old keys."

Remediation: Hardening Against Trusted-Access Compromise

There is no patch for credential abuse — the fix is architectural. Prioritize in this order:

Immediate (24–48 hours)

  1. Rotate every third-party credential in your AWS accounts. Inventory all IAM users and access keys created for vendors (Qubole and otherwise), disable keys older than 90 days, and force rotation. Use the script above. If you suspect exposure, disable first, investigate second.
  2. Audit CloudTrail for the last 90 days (or your retention maximum). Look for vendor identity ARNs used from non-vendor source IPs, enumeration bursts (GetCallerIdentity, ListBuckets, ListAccessKeys), and GetObject volume spikes against buckets outside the vendor's normal working set.
  3. Verify vendor egress ranges. Pull the vendor's documented infrastructure CIDRs and alert on any use of their identity from outside those ranges. If the vendor can't give you ranges, that itself is a risk finding.
  4. Check S3 data events are enabled. Management events alone won't show GetObject. If you aren't logging S3 data events on sensitive buckets, enable them now — selectively, given cost, starting with regulated/critical data.

Short term (1–2 weeks)

  1. Eliminate long-lived vendor access keys. Migrate vendors from IAM users with AKIA keys to cross-account IAM roles with sts:AssumeRole. Roles issue temporary credentials (max 12h), which caps the value of theft.
  2. Enforce ExternalId on every third-party trust policy. The confused-deputy problem is real: without a unique ExternalId per customer, one customer's compromise pattern can be replayed against others. Any vendor integration that doesn't support ExternalId should be escalated or replaced.
  3. Scope vendor permissions to actual workload paths. Big data platforms need access to their staging buckets and prefixes — not s3:* on *. Rewrite vendor policies with explicit bucket ARNs and prefix conditions (s3:prefix), and explicitly Deny access to buckets containing regulated data where the vendor has no business need.
  4. Apply a permissions boundary to vendor-created roles and users so the vendor identity cannot escalate its own privileges or create new principals.

Strategic (30–90 days)

  1. Treat vendor access as a tier-zero asset. Add every third-party role/key to your identity inventory with an owner, a business justification, and a review cadence. Orphaned vendor credentials from completed POCs are a breach waiting for a calendar invite.
  2. Deploy behavior-based cloud detection. Static allowlists fail against valid-credential attacks. You need per-identity baselines (GuardDuty's IAM anomalous behavior findings, UEBA in your SIEM) that catch "the vendor role is suddenly reading buckets it has never touched."
  3. Contractual telemetry rights. For critical vendors, require notification SLAs for security incidents affecting their AWS infrastructure, and — where feasible — log sharing or IP range attestations. The Qubole incident illustrates that customers found out about their own data exposure via a vendor's compromise; contract for better.
  4. Exercise the scenario. Run a tabletop (or purple team) on "vendor credential compromised": Can you enumerate every vendor identity in under an hour? Can you kill all of them in under four hours without breaking production? If not, that's your gap list.

If you suspect you're affected

  • Snapshot and preserve CloudTrail, S3 access logs, and VPC Flow Logs before rotating anything — rotation destroys the actor's access but not your evidence.
  • Scope exfiltration by correlating GetObject events (data events) with bytes-out in VPC Flow Logs / NAT gateway metrics during the exposure window.
  • Assess notification obligations (GDPR, state breach laws, HIPAA/PCI as applicable) based on which buckets the identity could reach, not just which it demonstrably touched — assume maximum privilege realization unless logs prove otherwise.

Related Resources

Security Arsenal Incident Response Services AlertMonitor Platform Book a SOC Assessment incident-response Intel Hub

Is your security operations ready?

Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.