With supply chain attacks on the rise, it's crucial to know which third-party accounts you trust. In this post we'll show how to identify, at scale, two common forms of third-party trust: IAM Roles that can be assumed by other AWS customers, and disk images that were created by other AWS customers that are running in your environment. This will allow you to audit the third parties that you trust, so you can ensure they've been vetted and meet your organization’s security and compliance objectives.
Ensuring AMIs are from trusted sources
Any AWS customer can publish an Amazon Machine Image (AMI) for other AWS customers to launch instances from. AWS only vets a handful of images in the AWS Marketplace, there is no guarantee that other publicly shared AMIs are free of vulnerabilities or malicious code. While it's common for vendors to share their software as an AMI, it's also possible someone in your organization has launched an instance from a compromised image.
All AMIs are owned by an AWS account. With Steampipe, you can easily pull a report of the AWS accounts that own the AMIs they use to validate they come from trusted sources.
WITH instances AS (SELECTinstance_id,instance_type,account_id,tags ->> 'Name' AS instance_name,_ctx ->> 'connection_name' AS account_name,instance_state,region,image_idFROMaws_ec2_instance)SELECT DISTINCTaws_ec2_ami_shared.image_id as image_id,aws_ec2_ami_shared.owner_id as image_owner_id,aws_ec2_ami_shared.image_owner_alias as image_owner_name,instances.instance_name,instances.account_name,instances.region,aws_ec2_ami_shared.name as image_nameFROMinstancesLEFT JOIN aws_ec2_ami_shared ON aws_ec2_ami_shared.image_id=instances.image_idWHERE aws_ec2_ami_shared.image_owner_alias != 'amazon'AND aws_ec2_ami_shared.image_owner_alias != 'self'+-----------------------+----------------+------------------+---------------+--------------------+-----------+------------| image_id | image_owner_id | image_owner_name | instance_name | account_name | region | image_name+-----------------------+----------------+------------------+---------------+--------------------+-----------+------------| ami-08ab860352f8b42d5 | 679533241933 | aws-marketplace | <null> | aws_fooli_security | us-west-2 | splunk_AMI_| ami-0d52da3dadefbb017 | 186678614485 | 186678614485 | SIFT | aws_fooli_security | us-east-1 | sift-2022+-----------------------+----------------+------------------+---------------+--------------------+-----------+-------------
This query excludes all the AMIs that are published via AWS's official sources, but it doesn't exclude AWS Marketplace images. To exclude marketplace images, add the following to the WHERE clause:
AND aws_ec2_ami_shared.image_owner_alias != 'aws-marketplace'
List all the AWS accounts you trust
Another key third-party trust relationship is via IAM Role cross-account trusts. With AWS IAM, you can grant other AWS customers access to perform actions in your account. Obviously, this has third-party risk-management implications, and all third-party access should be reviewed.
Within an AWS organization, there are often a lot of cross-account trusts. Various accounts are created to audit configuration, write logs, perform auto-remediation tasks, or deploy resources via a centralized pipeline. These are not third-party risks and should be excluded from any list of foreign AWS accounts in your organization.
The following query will pull a list of foreign AWS accounts that are trusted across your AWS organization:
WITH org_accounts AS (SELECTidFROMaws_payer.aws_organizations_account),roles AS (SELECTname,(regexp_match(principals, ':([0-9]+):')) [ 1 ] AS trusted_account,_ctx ->> 'connection_name' AS account_nameFROMaws_iam_role AS role,jsonb_array_elements(role.assume_role_policy_std -> 'Statement') AS statement,jsonb_array_elements_text(statement -> 'Principal' -> 'AWS') AS principals)SELECTroles.name as role_name,roles.account_name,roles.trusted_accountFROMorg_accountsRIGHT JOIN roles ON org_accounts.id = roles.trusted_accountWHEREorg_accounts.id IS NULL+---------------------+--------------------------------+-----------------+| role_name | account_name | trusted_account |+---------------------+--------------------------------+-----------------+| TurbotGuardDutyRole | aws_fooli_sandbox | 495636112135 || steampipe-cloud | aws_fooli_sandbox | 316881668097 || steampipe-cloud | aws_fooli_memefactory | 316881668097 || steampipe-cloud | aws_fooli_payer | 316881668097 || steampipe-cloud | aws_fooli_prod | 316881668097 || steampipe-cloud | aws_fooli_dev | 316881668097 || steampipe-cloud | aws_fooli_security | 316881668097 |+---------------------+--------------------------------+-----------------+
Identifying foreign AWS Accounts
The above two queries provide you a list of the 12-digit account IDs, but that doesn’t tell you who they are. AWS doesn’t publish a list of known account IDs, but the cloud security community does. The CloudMapper project from Duo has a yaml file with a number of known AWS accounts. With the config plugin you can query this resource. First install the Steampipe Config plugin, then create a connection in the config.spc
file that looks like this:
connection "cloudmapper" {plugin = "config"yml_paths = [ "github.com/duo-labs/cloudmapper//main/vendor_accounts.yaml" ]}
This query parses the file and returns the name and account Id:
WITH name_data AS (SELECTsplit_part(key_path::text, '.', 1) AS id,value AS nameFROMcloudmapper.yml_key_valueWHEREkey_path::text LIKE '%.name%'), account_data AS (SELECTsplit_part(key_path::text, '.', 1) AS id,value AS accountFROMcloudmapper.yml_key_valueWHEREkey_path::text LIKE '%.accounts.%')SELECTn.name,a.accountFROMname_data nJOINaccount_data a ON n.id = a.idORDER BYn.name, a.account+-----------------------------------------------------+--------------+| name | account |+-----------------------------------------------------+--------------+| Summit Route | 393727464233 || Sumo Logic | 926226587429 || Threat Stack | 896126563706 || TrendMicro | 147995105371 || Turbot | 255798382450 || Turbot | 287590803701 || Upsolver | 428641199958 || VManage | 200235630647 || cloudbreak | 755047402263 || cloudsploit | 057012691312 |...+-----------------------------------------------------+--------------+
Getting an actionable list of trusted third-party accounts
To put all of this together, here is an 82-line SQL query which you can find in our samples repo.
We combine the first two queries (via UNION) into a list of all_foreign_accounts. We join that with the data from CloudMapper, and the final query and results looks something like:
SELECTall_foreign_accounts.foreign_account_id,known_aws_accounts.nameFROMall_foreign_accountsLEFT JOIN known_aws_accountsON all_foreign_accounts.foreign_account_id = known_aws_accounts.account+--------------------+-----------+| foreign_account_id | name |+--------------------+-----------+| 316881668097 | Steampipe || 679533241933 | <null> || 525188041748 | <null> || 495136121356 | <null> || 186678614485 | <null> |+--------------------+-----------+
What results is a final list of foreign AWS accounts trusted by your organization. You and your third-party risk management team will need to track down the owner of the AWS account and determine if they have been properly vetted.
Key Takeaway
To enhance the protection of your AWS infrastructure from supply chain attacks, it is crucial to actively manage third-party risks. We have shown how Steampipe can help by validating AMIs, and cross-account trusts for IAM roles. To get started in your environment, download Steampipe and join the Slack community to discuss your use cases.