- What: The article discusses the confused deputy problem in SaaS AWS integrations, where a trusted SaaS platform can be tricked into misusing its authority, leading to unauthorized access.
- Impact: Cross-tenant data access problems can arise due to failures in binding tenant identity to cloud identity, potentially leading to unauthorized actions.
Intro Some SaaS “AWS integrations” are only one AssumeRole away from becoming a cross-tenant data access problem. Not because AWS is broken, but because the SaaS integration layer often fails to bind tenant identity to cloud identity in a way that holds up under real attacker behavior (guessing, reusing values, finding legacy trust paths, and abusing configuration surfaces). This post is a practical write-up of confused deputy patterns I repeatedly observed while assessing cloud integrations. Confused Deputy Problem What is the confused deputy problem? A security vulnerability where a trusted, higher-privileged entity (the "deputy") is tricked by a less-privileged entity into misusing its authority to perform actions it shouldn't, leading to unauthorized access or privilege escalation. In this context, the deputy is the trusted SaaS platform that offers cloud integrations (AWS, GCP, Azure). for example S3 import/export, CloudTrail ingestion, or cloud cost analytics. Therefore the SaaS company may have broad access to customer AWS accounts, and it is their responsibility to secure that access. Link to AWS Confused Deputy Documentation AWS Role Assumption There are several ways to integrate an AWS account into a SaaS platform (cross-account access): Static AWS access keys (still common, and sometimes the only option) IAM role assumption OIDC This write-up focuses on IAM role assumption, which allows cross-account access without hardcoded credentials. From AWS Documentation: But how does it work in real life? SaaS company has a custom IAM Role (or IAM User) in their AWS account with an IAM policy that allows to assume any role. Customer creates a role in their own AWS account with a trust policy that trusts SaaS company's AWS account for role assumption with the necessary permissions to access resources. When the setup is complete, the SaaS company's service try to assume the role in customer's account and if succeed, the service will have the same permissions as the IAM role in customers account This seems straightforward. To make it secure, AWS introduced a conditional parameter that must be provided during role assumption from the SaaS service. This is called ExternalId . The idea is that every customer has their own ExternalId value tied to their account within the SaaS platform and included in the IAM role's trust policy. If another customer tries to add the same AWS IAM role for integration, it would have a different ExternalId , so it would not work. This is an ideal scenario, but let me get back to this part later. Below is an example trust policy that grants access to AssumeRole from any user/role in AWS account 560701437021 if the ExternalId value is present: How it started? I was working on an analytics SaaS platform and had already reported issues (including SSRF to AWS metadata, which gave me information about the company's infrastructure and naming conventions). I noticed they had an S3 import integration that used IAM role assumption. I read the documentation and realized that cross-account role assumption with ExternalId seems secure, so there was no obvious path to accessing other customers' data. But I had an idea: what if I tried to access internal resources instead? Everyone trusts the AWS implementation, but has anyone ever checked what happens if the S3 import tries to assume an internal role? Well if everybody would have included this into their threat model, then this write-up would not exist. I tried a few role names using an AWS IAM role enumerator and found a default Readonly role that existed in that account. That gave me access to every production S3 bucket, and I could import the contents via a custom parser to my account. This was the moment that got me thinking, if there is this issue and nobody found it, there must be others. I searched and collected all the available bug bounty targets that have cloud connectors/integrations and started looking into the implementation one by one. That OrganizationAccountAccessRole The first big bounty As I was digging deeper and deeper into AWS IAM documentations I had different ideas that I wanted to test and constantly learned new things after already looking at 10-20 integrations in different programs. One role that consistently came up was OrganizationAccountAccessRole found by my role enumerator in most AWS accounts. But what is it? From AWS Documentation: By default, if you create a member account as part of your organization, AWS automatically creates a role in the account that grants administrator permissions to IAM users in the management account who can assume the role. By default, that role is named OrganizationAccountAccessRole. So this means that if it exists, the AWS account belongs to an AWS organization and from the management account it is possible to assume that role with administrator privileges. One of my target had an AWS integration, and I collected different AWS account IDs from CloudFormation templates used to deploy th...