Server-side request forgery (SSRF) via IMDSv1 metadata endpoint in AWS EC2

Critical Risk infrastructure-security
awsec2imdsmetadatassrfprivilege-escalationiam-credentialsterraform

What it is

AWS EC2 instances using Instance Metadata Service version 1 (IMDSv1) are vulnerable to Server-Side Request Forgery (SSRF) attacks. IMDSv1 allows unauthenticated HTTP GET requests to retrieve sensitive instance metadata including IAM role credentials, enabling privilege escalation and lateral movement within AWS environments.

# VULNERABLE: EC2 instance with IMDSv1 enabled

resource "aws_instance" "vulnerable_web" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t3.medium"
  subnet_id     = aws_subnet.public.id
  
  # VULNERABLE: No metadata_options - defaults to IMDSv1
  # Allows unauthenticated HTTP GET to 169.254.169.254
  
  iam_instance_profile = aws_iam_instance_profile.app.name
  
  tags = {
    Name = "vulnerable-server"
  }
}

# VULNERABLE: Launch template without metadata protection
resource "aws_launch_template" "vulnerable" {
  name_prefix   = "app-"
  image_id      = data.aws_ami.amazon_linux.id
  instance_type = "t3.micro"
  
  # VULNERABLE: No metadata_options configured
  
  iam_instance_profile {
    name = aws_iam_instance_profile.app.name
  }
}

# VULNERABLE: EKS node group without IMDSv2
resource "aws_eks_node_group" "vulnerable" {
  cluster_name    = aws_eks_cluster.main.name
  node_group_name = "workers"
  node_role_arn   = aws_iam_role.node.arn
  subnet_ids      = aws_subnet.private[*].id
  
  scaling_config {
    desired_size = 2
    max_size     = 4
    min_size     = 1
  }
  
  # VULNERABLE: No launch_template with metadata restrictions
}
# SECURE: EC2 instance with IMDSv2 enforced

resource "aws_instance" "secure_web" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t3.medium"
  subnet_id     = aws_subnet.private.id
  
  # SECURE: Enforce IMDSv2 with session tokens
  metadata_options {
    http_endpoint               = "enabled"
    http_tokens                 = "required"  # SECURE: IMDSv2 only
    http_put_response_hop_limit = 1           # SECURE: Prevent forwarding
    instance_metadata_tags      = "enabled"
  }
  
  iam_instance_profile = aws_iam_instance_profile.app.name
  
  tags = {
    Name        = "secure-server"
    IMDSVersion = "v2-enforced"
  }
}

# SECURE: Launch template with metadata protection
resource "aws_launch_template" "secure" {
  name_prefix   = "secure-app-"
  image_id      = data.aws_ami.amazon_linux.id
  instance_type = "t3.micro"
  
  # SECURE: Enforce IMDSv2
  metadata_options {
    http_endpoint               = "enabled"
    http_tokens                 = "required"  # SECURE: IMDSv2 only
    http_put_response_hop_limit = 1
    instance_metadata_tags      = "enabled"
  }
  
  iam_instance_profile {
    name = aws_iam_instance_profile.app.name
  }
}

# SECURE: EKS node launch template (hop limit = 2 for K8s)
resource "aws_launch_template" "eks_nodes" {
  name_prefix = "eks-nodes-"
  
  metadata_options {
    http_endpoint               = "enabled"
    http_tokens                 = "required"  # SECURE: IMDSv2 only
    http_put_response_hop_limit = 2           # SECURE: EKS needs hop=2
    instance_metadata_tags      = "enabled"
  }
}

💡 Why This Fix Works

The vulnerable configuration omits metadata_options or leaves defaults, allowing IMDSv1 where simple HTTP GET requests can retrieve IAM credentials and metadata. The secure version enforces IMDSv2 by requiring session tokens (http_tokens='required'). For regular EC2 instances, http_put_response_hop_limit is set to 1 to prevent container-to-host metadata access. For EKS nodes, the hop limit must be 2 to allow Kubernetes pods to access instance metadata.

Why it happens

AWS EC2 instances are launched with IMDSv1 enabled by default for backwards compatibility. Unless explicitly configured otherwise, all new instances allow unauthenticated HTTP GET requests to the metadata service at 169.254.169.254.

Root causes

Default IMDSv1 Configuration

AWS EC2 instances are launched with IMDSv1 enabled by default for backwards compatibility. Unless explicitly configured otherwise, all new instances allow unauthenticated HTTP GET requests to the metadata service at 169.254.169.254.

Missing Terraform Metadata Configuration

Terraform aws_instance and aws_launch_template resources lack metadata_options configuration blocks. Without explicit configuration, instances default to IMDSv1, leaving them vulnerable to SSRF attacks that can steal IAM credentials.

Optional HTTP Tokens Setting

The http_tokens parameter is set to 'optional' instead of 'required', allowing applications to use either IMDSv1 or IMDSv2. This permissive setting enables SSRF vulnerabilities to exploit the unauthenticated IMDSv1 endpoint.

Application SSRF Vulnerabilities

Web applications with Server-Side Request Forgery vulnerabilities can be exploited to make requests to the IMDSv1 endpoint. Attackers can retrieve IAM role credentials, user data, and other sensitive metadata through these SSRF flaws.

Container-to-Host Metadata Access

Containers running on EC2 instances can access the host's metadata service if hop limits aren't properly restricted. This allows compromised containers to retrieve host IAM credentials and escalate privileges beyond the container boundary.

Fixes

1

Enforce IMDSv2 with Required Tokens

Set http_tokens = 'required' in the metadata_options block for all EC2 instances and launch templates. This enforces IMDSv2, which requires session tokens obtained via PUT request, preventing simple SSRF attacks from accessing metadata.

2

Configure Appropriate Hop Limits

Set http_put_response_hop_limit to 1 for standard EC2 instances to prevent container escape scenarios. For EKS worker nodes, use a hop limit of 2 to allow Kubernetes pods legitimate access while still restricting forwarding.

3

Enable Instance Metadata Tags

Set instance_metadata_tags = 'enabled' to make instance tags accessible via the metadata service. This improves tracking and identification of instances while maintaining IMDSv2 security protections.

4

Disable Metadata Service When Unnecessary

If your application doesn't need access to instance metadata or IAM credentials, disable the metadata service entirely by setting http_endpoint = 'disabled'. This eliminates the attack surface completely for instances that don't require metadata access.

5

Implement SSRF Application Defenses

Add application-level protections against SSRF attacks including URL allowlisting, blocking access to private IP ranges (especially 169.254.169.254), and validating all user-controlled URLs before making outbound requests.

Detect This Vulnerability in Your Code

Sourcery automatically identifies server-side request forgery (ssrf) via imdsv1 metadata endpoint in aws ec2 and many other security issues in your codebase.