Back to Intelligence

Qubole Cross-Account IAM Abuse: Detecting and Remediating Third-Party AWS Role Assumption in Your Environment

SA
Security Arsenal Team
November 9, 2026
14 min read

Wiz recently disclosed an incident that should make every cloud security team re-examine their third-party trust relationships: a threat actor obtained IAM credentials belonging to Qubole's own AWS account and used them to assume integration roles that customers had created — trusting Qubole — inside their own AWS environments. This is the cloud-native version of a supply-chain compromise. No vulnerability was exploited in your infrastructure. No malware was dropped. The attacker simply walked through a door you intentionally left unlocked for a vendor, using the vendor's stolen key.

If your organization uses (or historically used) Qubole for data lake, ETL, or big-data orchestration on AWS, you need to assume the integration role in your account may have been accessed by an unauthorized party and hunt accordingly. Even if you are not a Qubole customer, the defensive pattern here — auditing external cross-account role assumption — applies to every SaaS integration in your AWS estate. This post breaks down the attack chain, gives you concrete CloudTrail detections in Sigma, KQL, and VQL, and walks through containment and long-term hardening of third-party IAM trust.

Technical Analysis: How the Attack Worked

The Trust Model Qubole (and Many SaaS Vendors) Require

Qubole's architecture requires customers to create an IAM role in their AWS account — often called a cross-account or integration role — with a trust policy allowing principals from Qubole's AWS account to call sts:AssumeRole against it. This is standard practice for SaaS data platforms: the vendor needs to spin up clusters, read from S3 buckets, and manage compute on your behalf. The typical trust relationship looks like this:

Code
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::<QUBOLE-ACCOUNT-ID>:root" },
      "Action": "sts:AssumeRole",
      "Condition": { "StringEquals": { "sts:ExternalId": "<your-external-id>" } }
    }
  ]
}

The critical weakness in this model is that your security boundary is only as strong as the vendor's. If an attacker compromises credentials inside the vendor's AWS account — access keys for a user or role that is permitted to assume your integration role — they inherit every permission you granted that role, from an AWS IP space you likely do not alert on, under an identity you explicitly trust.

Observed Attack Chain

Based on the reported activity, the intrusion unfolded along these lines:

  1. Credential theft in the vendor's account. The threat actor obtained IAM credentials from Qubole's AWS environment. These credentials carried permission to call sts:AssumeRole into customer accounts.
  2. Cross-account role assumption. The attacker called sts:AssumeRole from Qubole's account into customer integration roles. In CloudTrail, these appear as AssumeRole events where the userIdentity.accountId (the calling account) is Qubole's and the recipientAccountId is the customer's — an event that looks entirely normal unless you are baselining session context, source IP, and timing.
  3. Post-assumption activity. Using the temporary session credentials (issued to a role session typically named something like qubole-session or the vendor's integration session name), the attacker performed reconnaissance and data access inside customer accounts — IAM enumeration (ListRoles, GetRole, ListUsers), S3 bucket discovery and access, and other actions within the scope of the role's policy.
  4. Blending in. Because the assumed role was purpose-built for the vendor, its activity is frequently excluded from anomaly detection logic, excluded from break-glass alerting, and in many organizations not even ingested with the same scrutiny as human IAM activity. That blind spot is precisely what made this vector effective.

Why This Is Not a "CVE Problem"

There is no CVE here, no patch, no vulnerable package version. This is credential compromise plus implicit trust — MITRE ATT&CK T1078.004 (Valid Accounts: Cloud Accounts) combined with T1550 (Use Alternate Authentication Material). The exploitation status is confirmed active exploitation in the wild, as reported by Wiz's incident research team. Your detections must therefore be behavioral and relational, not signature-based: you are looking for abnormal use of a legitimate trust path.

What Is at Risk

Whatever the integration role can do, the attacker could do. In typical Qubole deployments that includes:

  • S3 access to data lake buckets (frequently broad: s3:GetObject, s3:ListBucket on production data)
  • EC2/EMR orchestration permissions (ability to launch compute, which doubles as cryptomining/persistence opportunity)
  • IAM read permissions for enumeration and privilege-mapping
  • In over-privileged deployments, iam:PassRole, wildcard actions, or even administrative access

If you granted the vendor role more than least-privilege — and historically most customers did — the blast radius of this compromise is the blast radius of your own role design.

Detection & Response

Hunting Strategy: What to Look For

Before writing a single rule, orient on the observables that distinguish malicious assumption from legitimate vendor traffic:

  • AssumeRole events where the calling account is the vendor's AWS account ID, but the sourceIPAddress does not match the vendor's documented egress ranges, or the userAgent deviates from the vendor's normal SDK/automation agents
  • Session names that deviate from the vendor's established naming convention
  • First-seen API calls under the vendor role session: GetCallerIdentity, ListBuckets, ListRoles, ListUsers, GetAccountAuthorizationDetails — classic post-compromise enumeration a data platform rarely performs
  • Temporal anomalies: vendor role activity outside the vendor's normal job windows, or from regions the integration never uses
  • UpdateAssumeRolePolicy, PutRolePolicy, AttachRolePolicy, CreateAccessKey, CreateLoginProfile — any attempt to modify IAM in your account using the vendor role session
  • S3 GetObject volume anomalies on data lake buckets attributed to the vendor role session (data staging/exfiltration)

Sigma Rules (AWS CloudTrail)

These rules assume you ship CloudTrail into a SIEM that supports Sigma's cloudtrail service (e.g., via Sigma backend conversion to Splunk/Elastic). Tune the vendor account ID and session-name allowlists to your environment before deploying.

YAML
---
title: Cross-Account AssumeRole from External Third-Party Account
description: Detects sts:AssumeRole events where the calling account differs from the recipient account, indicating external cross-account role assumption. Baseline known vendor account IDs (e.g., Qubole) and alert on any external account not on the allowlist, or on vendor assumptions with unusual session names or source IPs.
references:
  - https://www.wiz.io/blog/qubole-cross-account-access-abused-in-the-wild
  - https://attack.mitre.org/techniques/T1078/004/
author: Security Arsenal
date: 2026/06/30
id: 3f8a2c71-9b4e-4d1a-a7c2-5e6f8d0b1c3a
status: experimental
logsource:
  product: aws
  service: cloudtrail
detection:
  selection:
    eventSource: 'sts.amazonaws.com'
    eventName: 'AssumeRole'
  filter_known_vendors:
    userIdentity.accountId:
      - 'REPLACE_WITH_KNOWN_VENDOR_ACCOUNT_ID_1'
      - 'REPLACE_WITH_KNOWN_VENDOR_ACCOUNT_ID_2'
  condition: selection and not filter_known_vendors
falsepositives:
  - Legitimate SaaS integrations and MSP access — maintain an accurate allowlist of approved external account IDs
level: high
---
title: Cloud Enumeration Activity Under Vendor Assumed Role Session
description: Detects IAM and account enumeration API calls performed under an assumed role session, consistent with post-compromise reconnaissance observed after theft of third-party (Qubole) AWS credentials. Data-platform integration roles rarely enumerate IAM principals.
references:
  - https://www.wiz.io/blog/qubole-cross-account-access-abused-in-the-wild
  - https://attack.mitre.org/techniques/T1087/004/
author: Security Arsenal
date: 2026/06/30
id: 6b1d4e92-7c3a-4f8b-b2d9-1a5e7c0f3d6b
status: experimental
logsource:
  product: aws
  service: cloudtrail
detection:
  selection:
    userIdentity.type: 'AssumedRole'
    eventName:
      - 'ListUsers'
      - 'ListRoles'
      - 'ListGroups'
      - 'GetAccountAuthorizationDetails'
      - 'ListAttachedRolePolicies'
      - 'ListRolePolicies'
      - 'GetCallerIdentity'
      - 'ListBuckets'
  condition: selection
falsepositives:
  - Vendor platform health checks and inventory scans — baseline per-session-name and filter the vendor's documented automation user agents
level: medium
---
title: IAM Modification via Third-Party Assumed Role Session
description: Detects IAM write actions (policy attachment, trust policy modification, access key creation) executed under an assumed role session. Third-party integration roles such as Qubole's should never modify IAM in customer accounts; this is a high-fidelity indicator of attacker persistence attempts following cross-account credential abuse.
references:
  - https://www.wiz.io/blog/qubole-cross-account-access-abused-in-the-wild
  - https://attack.mitre.org/techniques/T1098/
author: Security Arsenal
date: 2026/06/30
id: 9c2e5f14-3d7b-4a6c-c8e1-2b4f6a9d0e5c
status: experimental
logsource:
  product: aws
  service: cloudtrail
detection:
  selection:
    userIdentity.type: 'AssumedRole'
    eventName:
      - 'UpdateAssumeRolePolicy'
      - 'PutRolePolicy'
      - 'AttachRolePolicy'
      - 'CreateAccessKey'
      - 'CreateLoginProfile'
      - 'UpdateLoginProfile'
      - 'CreateUser'
      - 'CreateRole'
      - 'AddUserToGroup'
  filter_vendor_sessions:
    userIdentity.principalId|contains: 'REPLACE_WITH_KNOWN_VENDOR_ROLE_ID'
    userIdentity.arn|contains: 'REPLACE_WITH_APPROVED_AUTOMATION_ARN'
  condition: selection and not filter_vendor_sessions
falsepositives:
  - Infrastructure-as-code pipelines using assumed roles (Terraform/CloudFormation execution roles) — exclude known IaC role ARNs
level: critical

KQL — Microsoft Sentinel (AWSCloudTrail)

If you ingest CloudTrail into Sentinel via the AWS connector, this query hunts the full behavior chain: external AssumeRole events, sessions that deviate from expected patterns, and enumeration/IAM-write activity under vendor sessions. Update the vendor account ID and expected session-name patterns before running in production.

KQL — Microsoft Sentinel / Defender
// Hunt 1: Cross-account AssumeRole events into your account from external AWS accounts
let KnownVendorAccounts = dynamic(["REPLACE_WITH_QUBOLE_ACCOUNT_ID", "REPLACE_WITH_OTHER_APPROVED_VENDOR_ID"]);
AWSCloudTrail
| where TimeGenerated > ago(30d)
| where EventSource == "sts.amazonaws.com" and EventName == "AssumeRole"
| extend CallerAccount = tostring(UserIdentityAccountId)
| extend RecipientAccount = tostring(RecipientAccountId)
| where CallerAccount != RecipientAccount
| extend IsKnownVendor = iff(CallerAccount in (KnownVendorAccounts), true, false)
| extend SessionName = tostring(parse_json(RequestParameters).roleSessionName)
| project TimeGenerated, CallerAccount, RecipientAccount, SessionName, SourceIpAddress, UserAgent, UserIdentityArn, IsKnownVendor
| order by TimeGenerated desc;
// Hunt 2: Enumeration + IAM-write activity under AssumedRole sessions attributed to vendor accounts
let EnumEvents = dynamic(["ListUsers","ListRoles","GetAccountAuthorizationDetails","ListBuckets","GetCallerIdentity","ListRolePolicies","ListAttachedRolePolicies"]);
let IAMWriteEvents = dynamic(["UpdateAssumeRolePolicy","PutRolePolicy","AttachRolePolicy","CreateAccessKey","CreateUser","CreateRole","CreateLoginProfile"]);
AWSCloudTrail
| where TimeGenerated > ago(30d)
| where UserIdentityType == "AssumedRole"
| where EventName in (EnumEvents) or EventName in (IAMWriteEvents)
| extend Risk = iff(EventName in (IAMWriteEvents), "IAM_WRITE", "ENUMERATION")
| summarize EventCount = count(), Events = make_set(EventName), SourceIPs = make_set(SourceIpAddress) by UserIdentityArn, UserIdentityPrincipalId, bin(TimeGenerated, 1h), Risk
| order by TimeGenerated desc;

Velociraptor VQL — Endpoint Artifact Hunt

Cross-account IAM abuse is primarily a control-plane (CloudTrail) problem, but IR scoping often requires checking whether attacker tooling landed on any compute the compromised role could reach — e.g., EC2 instances the vendor role could launch or access. This artifact hunts endpoints for AWS CLI usage performing assume-role calls and for cached STS session credentials, which attackers frequently stage after pivoting to instances.

VQL — Velociraptor
-- Hunt for AWS CLI assume-role execution and staged STS session credentials on endpoints
SELECT Pid, Name, CommandLine, Exe, Username, CreateTime
FROM pslist()
WHERE CommandLine =~ '(?i)aws\s+sts\s+assume-role'
   OR CommandLine =~ '(?i)aws_access_key_id|aws_secret_access_key|aws_session_token'
   OR Exe =~ '(?i)aws(\.(exe|com))?$'

-- Complement with a filesystem hunt for cached CLI credentials outside expected user profiles:
-- SELECT FullPath, Size, Mtime FROM glob(globs='C:/Users/*/.aws/credentials')
-- WHERE NOT FullPath =~ '(?i)(svc|admin|expected-service-account)'

Remediation / Audit Script — AWS CLI

This Bash script inventories every IAM role in your account whose trust policy allows an external AWS account to assume it, flags roles trusting a specific vendor account ID, checks whether an ExternalId condition is present, and pulls recent CloudTrail AssumeRole history for triage. Run it per-account, or wrap it in an AWS Organizations loop across your estate.

Bash / Shell
#!/bin/bash
# Audit cross-account IAM trust relationships and vendor role assumption activity
# Requires: awscli, jq; credentials with iam:ListRoles, iam:GetRole, cloudtrail:LookupEvents

VENDOR_ACCOUNT_ID="REPLACE_WITH_QUBOLE_ACCOUNT_ID"
MY_ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
echo "[*] Auditing account: $MY_ACCOUNT_ID for external trusts (vendor: $VENDOR_ACCOUNT_ID)"

echo "[*] Enumerating roles with external cross-account trust policies..."
for ROLE in $(aws iam list-roles --query 'Roles[].RoleName' --output text); do
  TRUST=$(aws iam get-role --role-name "$ROLE" --query 'Role.AssumeRolePolicyDocument' --output json)
  EXTERNAL=$(echo "$TRUST" | jq -r --arg me "$MY_ACCOUNT_ID" '
    .Statement[]? | select(.Effect=="Allow") | .Principal.AWS? // empty |
    if type=="array" then .[] else . end' 2>/dev/null | grep -v "$MY_ACCOUNT_ID" | grep -E '^arn:aws:iam::[0-9]{12}:')
  if [ -n "$EXTERNAL" ]; then
    HAS_EXTID=$(echo "$TRUST" | jq -r '[.Statement[]?.Condition?.StringEquals?.["sts:ExternalId"] // empty] | length')
    echo "[!] EXTERNAL TRUST: $ROLE -> $EXTERNAL (ExternalId conditions: $HAS_EXTID)"
    echo "$TRUST" | jq '.' > "trust_${ROLE}.json"
    if echo "$TRUST" | grep -q "$VENDOR_ACCOUNT_ID"; then
      echo "[CRITICAL] Role $ROLE trusts the vendor account $VENDOR_ACCOUNT_ID — review and rotate immediately"
    fi
  fi
done

echo "[*] CloudTrail: AssumeRole events in the last 7 days..."
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRole \
  --start-time "$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ)" \
  --query 'Events[].{Time:EventTime,User:Username,Event:CloudTrailEvent}' \
  --output json | jq -r '.[] | .Time + " " + .User + " " + (.Event | fromjson | (.userIdentity.accountId // "?") + " -> " + (.recipientAccountId // "?") + " src=" + (.sourceIPAddress // "?"))'

echo "[*] Done. Review flagged roles: rotate ExternalIds, tighten trust policies, and scope session activity."

Remediation: Contain, Rotate, Harden

Immediate Containment (Hours)

  1. Identify every role trusting the vendor's account. Use the script above, or AWS IAM Access Analyzer (which reports external access findings per role — enable it in every region). Inventory every SaaS integration role, not just Qubole's.
  2. If you are a Qubole customer: suspend or tightly constrain the integration role until you have validated activity. At minimum, temporarily deny sts:AssumeRole from the vendor account, or scope the role's permissions down to read-only while you investigate.
  3. Rotate the ExternalId. Treat the external ID as a shared secret — if the vendor's environment was compromised, assume it is known. Issue a new external ID through the vendor's console/support process and update the trust policy condition.
  4. Scope CloudTrail for the exposure window. Query all API activity performed under the vendor role session ARNs for at least the last 90 days (CloudTrail event history retention) or your full SIEM/S3 log archive. Look for enumeration, IAM writes, S3 GetObject volume spikes, CreateAccessKey, and EC2 launches you cannot attribute to legitimate vendor jobs.
  5. Revoke active sessions. Modifying the role's trust policy or attaching an explicit deny invalidates future assumptions; existing sessions (up to their maximum duration) must be revoked via a deny-all inline policy on the role or by deleting/recreating it during incident response.

Validation and Forensics (Days)

  • CloudTrail Lake / Athena queries over your organizational trail: correlate every AssumeRole from external accounts with downstream API calls by session ARN (userIdentity.principalId suffix = session name).
  • S3 server access logs / CloudTrail data events on data lake buckets: quantify object reads attributable to the vendor sessions to determine whether data was staged or exfiltrated — this drives your notification obligations (contractual, regulatory, or both).
  • EC2/EMR audit: identify any compute launched by the vendor role during the window; isolate and image before terminating.
  • Credential exposure check: verify whether any customer-side credentials (e.g., database connection strings stored in Qubole's configuration) were accessible to the vendor platform and rotate them.

Durable Hardening (Weeks)

  1. Enforce ExternalId on every third-party trust. It mitigates confused-deputy abuse, though it does not help once the vendor's account itself is breached — which is why the next controls matter more.
  2. Constrain trust policies with conditions: aws:SourceIp (vendor's published egress ranges), aws:SourceVpc/VPC endpoint conditions where the vendor supports it, and short MaxSessionDuration (1 hour) on integration roles.
  3. Least-privilege the integration role. Strip wildcards. Scope S3 actions to specific bucket ARNs, deny iam:* writes entirely, and use permission boundaries so even a compromised role cannot escalate.
  4. Alert on the trust path, not just the account. Deploy the Sigma/KQL detections above. Specifically: alert on (a) any new external account appearing in AssumeRole events, (b) enumeration or IAM-write APIs under any AssumedRole session, and (c) vendor session activity from novel source IPs or user agents.
  5. Monitor trust policy changes as a Tier-1 alert. UpdateAssumeRolePolicy in any account should page a human — it is the single event that opens this entire attack class.
  6. Vendor risk governance. Require SaaS vendors to disclose their own cloud security posture (separate prod/corp accounts, hardware-key MFA on human access, key rotation cadence) and their breach notification SLAs. Contractually require notification within 24–72 hours of a vendor-side credential compromise affecting your integration.
  7. Consider IAM Roles Anywhere / OIDC alternatives where vendors support workload identity federation with audience restrictions, reducing reliance on long-lived shared trust.

The Bigger Lesson

This incident is the canonical demonstration of a principle we repeat in every cloud IR engagement: cross-account trust relationships are part of your attack surface, and their compromise produces no malware, no exploit, and no vulnerability scan finding. The attacker never touched your perimeter. They authenticated successfully with valid material through an authorized path. The only defenses that work are least privilege on the role, tight conditions on the trust, and behavioral detection on what the session actually does after assumption. Audit every external trust in your organization this week — Qubole is the headline, but your exposure is every vendor role you have ever created and forgotten.

Related Resources

Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub

Is your security operations ready?

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