Skip to main content

Finding a mystery IAM access key

Recently I had an issue with some IAM keys. Somebody was trying to use an IAM access key to download some objects from S3, and it wasn’t working – it was giving a “permission denied” error. I wanted to inspect the permissions, but first I had to find where this IAM key came from. IAM keys are associated with IAM users, but which user had this key?

I found a couple of useful, new-to-me AWS APIs for doing this.

  1. You can find the account ID using the GetAccessKeyInfo API, for example:

    $ aws sts get-access-key-info --access-key-id AKIA3B6K4VLAVGRVTXJA
    {
      "Account": "760097843905"
    }
    

    This should work when you authenticate as any IAM entity that has the sts:GetAccessKeyInfo permission, even if it’s in a different account to the key.

    This is useful because the AWS estate at work is split over a dozen accounts, and some of the accounts have overlapping use cases. Even if you know roughly what a key is used for, it may not be obvious which account it’s defined in.

  2. Once you know the account, you can find the username with the GetAccessKeyLastUsed API. You’ll need to authenticate as an IAM entity with the iam:GetAccessKeyLastUsed permission in that particular account:

    For example:

    $ aws iam get-access-key-last-used --access-key-id "AKIA3B6K4VLAVGRVTXJA"
    {
        "UserName": "example-user-2023-08-26",
        "AccessKeyLastUsed": {
            "LastUsedDate": "2023-08-24T15:58:00Z",
            "ServiceName": "s3",
            "Region": "eu-west-1"
        }
    }
    

    Note that this works even if the access key has never actually been used, for example:

    $ aws iam get-access-key-last-used --access-key-id "AKIA3B6K4VLAVGRVTXJA"
    {
        "UserName": "example-user-2023-08-26",
        "AccessKeyLastUsed": {
            "ServiceName": "N/A",
            "Region": "N/A"
        }
    }
    

I took these APIs and wrapped them in a Python script that takes an access key as input, and prints a bunch of information about the key and the associated user. This is what it looks like:

$ python3 describe_iam_access_key.py AKIA3B6K4VLAVGRVTXJA
access key:       AKIA3B6K4VLAVGRVTXJA
account:          platform (760097843905)
username:         example-user-2023-08-26
key created:      26 August 2023
status:           Active

IAM permissions:  example-user-2023-08-26.iam_permissions.txt

console:          https://us-east-1.console.aws.amazon.com/iamv2/home#/users/details/example-user-2023-08-26
terraform:        https://github.com/wellcomecollection/platform-infrastructure/tree/main/terraform/users

This script won’t work for everyone – in particular, going from an AWS account ID to an authenticated IAM session is probably going to look different for every organisation, but a lot of the bigger pieces are reusable.

Because the IAM permissions can be quite long and verbose, it saves them to a separate text file. It also includes links to the IAM console and the Terraform configuration (and it can find the latter because we tag the user with that link).

This script only works with long-term credentials created for an IAM user. It doesn’t work for temporary credentials using AWS STS – if you want to find out who owns the latter, you have to review your CloudTrail logs – but for my purposes, that’s not an issue.

When writing this script, one of the things I was pleasantly surprised by was the presence of AWS APIs that feel tailor-made for this use case. I was expecting I’d have to loop through every account, every user, every access key, and look for one that matched, which could have been pretty slow. Using these APIs was much simpler and quicker!