Skip to content
  • HOME
  • APPS
  • GAMES
  • BOOKS
  • ABOUT
  • CONTACT
  • POLICY
    • Privacy
    • Terms Of Service
    • Return/Refund Policy
Follow Why Surf Swim on WordPress.com

ALL RIGHTS RESERVED

Creative Commons License
Why Surf Swim by PBKN is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.

Favorites For the Day

  • Colorful Sisters's avatar
  • Cooking Katie's avatar
  • Unknown's avatar
  • Unknown's avatar
  • Unknown's avatar
  • tipswoodworkingcom's avatar
  • Ian's avatar
  • SnappyVPN's avatar
  • Unknown's avatar
  • Emma Watt's avatar

WSS Reach

  • 458,972 Viewers around GLOBE
Do Not Sell or Share My Personal Information
  • AWS
  • Working Suggestions
  • Android Development
  • Ideas
Why Surf Swim

Why Surf Swim

why surf the web ? swim it !!!

  • HOME
  • APPS
  • GAMES
  • BOOKS
  • ABOUT
  • CONTACT
  • POLICY
    • Privacy
    • Terms Of Service
    • Return/Refund Policy
  • AWS

The Confused Deputy Problem: A Critical AWS Security Challenge (and How to Fix It)

PBKN's avatar PBKN February 13, 2025 0 Comments

  • 1. Service-Level Deep Dive
    • 1.1 Architecture Diagram and Explanation
    • 1.2 Setup in “poc” Account
    • 1.3 Setup in “partner” Account
    • 1.4 Triggering Lambda in “partner” Account
    • 1.5 Changing the TRUST policy in “poc” Account
    • 1.6 Demo Video
  • 2. Role-Level Deep Dive
    • 2.1 Setup in “mgmt” Account
    • 2.2 Code Changes in “partner” Account
    • 2.3 Prevent Confused Deputy Problem with STS External ID
    • 2.4 Access denied for “partner” to “mgmt” account
    • 2.5 Demo Video
  • Conclusion

1. Service-Level Deep Dive

By default, AWS enforces a tight security model. Trust policies are set to allow only internal (same-account) principals—like AWS Lambda functions—to assume specific roles. This restriction is a built-in safeguard against the confused deputy scenario.

1.1 Architecture Diagram and Explanation

This diagram illustrates how the default configuration prevents an external account (the partner account) from assuming a role that’s meant only for internal use within the poc account.

1.2 Setup in “poc” Account

The IAM role in the “poc” account initially has the following trust policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

This policy explicitly allows the lambda.amazonaws.com service within the “poc” account to assume this role. Crucially, it does not allow services from other accounts to assume this role.

1.3 Setup in “partner” Account

The Lambda function in the “partner” account attempts to assume the role in the “poc” account:

import boto3

def lambda_handler(event, context):
    s3 = boto3.client('s3')
    
    list_buckets_from_assumed_role_without_mfa(
        assume_role_arn="arn:aws:iam::381491851702:role/STS-Assume-S3-Read-Only-POC",
        session_name="AssumeRoleSession",
        sts_client=boto3.client("sts")
    )

def list_buckets_from_assumed_role_without_mfa(assume_role_arn, session_name, sts_client):
    """
    Assumes a role from another account and uses the temporary credentials
    to list the S3 buckets in that account.
    
    :param assume_role_arn: The ARN of the role to assume.
    :param session_name: The name for the STS session.
    :param sts_client: A Boto3 STS client with permission to assume the role.
    """
    response = sts_client.assume_role(
        RoleArn=assume_role_arn,
        RoleSessionName=session_name,
    )
    temp_credentials = response["Credentials"]
    print(f"Assumed role {assume_role_arn} and got temporary credentials.")

    s3_resource = boto3.resource(
        "s3",
        aws_access_key_id=temp_credentials["AccessKeyId"],
        aws_secret_access_key=temp_credentials["SecretAccessKey"],
        aws_session_token=temp_credentials["SessionToken"],
    )

    print("Listing buckets for the assumed role's account:")
    for bucket in s3_resource.buckets.all():
        print(bucket.name)

1.4 Triggering Lambda in “partner” Account

When the Lambda function in the “partner” account is triggered, the sts:AssumeRole call fails with an AccessDenied error. This is expected and desirable. The error message clearly indicates the problem:

[ERROR] ClientError: An error occurred (AccessDenied) when calling the AssumeRole operation: 
User: arn:aws:sts::307946673750:assumed-role/STS-Assume-Role-based-Service-Lambda-Role/STS-Assume-Role-based-Service-Cross-Account 
is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::381491851702:role/STS-Assume-S3-Read-Only-POC

This demonstrates that AWS’s default behavior prevents cross-account role assumption, protecting the “poc” account from unauthorized access

1.5 Changing the TRUST policy in “poc” Account

To allow the “partner” account’s Lambda function to assume the role, the trust policy in the “poc” account must be explicitly updated to include the “partner” account’s principal:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com",
        "AWS": "arn:aws:iam::307946673750:root" // Partner account ID
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

This updated policy now explicitly trusts the “partner” account’s root user (or a specific role within the partner account) to assume the role. Only after this change will the Lambda function in the “partner” account be able to successfully assume the role and access the S3 bucket in the “poc” account.

Assumed role arn:aws:iam::381491851702:role/STS-Assume-S3-Read-Only-POC and got temporary credentials.
Listing buckets for the assumed role's account:

1.6 Demo Video

2. Role-Level Deep Dive

This part delves into the problem at the role level, showcasing how a seemingly trusted intermediary can be exploited and how to mitigate this risk using STS External IDs.

2.1 Setup in “mgmt” Account

We introduce a third account, the “mgmt” account, containing a role (STS-Assume-S3-Read-Only-MGMT) that the “poc” account needs to access. The initial trust policy in the “mgmt” account’s role allows the “poc” account to assume it:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::381491851702:root" // POC Account ID
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

The setups in the “poc” and “partner” accounts remain the same as described in Part 1.

2.2 Code Changes in “partner” Account

The “partner” account’s Lambda function is modified to exploit the trust relationship between “poc” and “mgmt”. It first assumes the role in the “poc” account (as before) and then uses those temporary credentials to assume the role in the “mgmt” account:

import boto3

def lambda_handler(event, context):
    # Initially, assume the role from the poc account
    list_buckets_from_assumed_role_without_mfa(
        assume_role_arn="arn:aws:iam::381491851702:role/STS-Assume-S3-Read-Only-POC",
        session_name="AssumeRoleSession",
        sts_client=boto3.client("sts")
    )

def list_buckets_from_assumed_role_without_mfa(assume_role_arn, session_name, sts_client):
    """
    Assumes a role from the poc account and uses the temporary credentials to list S3 buckets.
    """
    response = sts_client.assume_role(
        RoleArn=assume_role_arn,
        RoleSessionName=session_name,
    )
    temp_credentials = response["Credentials"]
    print(f"Assumed role {assume_role_arn} and got temporary credentials.")

    s3_resource = boto3.resource(
        "s3",
        aws_access_key_id=temp_credentials["AccessKeyId"],
        aws_secret_access_key=temp_credentials["SecretAccessKey"],
        aws_session_token=temp_credentials["SessionToken"],
    )

    print("Listing buckets for the assumed role's account:")
    for bucket in s3_resource.buckets.all():
        print(bucket.name)
    
    # Create a new session using the assumed poc account credentials
    new_session = boto3.Session(
        aws_access_key_id=temp_credentials['AccessKeyId'],
        aws_secret_access_key=temp_credentials['SecretAccessKey'],
        aws_session_token=temp_credentials['SessionToken']
    )

    # Now, attempt to assume the mgmt account role using the new session
    list_buckets_from_confused_deputy_role(
        assume_role_arn="arn:aws:iam::401135408972:role/STS-Assume-S3-Read-Only-MGMT",
        session_name="AssumeRoleSession",
        sts_client=new_session.client('sts')
    )

def list_buckets_from_confused_deputy_role(assume_role_arn, session_name, sts_client):
    """
    Assumes a role from the mgmt account using the temporary credentials from the poc account.
    """
    response = sts_client.assume_role(
        RoleArn=assume_role_arn,
        RoleSessionName=session_name,
    )
    temp_credentials = response["Credentials"]
    print(f"Assumed role {assume_role_arn} and got temporary credentials.")

    s3_resource = boto3.resource(
        "s3",
        aws_access_key_id=temp_credentials["AccessKeyId"],
        aws_secret_access_key=temp_credentials["SecretAccessKey"],
        aws_session_token=temp_credentials["SessionToken"],
    )

    print("Listing buckets for the assumed mgmt account role:")
    for bucket in s3_resource.buckets.all():
        print(bucket.name)

This code successfully exploits the Confused Deputy problem. The “partner” account, by leveraging the “poc” account’s access, gains access to the “mgmt” account, even though it has no direct trust relationship with “mgmt”. The “partner” account effectively impersonates the “poc” account.

2.3 Prevent Confused Deputy Problem with STS External ID

To prevent this role-level exploitation, the “mgmt” account’s role trust policy is updated to include the sts:ExternalId condition:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::381491851702:root" // POC Account ID
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "secret-external-id-used-by-apps" // Shared secret
        }
      }
    }
  ]
}

The sts:ExternalId acts as a shared secret between the “poc” and “mgmt” accounts. The “partner” account is unaware of this value.

2.4 Access denied for “partner” to “mgmt” account

Now, even though the “partner” account can still assume the role in the “poc” account, it cannot use those credentials to assume the role in the “mgmt” account. The sts:AssumeRole call will fail with an AccessDenied error:

[ERROR] ClientError: An error occurred (AccessDenied) when calling the AssumeRole operation:
User: arn:aws:sts::381491851702:assumed-role/STS-Assume-S3-Read-Only-POC/AssumeRoleSession 
is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::401135408972:role/STS-Assume-S3-Read-Only-MGMT

This demonstrates how sts:ExternalId effectively mitigates the Confused Deputy problem at the role level. By requiring a shared secret, it ensures that only authorized entities (in this case, the “poc” account, which knows the External ID) can leverage the trust relationship. The “partner” account, even with temporary credentials from the “poc” account, is blocked from accessing the “mgmt” account.

2.5 Demo Video

Conclusion

The Confused Deputy problem poses a significant security challenge in cloud environments. However, AWS provides robust mechanisms to mitigate this risk. Default service-level restrictions prevent unauthorized cross-account access by services. Furthermore, the sts:ExternalId condition provides a powerful tool to protect against role-level exploitation, ensuring that only authorized entities can leverage trust relationships. By understanding these concepts and implementing appropriate security measures, organizations can build secure and resilient AWS architectures, protecting their resources from unauthorized access and the unintended consequences of the Confused Deputy problem. Using sts:ExternalId (or similar mechanisms where appropriate) is a best practice for cross-account role access and should be a standard part of any secure AWS deployment.

https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html

Share:

  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on X (Opens in new window) X
  • Click to share on WhatsApp (Opens in new window) WhatsApp
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • More
  • Click to email a link to a friend (Opens in new window) Email
  • Click to share on Tumblr (Opens in new window) Tumblr
  • Click to share on Pinterest (Opens in new window) Pinterest
  • Click to share on Telegram (Opens in new window) Telegram
  • Click to share on Pocket (Opens in new window) Pocket
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to print (Opens in new window) Print
Like Loading...

Related

CategoriesAWS
TagsAccess Control, Amazon S3, Assume Role, AWS, AWS Best Practices, AWS Consulting, AWS Consulting for a few seconds AWS, AWS Lambda, AWS Security, AWS Security Best Practices, AWS Training, Best Practices, Boto3, cloud, Cloud Computing, Cloud Security, Confused Deputy, Confused Deputy Problem, Cross-Account, Cross-account Access, Cross-Account Security, Cybersecurity, External ID, iam, Identity and Access Management, lambda, Mitigation, Multi-account Architecture, news, permissions, Policy, Role Assumption, Role-level Security, Roles, security, Security Best Practices, Security Configuration, Service-level Security, STS, technology, Trust Policies, Vulnerability
Previous
Understanding Push vs. Pull in AWS Event-Driven Architecture
January 26, 2025
Next
Hands-On: Implementing CloudWatch Log Insights Filter Index for Cost Optimization
February 25, 2025

Leave a comment Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Website Powered by WordPress.com.

Discover more from Why Surf Swim

Subscribe now to keep reading and get access to the full archive.

Continue reading

Privacy & Cookies: This site uses cookies. By continuing to use this website, you agree to their use.
To find out more, including how to control cookies, see here: Cookie Policy
  • Comment
  • Reblog
  • Subscribe Subscribed
    • Why Surf Swim
    • Join 312 other subscribers
    • Already have a WordPress.com account? Log in now.
    • Why Surf Swim
    • Subscribe Subscribed
    • Sign up
    • Log in
    • Copy shortlink
    • Report this content
    • View post in Reader
    • Manage subscriptions
    • Collapse this bar
 

Loading Comments...
 

    %d