HackBlue

Tools · Cloud security · 05 / 24

Find old and unused AWS access keys with the IAM credential report

Generate the credential report, find keys older than 90 days or never used, rotate keys without downtime, and replace long-lived keys with roles.

10 min read · practice only with authorization

Long-lived keys leak

IAM user access keys never expire on their own. They end up in laptops, CI systems, shell history, and code repositories, and automated scanners search public repositories for them constantly. A leaked key with broad permissions is one of the most common starting points for cloud incidents, often leading to crypto-mining on the victim’s bill.

The best key is one that does not exist: use roles with temporary credentials wherever possible.

The credential report

The credential report is a CSV listing every IAM user with password and key status: access_key_1_active, access_key_1_last_rotated, access_key_1_last_used_date, and the same for key 2, plus MFA status. Parse it to find keys older than 90 days, keys never used, and users with console passwords but no MFA.

aws iam get-access-key-last-used shows the last service and region for a single key.

Snippet
aws iam generate-credential-report >/dev/null; sleep 5
aws iam get-credential-report --query Content --output text | base64 -d > cred.csv
python3 - <<'EOF'
import csv, datetime as dt
now = dt.datetime.now(dt.timezone.utc)
for r in csv.DictReader(open('cred.csv')):
    for k in ('1', '2'):
        if r[f'access_key_{k}_active'] == 'true':
            age = (now - dt.datetime.fromisoformat(r[f'access_key_{k}_last_rotated'])).days
            used = r[f'access_key_{k}_last_used_date']
            if age > 90 or used == 'N/A':
                print(r['user'], f'key{k}', f'{age}d old', 'last used', used)
EOF

Lab: rotate without downtime

IAM allows two keys per user, which makes rotation safe. Create a second key, update the application to use it, confirm the old key’s last-used date stops advancing, deactivate the old key, wait a few days, and then delete it. Deactivating first lets you reactivate if something you forgot still depends on it.

Practice on a sandbox user with a small script that calls aws sts get-caller-identity.

Snippet
aws iam create-access-key --user-name report-bot
# update the app/secret store with the new key, then:
aws iam get-access-key-last-used --access-key-id AKIAOLDKEYEXAMPLE
aws iam update-access-key --user-name report-bot --access-key-id AKIAOLDKEYEXAMPLE --status Inactive
aws iam delete-access-key --user-name report-bot --access-key-id AKIAOLDKEYEXAMPLE

Replace keys with roles

EC2, Lambda, ECS, and EKS workloads should use roles that supply short-lived credentials automatically. CI systems such as GitHub Actions can use OpenID Connect federation to assume a role without storing any key. People should use IAM Identity Center for console and CLI access.

If a key leaks, deactivate it immediately, review CloudTrail for its activity, and check for new users, keys, or roles the attacker may have created.

Diagram

Zero-downtime key rotation

  1. Create

    Make a second key for the same user.

  2. Switch

    Update the application or secret store.

  3. Deactivate

    Mark the old key Inactive after its last-used date stops moving.

  4. Delete

    Remove it after a short grace period.

Check yourself

1. Why rotate by creating a second key first?

2. Which report shows each key’s age and last use?

3. How should GitHub Actions authenticate to AWS ideally?