AWS OIDC Provider Enumeration
Jun 21, 24Yesterday, Nick Frichette tweeted out a discovery:
In a desperate rush to meet Daniel Grzelak’s challenge to “publish something” in the week after fwd:cloudsec, I immediately sharked some follow on research from Nick.
Dogfooding known_aws_accounts
As part of the fwd:cloudsec “Technical Oversight Committee,” I help maintain known_aws_accounts. This repository contains known AWS account IDs (used by vendors and AWS services).
Nick’s announcement immediately raised the question: “Which known AWS account IDs have the Github Actions OIDC provider configured?”
This was a good chance to demonstrate the utility of known_aws_accounts, and to build a practical demonstration of Nick’s research.
Automating the search
I’m sure there were better ways to do this, but I immediately rolled with an approach that simply ran the Github Action aws-actions/configure-aws-credentials@v3
on a placeholder role name, across each Account ID in our data set.
Building the matrix
In Github Actions, “a matrix strategy lets you use variables in a single job definition to automatically create multiple job runs that are based on the combinations of the variables.”
This strategy would allow me to use a single workflow to test a list of account IDs.
To generate the matrix, I wrote the following basic python:
import yaml
import json
import requests
# URL of the YAML file on GitHub
url = "https://raw.githubusercontent.com/fwdcloudsec/known_aws_accounts/main/accounts.yaml"
# Fetch the content from the URL
response = requests.get(url)
# Check if the request was successful
if response.status_code == 200:
# Load the YAML content
data = yaml.safe_load(response.text)
else:
print(f"Failed to retrieve the file. HTTP Status code: {response.status_code}")
exit(1)
# Extract all accounts into a list
accounts_list = []
for entry in data:
accounts_list.extend(entry.get('accounts', []))
accounts_list = accounts_list[:255]
# Create a matrix with the accounts
matrix = {"include": [{"account": account} for account in accounts_list]}
# Print the matrix in JSON format
print(json.dumps(matrix))
Note that we truncate the list to the first 255 Account IDs. This is due to a limitation in Github that a matrix will generate a maximum of 256 jobs per workflow run. The result is I actually split this into multiple workflows to cover the whole list of 418 Account IDs. I’ll leave that as an exercise for the reader!
The workflow
The python script could then be called within a Github Actions workflow, which would check each account ID
on: [workflow_dispatch]
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
jobs:
generate-matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.generate-matrix.outputs.matrix }}
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pyyaml requests
- name: Generate matrix
id: generate-matrix
run: |
python generate-matrix-first256.py > matrix.json
echo "::set-output name=matrix::$(cat matrix.json)"
check:
needs: generate-matrix
runs-on: ubuntu-latest
strategy:
matrix: ${{fromJson(needs.generate-matrix.outputs.matrix)}}
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Configure AWS credentials
id: configure-aws
uses: aws-actions/configure-aws-credentials@v3
with:
role-to-assume: arn:aws:iam::${{ matrix.account }}:role/example-role
role-session-name: samplerolesession
aws-region: us-east-1
disable-retry: true
continue-on-error: true
The results
There are 418 accounts in the known_aws_accounts dataset.
Of those, the following 29 vendors had the Github Actions OIDC configured, across 34 accounts.
cloudsploit / 057012691312
New Relic / 061190967865
Rockset / 216690786812
Sysdig / 263844535661, 761931097553
Cloudinary / 232482882421
n0ps / 202279780353
Spacelift / 324880187172
unusd.cloud / 398997493752
rev.com / 414502572119
Upsolver / 428641199958
Lacework / 434813966438
JupiterOne / 612791702201
Axonius.com / 802876684602
Fivetran / 834469178297
Sumo Logic / 926226587429
CloudCraft / 968898580625
Starburst / 179619298502
Monte Carlo / 190812797848
Plerion / 173985203412,044642040396,588158338731,736689547456
Barracuda Networks / 151784055945
ProsperOps / 205499583182
sst.dev / 226609089145
Mackerel / 217452466226
RudderStack / 422074288268
Turbot / 453761072151
Contrast Security / 490991382221,763284681916
CrowdStrike / 517716713836
Claroty / 883241448326
Serverless / 802587217904
In addition, the following two accounts had an additional error: Could not assume role with OIDC: Incorrect token audience
Scale / 307185671274
Snyk / 198361731867
I can only assume this means they have either a misconfigured Github OIDC provider or are using a custom action. The token audience must be sts.amazonaws.com
with the official action.
Takeaways
So, what can we do with this information?
I’m not really sure. All we really learn is that these vendors currently or previously setup (hopefully, in that case, actually used) Github Actions.
I do wonder about the attack surface and isolation implications. On a quick check, there are vendors in this list that don’t use Github Actions with regards to the service for which they request trust in their Account ID. I wonder if it would be idea for these trusted third parties to isolate the trusted account from their other infrastructure, as a sort of “bastion” account, which would mean removing unnecessary attack surface.