cloudgoat/cloudgoat/scenarios/aws/ecs_takeover at master · RhinoSecurityLabs/cloudgoat
CloudGoat is Rhino Security Labs' "Vulnerable by Design" AWS deployment tool - RhinoSecurityLabs/cloudgoat
github.com
이번 글에서는 CloudGoat의 ECS_TakeOver 시나리오를 실습한 내용을 다루도록 하겠다. 화이트 햇 스쿨과 학교 수업, 프로젝트 고도화를 동시에 진행하면서 풀었더니 하나 푸는데 너무 시간이 오래 걸린 것 같다.. 근데 처음 아이디어를 떠올려서 공격에 성공했을 때 너무 뿌듯하고 신이 났다ㅎㅎ 하지만 문제를 다 풀고 다른 분들 블로그의 풀이를 보니 내가 참 삽질을 많이 했구나.. 싶을 정도로 깔끔하게 정리가 되어있었다.. 그래도 처음 푼 문제 치고는 한 달 이상이 안 걸렸다는 것에 스스로 안심하며 실습 내용과 기본적으로 필요했던 지식을 설명하겠다.
클라우드 보안
클라우드 보안이 왜 필요한 것일까? 클라우드 서비스를 사용하는 과정에서 데이터와 네트워크에 대한 보안과 권한에 대한 보안이 필요하다. 쉽게 VPC(네트워크) 보안에 대한 예시를 들어보겠다.
위와 같이 구성된 VPC가 존재한다고 생각해보자. 왼쪽의 VPC는 프라이빗 서브넷의 리소스가 외부와 접근을 하기 위해서는 퍼블릭 서브넷에 위치한 NAT 게이트웨이를 통해 간접적으로 진행된다. 그와 반대로 오른쪽의 VPC는 프라이빗 서브넷의 리소스가 인터넷 게이트웨이로 바로 외부와 통신이 되고 있다. 만약 프라이빗 서브넷에 존재하는 리소스가 외부로 유출되면 안 되는 리소스라면 어떨까?? 외부의 공격이 조금 더 쉬워지고 보안적으로 취약한 상태가 된 것이다. 때문에 왼쪽과 같은 VPC가 보안적으로 안전한 구조라고 생각하면 된다. VPC 환경을 구축할 때 오른쪽과 같은 인프라를 구축하는 실수를 막기 위해 클라우드 보안이라는 개념이 중요하고 신경써야 하는 내용인 것 같다. 예시로 설명한 내용 외에도 클라우드를 사용하는 과정에서 신경 써야 하는 보안 요소들은 더 다양하지만, 일단 이 글에서 진행하는 실습은 이러한 설계의 결함으로 인해 생기는 문제이기 때문에 여기까지만 설명하겠다.
AWS의 컨테이너 서비스
AWS의 컨테이너 서비스는 이미지를 저장하는 ECR, 컨테이너를 관리하는 ECS와 EKS, 컨테이너를 실행시키는 방법인 EC2, Fargate가 존재한다. 이 중에서 ECS TakeOver에서 사용되는 서비스는 ECS이기 때문에 ECS에 대해 설명해 보겠다.
Amazon ECS
이 전의 클라우드 프로젝트를 진행하는 글에서도 한 번 설명했지만, 다시 말하자면 ECS는 아마존에서 제공하는 완전 관리형 컨테이너 오케스트레이션 툴이다. 그냥 도커 컨테이너를 aws에서 쉽게 실행하고 운영하며 관리할 수 있도록 도와주는 서비스라고 보면 될 것 같다.
구조는 위와 같은데, 태스크 정의에서 실행될 컨테이너에 대하여 정의를 하게 되면 컨테이너의 묶음 즉 태스크가 정의되고, 이러한 태스크 정의를 서비스를 통해 또는 독립적으로 실행이 된다. 또한 이를 묶어두고 관리하는 그룹이 클러스터이다. (말이 조금 복잡하고 어려운데 그냥 위의 사진을 그대로 이해하면 될 것 같다..)
이러한 ECS를 통해 컨테이너를 실행시키는 방법은 2가지가 있다.
AWS Fargate
첫 번째로는 Fargate이다. Fargate는 서버 인프라를 AWS에서 자동으로 관리해 주는 서버리스 컴퓨팅 엔진으로, 서버에 대한 고민과 관리 없이 컨테이너를 띄울 수 있는 서비스다.
Amazon EC2
EC2는 Fargate와 반대로 인스턴스라는 가상 이미지를 통해 서버를 직접 관리하고 해당 서버에 컨테이너를 띄우게 된다.
AWS IAM
AWS IAM은 ECS 등의 서비스와 이러한 서비스를 생성하고 관리하는 사용자에 대한 권한을 부여해 주고 관리하는 것이 IAM이라는 서비스다. 결국 서비스와 사용자에게 권한을 부여해 주는 것도 서비스이기 때문에 이 서비스를 잘 관리해야 보안적으로 취약점이 안 생기겠죠?!
ECS TakeOver 시나리오 실습
이제 시나리오에서 사용되는 서비스에 대한 이해를 끝냈으니 실습 진행 내용을 설명해 보겠다. 일단 Github에서 확인한 시나리오의 내용과 목표는 다음과 같다.
- VPC와 서브넷이 생성되며 EC2 인스턴스 2개, ECS 클러스터 1개, ECS 서비스 3개, 인터넷 게이트웨이 1개가 생성된다.
- 명령어를 통해 환경을 구축하면 EC2 인스턴스의 IP가 제공되며, 해당 웹 사이트에서 실습이 진행된다.
- 목표는 vault라는 컨테이너에 접근하여 FLAG를 획득하는 것이다.
이러한 정보를 얻은 상태에서 환경 구축을 진행했다.
위와 같이 vuln site라는 IP 주소가 제공되었고,
접속해 보니 위와 같이 URL을 입력할 수 있는 from이 나왔고,
URL을 입력하니 HTML 응답이 반환되었다. 이를 통해 CURL 등과 같은 메시지가 반환된다는 것을 예측할 수 있었다. 명령어가 실행되는 것으로 보아 command injection이 될 것 같아서 시도해 보았고,
세미콜론과 ls 명령어를 통해 Command injection이 가능하다는 것을 알게 되었다.
이 외에 사이트에서 할 수 있는 기능은 없어 보였기에 어떻게 해야 할지 알아보았다. 일단 컨테너에 접근을 해야 하기 때문에 컨테이너의 수와 같은 정보를 알아내야 하는데, 그런 내용은 aws cli를 통해 확인 가능하기 때문에 cli에서 자격 증명을 할 수 있는 방법을 찾아보았다. 일단 접속한 인스턴스의 메타데이터를 알아보기로 했다.
https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html
EC2 인스턴스에 대한 인스턴스 메타데이터에 액세스 - Amazon Elastic Compute Cloud
EC2 인스턴스에 대한 인스턴스 메타데이터에 액세스 인스턴스 자체 내에서 또는 EC2 콘솔, API, SDK 또는 AWS CLI에서 EC2 인스턴스 메타데이터에 액세스할 수 있습니다. 콘솔 또는 명령줄에서 인스턴
docs.aws.amazon.com
위 공식 문서에서 확인할 수 있듯이 http://169.254.169.254/lastest/meta-data/ 를 통해 EC2 인스턴스의 메타 데이터를 확인할 수 있다. 그중에서 자격 증명을 얻을 수 있은 엔드포인트가 존재하는데,
https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/ec2-instance-metadata.html
인스턴스 메타데이터를 사용하여 EC2 인스턴스를 관리합니다. - Amazon Elastic Compute Cloud
사용자는 인스턴스 자체 내에서 인스턴스 메타데이터 및 사용자 데이터에만 액세스할 수 있지만, 데이터는 인증 또는 암호화 방법으로 보호되지 않습니다. 인스턴스에 직접 액세스할 수 있는
docs.aws.amazon.com
해당 엔드포인트는 위의 공식 문서에서 확인할 수 있다.
결과적으로 http://169.254.169.254/lastest/meta-data/iam/security-credentials/ 를 통해 EC2 인스턴스의 역할 이름을 알아낸 후 해당 역할 이름까지 엔드포인트에 포함시켜서 임시 보안 자격 증명을 알아내면 되는 것이다.
위와 같은 임시 보안 자격 증명의 키 값과 토큰을 알아내어 aws cli 로그인을 진행할 수 있다.
해당 url로 요청을 보냈더니 예상대로 ec2 인스턴스 역할 이름이 출력되었다.
이를 포함시켜 자격 증명을 얻어낼 수 있었고,
해당 계정으로 cli 설정을 해주고 aws 명령어를 사용해 줬다.
근데 iam 뿐만 아니라 몇몇 서비스에 접근할 수 없는 권한 제한이 존재했다.. 그래서 다른 방법을 좀 생각해 보다가 Command Injection이 된다는 것과 현재 접속한 EC2 인스턴스는 어차피 컨테이너를 띄우고 있다는 것 때문에 명령어로 도커 명령어를 입력하면 실행 중인 컨테이너 정보를 확인할 수 있지 않을까? 라는 생각을 했다.
그래서 세미 콜론과 docker ps를 통해 실행 중인 docker 컨테이너를 확인해보니 명령어가 실행되는 것을 확인했다.
그래서 현재 인스턴스에서 실행중인 컨테이너들을 확인해 보았는데
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bdaf2b3291bc cloudgoat/ecs-takeover-vulnsite:latest "./main" 46 minutes ago Up 46 minutes ecs-cg-ecs-takeover-cgid0damkrfmaq-vulnsite-1-vulnsite-a2a78ddbf1f1dee4e401
24fc6b0d6a8f busybox:latest "sleep 365d" 46 minutes ago Up 46 minutes ecs-cg-ecs-takeover-cgid0damkrfmaq-privd-1-privd-98f2b7eff997ace6bf01
e72222d546bb amazon/amazon-ecs-agent:latest "/agent" 47 minutes ago Up 47 minutes (healthy) ecs-agent
위와 같이 총 3개의 컨테이너가 실행되고 있었다.
ecs-agent는 클러스터에 등록된 모든 컨테이너 인스턴스에서 실행되는 프로세스이고,
vulnsite는 현재 접속한 웹 사이트의 컨테이너,
privd는..??? 조금 수상하다..
365일 동안 sleep 상태 즉, 실행은 되는데 아무런 동작을 하지 않게 설정되어 있다.
컨테이너를 활용해 무엇을 할 수 있을까 하다가, EC2 인스턴스의 IAM으로는 ECS의 서비스들을 확인할 권한이 없었지만, ECS에는 Task 역할이 따로 존재하기 때문에 컨테이너에 접근하여 해당 권한으로 자격 증명을 해보면 될 것 같다는 생각을 했다. 때문에 ECS Task 역할의 임시 자격 증명을 얻는 방법을 찾아보았다. ECS에 대한 공식 문서들을 여러 곳에서 찾아보던 중
https://docs.aws.amazon.com/ko_kr/AmazonECS/latest/developerguide/task-iam-roles.html
Amazon ECS 작업 IAM 역할 - Amazon Elastic Container Service
작업에 대한 IAM 역할을 지정할 때 해당 작업에 대한 컨테이너의 AWS CLI 또는 다른 SDK는 작업 역할에 의해 단독으로 제공된 AWS 자격 증명을 사용하며 Amazon EC2 또는 실행 중인 외부 인스턴스에서 IAM
docs.aws.amazon.com
위 링크에서 아래와 같이 EC2의 인스턴스 메타데이터를 확인하는 것과 비슷한 IP를 발견하였고,
자세히 해당 내용을 보니,
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You may
# not use this file except in compliance with the License. A copy of the
# License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is distributed
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied. See the License for the specific language governing
# permissions and limitations under the License.
$gateway = (Get-NetRoute | Where { $_.DestinationPrefix -eq '0.0.0.0/0' } | Sort-Object RouteMetric | Select NextHop).NextHop
$ifIndex = (Get-NetAdapter -InterfaceDescription "Hyper-V Virtual Ethernet*" | Sort-Object | Select ifIndex).ifIndex
New-NetRoute -DestinationPrefix 169.254.170.2/32 -InterfaceIndex $ifIndex -NextHop $gateway -PolicyStore ActiveStore # credentials API
New-NetRoute -DestinationPrefix 169.254.169.254/32 -InterfaceIndex $ifIndex -NextHop $gateway -PolicyStore ActiveStore # metadata API
뒷부분에 주석으로 credentials API라는 사실을 알게 되었다.
ECS도 EC2와 같이 이러한 주소를 통해 확인할 자격 증명에 접근할 수 있을 것 같아서, 이번에는 어떤 엔드포인트로 접근해야 하는지 찾아보았다.
https://docs.aws.amazon.com/ko_kr/sdkref/latest/guide/feature-container-credentials.html
컨테이너 보안 인증 제공업체 - AWS SDKs 및 도구
이 페이지에 작업이 필요하다는 점을 알려 주셔서 감사합니다. 실망시켜 드려 죄송합니다. 잠깐 시간을 내어 설명서를 향상시킬 수 있는 방법에 대해 말씀해 주십시오.
docs.aws.amazon.com
그런데 위 링크에서 SDK라는 aws 리소스를 자동화할 때 이용되는 소프트웨어 개발 키트에서 보안 인증을 요청할 때 사용되는 엔드포인트가 환경 변수에 저장된다고 한다.
마침 또 169.254.170.2에 추가되는 내용이길래 이 환경 변수를 출력시켜서 엔드포인트를 알아내고, 169.254.170.2에 추가하여 자격 증명을 얻어내면 될 것 같다.
privd 컨테이너 내부에서 환경 변수를 출력시켜 엔드포인트를 얻을 수 있었고 해당 엔드포인트를 주소해 추가해서
위와 같이 자격 증명을 받아낼 수 있었다.
해당 계정으로 cli 로그인을 했더니
aws cli에서 클러스터의 정보와 태스크에 대한 정보를 얻을 수 있었다. 각 태스크에 대한 정보를 확인했더니
이렇게 privd 서비스 하나에 privd 태스크 2개, vault 서비스 하나에 vault 태스크 1개, vulnsite 서비스에 vulnsite 태스크 1개로 구성되어 있었다. 문제에서 제공한 EC2 인스턴스는 2개라고 초반에 확인하였고, 아까 웹 사이트에서 docker 명령어로 현재 인스턴스에서 실행 중인 컨테이너를 확인해 보니 privd, vulnsite 컨테이너만 존재했기 때문에 vault 컨테이너는 현재 인스턴스 말고 또 다른 인스턴스에 존재한다는 것을 알 수 있었다. 따라서 현재 구조는 다음과 같다.
이 구조에서 내가 뭘 해야 할까 고민하다가 말도 안 되는 발상으로 문제를 풀게 되었다..
-> 사실 원래 문제 이름은 TakeOver인데, 스스로 왜 그랬는지 TaskOver이라고 알고 있는 바람에 문제 제목에서 힌트를 주는 건가 싶었다.. 그래서 Task가 여러 개 있어야 하나? 라는 생각도 하고 다양한 생각을 하다가 혹시 Task는 많은데 EC2가 부족하면 Task가 Over 되는건가?? 라는 생각이 들어 이 문제를 이런 방법으로 접근할 수 있게 되었다..ㅎㅎㅎ
그래서 만약 EC2 인스턴스가 삭제되거나 인스턴스 안에 있는 태스크가 삭제된다면
당연히 위와 같이 EC2의 Task는 종료될 것이다. 하지만 또 만약 여기서 Task 정의가 데몬이 아닌 복제본 상태라면 어떻게 될까?
복제본으로 수가 지정된 Task는 무조건 그 수를 유지해야 한다. 때문에 기존 인스턴스를 사용하지 못한다고 하더라도
클러스터 내부의 또 다른 인스턴스에 Task를 실행시킬 것이다.
최종적으로는 위와 같은 구조가 만들어지고, 우리가 현재 접근할 수 있는 vulnsite와 같은 인스턴스 내에 위치하게 된다. 그렇다면 접근이 가능할 것으로 보이니 EC2를 종료시킬 방법을 찾아보았다.
Amazon ECS 컨테이너 인스턴스 드레이닝 - Amazon Elastic Container Service
minimumHealthyPercent와 maximumPercent가 모두 100%이면 서비스에서 기존 태스크를 제거할 수 없으며 교체 태스크도 시작할 수 없습니다. 그러면 컨테이너 인스턴스 드레이닝이 실패하고 새 배포가 만들
docs.aws.amazon.com
그중에서 ECS가 컨테이너 인스턴스를 드레이닝 상태로 전환할 수 있는 기능을 제공한다는 것을 알게 되었다. 드레이닝은 시스템 업데이트를 수행하거나 클러스터 용량을 스케일 다운하기 위해 클러스터에서 컨테이너 인스턴스를 제거해야 하는 경우를 위한 ECS의 기능이다. 드레이닝 상태가 되면 ECS는 Task가 컨테이너 인스턴스에서 배치를 위해 예약되지 않도록 한다. 이 기능을 이용해 예상했던 대로 Task의 위치를 재배치시켜보겠다.
일단 vault Task가 복제본 상태로 설정된 것을 확인했고,
위와 같이 인스턴스를 변경하는 명령어를 이용해 대상 클러스터와 상태를 변경할 EC2 인스턴스의 arn을 입력하여 상태를 DRAINING로 바꾸었다. 정상적으로 명령어가 실행된 것을 확인할 수 있고,
웹 사이트에서 docker ps 명령어를 실행시키니 다음과 같은 응답이 나왔다.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
08a0307f7d76 busybox:latest sh -c '/bin/sh -c "…' 14 seconds ago Up 12 seconds ecs-cg-ecs-takeover-cgid0damkrfmaq-vault-1-vault-a2bedd8dd8cdc7a6b501
bdaf2b3291bc cloudgoat/ecs-takeover-vulnsite:latest ./main 5 hours ago Up 5 hours ecs-cg-ecs-takeover-cgid0damkrfmaq-vulnsite-1-vulnsite-a2a78ddbf1f1dee4e401
24fc6b0d6a8f busybox:latest sleep 365d 5 hours ago Up 5 hours ecs-cg-ecs-takeover-cgid0damkrfmaq-privd-1-privd-98f2b7eff997ace6bf01
e72222d546bb amazon/amazon-ecs-agent:latest /agent 5 hours ago Up 5 hours ecs-agent
vault 라는 컨테이너가 추가되었고, 예상대로 동작된 것 같다.
컨테이너 내부에 접근해서 ls 명령어를 통해 FLAG.TXT 파일의 존재를 확인할 수 있었고,
cat 명령어로 해당 파일을 읽어 Flag를 획득할 수 있었다.
대응 방안
이번 시나리오에서 발생한 취약점들은 다음과 같다.
- Command Injection
- Credentials Endpoint 접근 가능
- 웹 사이트에서 docker 명령어 사용 가능
- ECS와 EC2에 과한 IAM 권한 부여
- 민감한 정보가 담긴 Task가 복제본 상태로 설정됨
해당 취약점들에 대한 대응 방안은 다음과 같다.
Command Injection
웹 사이트에서 발생하는 취약점을 해결해야 한다. 때문에 사용자의 입력 값을 필터링하거나 os.system() 등과 같이 명령어를 직접 실행시키는 함수를 사용하는 것은 지양해야 한다.
Credentials Endpoint 접근 가능
Credentials Endpoint 비활성화 또는 AWS_CONTAINER_CREDENTIALS_FULL_URI와 같은 환경 변수를 암호화해두는 방법도 좋은 방법이다.
웹 사이트에서 docker 명령어 사용 가능
컨테이너에 docker.sock 마운트를 하면 안 된다. 문제를 다 풀고 cloudgoat의 테라폼 코드를 살펴보니 아래와 같이 docker.sock가 마운트 되고 있었다.. 이렇게 되면 컨테이너에서 도커를 조종할 수 있기 때문에 위험하다.
ECS와 EC2에 과한 IAM 권한 부여
docker.sock와 마찬가지로 테라폼 코드에 아래 사진 왼쪽과 같이 IAM 권한이 부여되고 있었다. 이를 왼쪽과 같이 사용해야 하는 권한만 사용하도록 최소한의 권한을 부여해주어야 한다.
민감한 정보가 담긴 Task가 복제본 상태로 설정되어 있음
vualt와 같이 접근할 경우 위험한 Task는 데몬 상태로 설정되어 있어야 한다. 일반적인 Task는 무중단 배포를 위해 Rolling Update를 사용하는 경우에는 복제본 상태를 유지해야 하지만, 민감 정보가 실행되는 Task는 되도록 데몬 상태로 설정하는 것이 좋다.
처음으로 해보는 클라우드 실습 문제였는데, 힘든 만큼 해결했을 때 희열감 때문에 하는구나 싶었다.. 앞으로 비용적으로 지원이 조금 된다면 클라우드 관련 실습 문제들도 더 많이 접해보고 싶다!!