SSRF via IMDSv1 in AWS EC2

Critical Risk infrastructure-security
awsterraformec2imdsssrfprivilege-escalationmetadata-service

What it is

EC2 instances allowing access to the Instance Metadata Service version 1 (IMDSv1) without requiring session tokens enable Server-Side Request Forgery (SSRF) attacks where malicious applications can access instance metadata, IAM role credentials, and sensitive configuration data through simple HTTP requests to 169.254.169.254, leading to privilege escalation and lateral movement.

# VULNERABLE: EC2 instances with IMDSv1 enabled

resource "aws_instance" "vulnerable_web_server" {
  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 SSRF attacks to access metadata
  
  iam_instance_profile = aws_iam_instance_profile.web_profile.name
  
  tags = {
    Name = "vulnerable-web-server"
  }
}

# VULNERABLE: Explicitly allowing IMDSv1
resource "aws_instance" "legacy_app" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.large"
  subnet_id     = aws_subnet.private.id
  
  metadata_options {
    http_endpoint = "enabled"
    http_tokens   = "optional"  # VULNERABLE: Allows IMDSv1
  }
  
  iam_instance_profile = aws_iam_instance_profile.app_profile.name
}

# VULNERABLE: Launch template with IMDSv1
resource "aws_launch_template" "vulnerable_template" {
  name_prefix   = "app-"
  image_id      = data.aws_ami.amazon_linux.id
  instance_type = "t3.medium"
  
  metadata_options {
    http_tokens = "optional"  # VULNERABLE
  }
  
  iam_instance_profile {
    name = aws_iam_instance_profile.asg_profile.name
  }
}
# SECURE: EC2 instances with IMDSv2 enforced

resource "aws_instance" "secure_web_server" {
  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"   # SECURE: Enable tags
  }
  
  iam_instance_profile = aws_iam_instance_profile.web_profile.name
  
  tags = {
    Name        = "secure-web-server"
    IMDSVersion = "v2-only"
  }
}

# SECURE: Launch template with IMDSv2 enforced
resource "aws_launch_template" "secure_template" {
  name_prefix   = "secure-app-"
  image_id      = data.aws_ami.amazon_linux.id
  instance_type = "t3.medium"
  
  # SECURE: Strict metadata options
  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.asg_profile.name
  }
  
  tag_specifications {
    resource_type = "instance"
    tags = {
      Name        = "secure-asg-instance"
      IMDSVersion = "v2-enforced"
    }
  }
}

💡 Why This Fix Works

The vulnerable configuration omits metadata_options or sets http_tokens to 'optional', allowing IMDSv1 access where simple HTTP GET requests can retrieve IAM credentials and metadata. The secure version enforces IMDSv2 by setting http_tokens to 'required', which mandates session tokens, and sets http_put_response_hop_limit to 1 to prevent SSRF through containerized applications.

Why it happens

EC2 instances launched without explicitly configuring metadata_options block in Terraform or MetadataOptions in CloudFormation. AWS defaults to allowing both IMDSv1 and IMDSv2 (http_tokens = 'optional'), enabling vulnerable IMDSv1 access where applications can retrieve IAM role credentials, instance metadata, and user data through simple HTTP GET requests to http://169.254.169.254 without any authentication or session tokens, making SSRF exploitation trivial.

Root causes

EC2 Instances with Default Metadata Service Settings

EC2 instances launched without explicitly configuring metadata_options block in Terraform or MetadataOptions in CloudFormation. AWS defaults to allowing both IMDSv1 and IMDSv2 (http_tokens = 'optional'), enabling vulnerable IMDSv1 access where applications can retrieve IAM role credentials, instance metadata, and user data through simple HTTP GET requests to http://169.254.169.254 without any authentication or session tokens, making SSRF exploitation trivial.

Missing metadata_options Configuration

Terraform aws_instance and aws_launch_template resources deployed without the metadata_options block specified. When this configuration is omitted, instances default to IMDSv1 enabled (http_tokens = 'optional'), allowing Server-Side Request Forgery attacks to access the metadata service at 169.254.169.254 through vulnerabilities in web applications, proxies, or any code that processes user-supplied URLs without proper validation.

http_tokens Set to 'optional' Allowing IMDSv1

Explicit metadata_options configuration with http_tokens = 'optional' in Terraform or HttpTokens: optional in CloudFormation. This setting permits both IMDSv1 (sessionless) and IMDSv2 (session-based) access methods. Attackers exploiting SSRF vulnerabilities can use the simpler IMDSv1 protocol (single GET request) to steal IAM temporary credentials, bypassing the IMDSv2 session token requirement and gaining unauthorized access to AWS API operations with the instance's IAM role permissions.

Launch Templates Without IMDSv2 Enforcement

AWS Launch Templates used by Auto Scaling Groups, Spot Fleets, and EC2 Fleet configurations lack metadata_options settings enforcing IMDSv2. When launch templates don't explicitly require IMDSv2, all instances launched from these templates (potentially hundreds or thousands across auto-scaling) inherit IMDSv1 vulnerability, multiplying the attack surface and making centralized remediation difficult without launch template updates and instance refreshes.

Legacy Application IMDSv1 Compatibility Requirements

Organizations maintaining http_tokens = 'optional' to support legacy applications that haven't been updated to use IMDSv2's token-based authentication flow. Legacy AWS SDKs (pre-2019), custom metadata retrieval scripts, and third-party tools may not support the PUT-then-GET token workflow required by IMDSv2, leading teams to keep IMDSv1 enabled despite security risks, creating ongoing SSRF exposure for compliance with outdated application dependencies.

Fixes

1

Enforce IMDSv2 by Setting http_tokens to Required

Configure metadata_options block in Terraform aws_instance and aws_launch_template resources with http_tokens = "required". In CloudFormation, set MetadataOptions.HttpTokens = required. This configuration disables IMDSv1 entirely and mandates that all metadata requests use IMDSv2's session-oriented authentication where applications must first obtain a session token via PUT request to /latest/api/token, then include that token in X-aws-ec2-metadata-token header for subsequent metadata requests. SSRF attacks fail because attackers typically cannot control custom headers through vulnerable applications.

2

Set http_put_response_hop_limit to 1

Configure http_put_response_hop_limit = 1 in metadata_options (Terraform) or MetadataOptions.HttpPutResponseHopLimit = 1 (CloudFormation) to restrict metadata service access to the instance itself. This prevents containerized applications from forwarding IMDS requests through additional network hops, blocking scenarios where containers running in bridge networking mode or behind proxies could relay metadata access from compromised container workloads, adding defense-in-depth against SSRF in containerized environments like Docker or Kubernetes pods on EC2.

3

Enable instance_metadata_tags for Enhanced Tracking

Set instance_metadata_tags = "enabled" in metadata_options (Terraform) or MetadataOptions.InstanceMetadataTags = enabled (CloudFormation) to make instance tags accessible through the metadata service. This enables applications to retrieve configuration and identification information from tags instead of relying on external API calls or configuration files, improving security monitoring by allowing instance-level tracking of metadata access patterns and supporting tag-based configuration management without requiring additional IAM permissions for DescribeTags API calls.

4

Apply IMDSv2 Enforcement to All Launch Templates

Audit and update all AWS Launch Templates used by Auto Scaling Groups, Spot Fleets, and EC2 Fleet configurations to include strict metadata_options with http_tokens = "required", http_put_response_hop_limit = 1, and instance_metadata_tags = "enabled". After updating launch templates, initiate instance refresh operations on Auto Scaling Groups to replace running instances with new instances using IMDSv2-enforced configuration. Use AWS Config rules or custom scripts to continuously monitor that all launch templates enforce IMDSv2 and prevent creation of non-compliant templates.

5

Audit Existing Instances for IMDSv1 Usage

Use AWS CLI commands like 'aws ec2 describe-instances --query "Reservations[*].Instances[*].[InstanceId,MetadataOptions.HttpTokens]"' to identify instances with http_tokens set to 'optional' or unset. Implement AWS Config rules to continuously detect IMDSv1-enabled instances. Use CloudWatch metrics and VPC Flow Logs to monitor for metadata service access patterns (connections to 169.254.169.254). Create remediation runbooks using Systems Manager Automation to update running instances with 'aws ec2 modify-instance-metadata-options --instance-id <id> --http-tokens required' after verifying application compatibility.

6

Update Application Code to Use IMDSv2 Token Flow

Modify application code and scripts accessing instance metadata to use IMDSv2's two-step token workflow: (1) Obtain session token via 'curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"' and (2) Use token in subsequent requests via 'curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/'. Update AWS SDK versions to 2019 or later which support IMDSv2 automatically. Test applications in non-production environments with IMDSv2-only instances before enforcing in production to identify compatibility issues.

Detect This Vulnerability in Your Code

Sourcery automatically identifies ssrf via imdsv1 in aws ec2 and many other security issues in your codebase.