Summary
Runner EC2 instances can read other instances' tokens (jitconfig and registration tokens) from SSM parameters.
Details
The runner-ssm-parameters
IAM policy (https://github.com/philips-labs/terraform-aws-github-runner/blob/v5.10.4/modules/runners/policies/instance-ssm-parameters-policy.json#L9-L11) allows ssm:GetParameter
on "Resource": "${arn_ssm_parameters_path_tokens}*"
. This is overly permissive and allows instances to read each others' jitconfig or registration tokens.
However, to exploit the overly permissive ssm:GetParameter
permissions, we need to discover the instance ID of another instance because it occurs in the parameter name. One of the first things a runner does is delete its config after retrieving it. This is a slightly tricky race to win.
There are multiple places the instance ID is exposed, but ec2:DescribeVolumes
is nice to use for an exploit because it appears before the victim instance has begun execution and the SSM should not yet be deleted.
Impact
The jitconfig or registration token can be stolen from concurrent workflows on the same runner, which can be used to register a new runner maliciously. The token can be exfiltrated and a runner created outside the AWS environment, or the malicious runner can be created inside the existing instance/runner. A runner's jitconfig is normally scoped down to a single repo, workflow and job. Stealing another runner's jitconfig allows lateral movement throughout the GitHub organization or repository and exposes the other runner's GITHUB_TOKEN, GitHub OIDC JWT ID token and any secrets.
The risk is that an attacker in with access to your org runners can steal a token and register a runner in the same org. When using a jitconfig this runner will run one job at most, for non JIT infinite which is a significant higher risk.
Summary
Runner EC2 instances can read other instances' tokens (jitconfig and registration tokens) from SSM parameters.
Details
The
runner-ssm-parameters
IAM policy (https://github.com/philips-labs/terraform-aws-github-runner/blob/v5.10.4/modules/runners/policies/instance-ssm-parameters-policy.json#L9-L11) allowsssm:GetParameter
on"Resource": "${arn_ssm_parameters_path_tokens}*"
. This is overly permissive and allows instances to read each others' jitconfig or registration tokens.However, to exploit the overly permissive
ssm:GetParameter
permissions, we need to discover the instance ID of another instance because it occurs in the parameter name. One of the first things a runner does is delete its config after retrieving it. This is a slightly tricky race to win.There are multiple places the instance ID is exposed, but
ec2:DescribeVolumes
is nice to use for an exploit because it appears before the victim instance has begun execution and the SSM should not yet be deleted.Impact
The jitconfig or registration token can be stolen from concurrent workflows on the same runner, which can be used to register a new runner maliciously. The token can be exfiltrated and a runner created outside the AWS environment, or the malicious runner can be created inside the existing instance/runner. A runner's jitconfig is normally scoped down to a single repo, workflow and job. Stealing another runner's jitconfig allows lateral movement throughout the GitHub organization or repository and exposes the other runner's GITHUB_TOKEN, GitHub OIDC JWT ID token and any secrets.
The risk is that an attacker in with access to your org runners can steal a token and register a runner in the same org. When using a jitconfig this runner will run one job at most, for non JIT infinite which is a significant higher risk.