terraform.lang.security.iam.no-iam-priv-esc-roles.no-iam-priv-esc-roles

profile photo of semgrepsemgrep
Author
unknown
Download Count*

Ensure that groups of actions that include iam:PassRole and could result in privilege escalation are not all allowed for the same user. These actions could result in an attacker gaining full admin access of an AWS account. Try not to use these actions in conjuction.

Run Locally

Run in CI

Defintion

rules:
  - id: no-iam-priv-esc-roles
    patterns:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  resource $TYPE "..." {
                    ...
                    policy = jsonencode({
                      ...
                      Statement = [
                        ...
                      ]
                      ...
                    })
                    ...
                  }
              - pattern-not-inside: |
                  resource $TYPE "..." {
                    ...
                    policy = jsonencode({
                      ...
                      Statement = [
                        ...,
                        {... Effect = "Deny" ...},
                        ...
                      ]
                      ...
                    })
                    ...
                  }
              - pattern: |
                  Action = $ACTION
              - metavariable-pattern:
                  metavariable: $TYPE
                  pattern-either:
                    - pattern: |
                        "aws_iam_role_policy"
                    - pattern: |
                        "aws_iam_policy"
                    - pattern: |
                        "aws_iam_user_policy"
                    - pattern: |
                        "aws_iam_group_policy"
          - patterns:
              - pattern-inside: |
                  data aws_iam_policy_document "..." {
                    ...
                    statement {
                      ...
                    }
                    ...
                  }
              - pattern-not-inside: |
                  data aws_iam_policy_document "..." {
                    ...
                    statement {
                      ...
                      effect = "Deny"
                      ...
                    }
                    ...
                  }
              - pattern: |
                  actions = $ACTION
      - metavariable-pattern:
          metavariable: $ACTION
          pattern-either:
            - patterns:
                - pattern: |
                    [..., "sts:AssumeRole", ...]
                - pattern: |
                    [..., "iam:UpdateAssumeRolePolicy", ...]
            - patterns:
                - pattern: |
                    [..., "iam:PassRole", ...]
                - pattern: |
                    [..., "lambda:CreateFunction", ...]
                - pattern: |
                    [..., "lambda:InvokeFunction", ...]
            - patterns:
                - pattern: |
                    [..., "iam:PassRole", ...]
                - pattern: |
                    [..., "lambda:CreateFunction", ...]
                - pattern: |
                    [..., "lambda:CreateEventSourceMapping", ...]
            - pattern: |
                "lambda:UpdateFunctionCode"
            - patterns:
                - pattern: |
                    [..., "iam:PassRole", ...]
                - pattern: |
                    [..., "glue:CreateDevEndpoint", ...]
            - patterns:
                - pattern: |
                    [..., "iam:PassRole", ...]
                - pattern: |
                    [..., "cloudformation:CreateStack", ...]
            - patterns:
                - pattern: |
                    [..., "iam:PassRole", ...]
                - pattern: |
                    [..., "datapipeline:CreatePipeline", ...]
                - pattern: |
                    [..., "datapipeline:PutPipelineDefinition", ...]
    message: Ensure that groups of actions that include iam:PassRole and could
      result in privilege escalation are not all allowed for the same user.
      These actions could result in an attacker gaining full admin access of an
      AWS account. Try not to use these actions in conjuction.
    metadata:
      references:
        - https://cloudsplaining.readthedocs.io/en/latest/glossary/privilege-escalation/
        - https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/
      category: security
      cwe:
        - "CWE-269: Improper Privilege Management"
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      technology:
        - terraform
        - aws
      owasp:
        - A04:2021 - Insecure Design
      subcategory:
        - audit
      likelihood: LOW
      impact: LOW
      confidence: LOW
      vulnerability_class:
        - Improper Authorization
    languages:
      - hcl
    severity: WARNING

Examples

no-iam-priv-esc-roles.tf

resource "aws_iam_user_policy" "lb_ro" {
  name = "test"
  user = aws_iam_user.lb.name

  # Terraform's "jsonencode" function converts a
  # Terraform expression result to valid JSON syntax.
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        # ok: no-iam-priv-esc-roles
        Action = [
          "std:AssumeRole",
        ]
        Effect   = "Allow"
        Resource = ["arn:aws:iam::ACCOUNT-ID-WITHOUT-HYPHENS:user/${aws.username}"]
      },
    ]
  })
}

resource "aws_iam_policy" "lb_ro" {
  name = "test"
  user = aws_iam_user.lb.name

  # Terraform's "jsonencode" function converts a
  # Terraform expression result to valid JSON syntax.
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        # ok: no-iam-priv-esc-roles
        Action = [
          "datapipeline:CreatePipeline",
          "datapipeline:PutPipelineDefinition"
        ]
        Effect   = "Allow"
        Resource = ["*"]
      },
    ]
  })
}

data aws_iam_policy_document "policy" {
   statement {
     # ok: no-iam-priv-esc-roles
     actions = ["glue:CreateDevEndpoint"]
     principals {
       type        = "AWS"
       identifiers = ["*"]
     }
     resources = ["*"]
   }
}

resource "aws_iam_user_policy" "policy" {
  name        = "test_policy"
  path        = "/"
  description = "My test policy"

  # Terraform's "jsonencode" function converts a
  # Terraform expression result to valid JSON syntax.
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        # ruleid: no-iam-priv-esc-roles
        Action = ["lambda:CreateFunction", "lambda:CreateEventSourceMapping", "iam:PassRole"]
        Effect   = "Allow"
        Resource = "*"
      },
    ]
  })
}

resource "aws_iam_user_policy" "policy" {
  name        = "test_policy"
  path        = "/"
  description = "My test policy"

  # Terraform's "jsonencode" function converts a
  # Terraform expression result to valid JSON syntax.
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        # ruleid: no-iam-priv-esc-roles
        Action = ["ec2:Describe", "sts:AssumeRole", "ec2:Test", "iam:UpdateAssumeRolePolicy"]
        Effect   = "Allow"
        Resource = "arn:aws:iam::account:user/*"
      },
    ]
  })
}

resource "aws_iam_user_policy" "policy" {
  name        = "test_policy"
  path        = "/"
  description = "My test policy"

  # Terraform's "jsonencode" function converts a
  # Terraform expression result to valid JSON syntax.
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        # ruleid: no-iam-priv-esc-roles
        Action = "lambda:UpdateFunctionCode"
        Effect   = "Allow"
        Resource = "arn:aws:iam::account:user/*"
      },
    ]
  })
}

resource "aws_iam_user_policy" "policy" {
  name        = "test_policy"
  path        = "/"
  description = "My test policy"

  # Terraform's "jsonencode" function converts a
  # Terraform expression result to valid JSON syntax.
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        # ruleid: no-iam-priv-esc-roles
        Action = ["iam:UpdateAssumeRolePolicy", "sts:AssumeRole"]
        Effect   = "Allow"
        Resource = "arn:aws:iam::account:user/*"
      },
    ]
  })
}

resource "aws_iam_policy" "policy" {
  name        = "test_policy"
  path        = "/"
  description = "My test policy"

  # Terraform's "jsonencode" function converts a
  # Terraform expression result to valid JSON syntax.
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        # ruleid: no-iam-priv-esc-roles
        Action = ["datapipeline:PutPipelineDefinition", "datapipeline:CreatePipeline", "iam:PassRole"]
        Effect   = "Allow"
        Resource = "arn:aws:iam::*:user/*"
      },
    ]
  })
}

resource "aws_iam_policy" "policy" {
  name        = "test_policy"
  path        = "/"
  description = "My test policy"

  # Terraform's "jsonencode" function converts a
  # Terraform expression result to valid JSON syntax.
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        # ok: no-iam-priv-esc-roles
        Action = "datapipeline:CreatePipeline"
        Effect   = "Allow"
        Resource = "arn:aws:iam::*:user/*"
      },
    ]
  })
}

data aws_iam_policy_document "policy" {
   statement {
     # ruleid: no-iam-priv-esc-roles
     actions = ["cloudformation:CreateStack", "iam:PassRole"]
     principals {
       type        = "AWS"
       identifiers = ["*"]
     }
     resources = ["*"]
   }
}