# Tackling Third-Party Risks in your AWS Environment

> Learn to identify 3rd party access to Amazon Machine Images (AMIs) and IAM cross-account trusts.

By Chris Farris
Published: 2023-04-12


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](https://docs.aws.amazon.com/marketplace/latest/userguide/product-and-ami-policies.html) 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.

```sql
WITH instances AS (
  SELECT
    instance_id,
    instance_type,
    account_id,
    tags ->> 'Name' AS instance_name,
    _ctx ->> 'connection_name' AS account_name,
    instance_state,
    region,
    image_id
  FROM
    aws_ec2_instance
)
SELECT DISTINCT
  aws_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_name
FROM
  instances
LEFT JOIN aws_ec2_ami_shared ON aws_ec2_ami_shared.image_id=instances.image_id
WHERE 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](https://github.com/turbot/steampipe-samples/blob/main/all/aws-trusts/foreign_ami_owners.sql) 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:

```sql
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](https://github.com/turbot/steampipe-samples/blob/main/all/aws-trusts/cross_account_trusts.sql) will pull a list of foreign AWS accounts that are trusted across your AWS organization:

```sql
WITH org_accounts AS (
  SELECT
    id
  FROM
    aws_payer.aws_organizations_account
),
roles AS (
  SELECT
    name,
    (regexp_match(principals, ':([0-9]+):')) [ 1 ] AS trusted_account,
    _ctx ->> 'connection_name' AS account_name
  FROM
    aws_iam_role AS role,
    jsonb_array_elements(role.assume_role_policy_std -> 'Statement') AS statement,
    jsonb_array_elements_text(statement -> 'Principal' -> 'AWS') AS principals
)
SELECT
  roles.name as role_name,
  roles.account_name,
  roles.trusted_account
FROM
  org_accounts
  RIGHT JOIN roles ON org_accounts.id = roles.trusted_account
WHERE
  org_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](https://github.com/duo-labs/cloudmapper) from Duo has a [yaml file](https://raw.githubusercontent.com/duo-labs/cloudmapper/main/vendor_accounts.yaml) with a number of known AWS accounts. With the config plugin you can query this resource. First install the Steampipe [Config plugin](https://hub.steampipe.io/plugins/turbot/config), then create a connection in the `config.spc` file that looks like this:


```hcl
connection "cloudmapper" {
  plugin = "config"
  yml_paths  = [ "github.com/duo-labs/cloudmapper//main/vendor_accounts.yaml" ]
}
```


[This query](https://github.com/turbot/steampipe-samples/blob/main/all/aws-trusts/parse_cloudmapper.sql) parses the file and returns the name and account Id:

```sql
WITH name_data AS (
  SELECT
    split_part(key_path::text, '.', 1) AS id,
    value AS name
  FROM
    cloudmapper.yml_key_value
  WHERE
    key_path::text LIKE '%.name%'
), account_data AS (
  SELECT
    split_part(key_path::text, '.', 1) AS id,
    value AS account
  FROM
    cloudmapper.yml_key_value
  WHERE
    key_path::text LIKE '%.accounts.%'
)
SELECT
  n.name,
  a.account
FROM
  name_data n
JOIN
  account_data a ON n.id = a.id
ORDER BY
  n.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](https://github.com/turbot/steampipe-samples/blob/main/all/aws-trusts/foreign_accounts_mapped_to_cloudmapper.sql).

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:

```sql
SELECT
  all_foreign_accounts.foreign_account_id,
  known_aws_accounts.name
FROM
  all_foreign_accounts
LEFT JOIN known_aws_accounts
ON 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](https://steampipe.io/downloads) and join the [Slack community](https://steampipe.io/community/join) to discuss your use cases.
