본문 바로가기

AWS

Bucket monopoly : 간단한 S3 이름의 함정


AWS S3 버킷 이름을 그저 파일을 저장할 공간의 식별자일 뿐이라고 생각하기 쉽습니다.  

많은 개발자들이 자신이 생성한 버킷의 이름 자체에 문제가 있을 것이라고 생각하지 않습니다. 그러나 AWS 전역에서 유일하다는 S3 이름의 특성상, 예측 가능한 이름이 공격자에게 기회를 제공합니다.

서비스 코드명과 환경명이 합쳐진 단순한 버킷 이름은 공격자에게도 충분히 추측 가능한 패턴입니다.
공용 권한이 열려 있거나, 미래에 생성될 리소스 이름이 미리 점유된다면? 높은 확률은 아니지만 경우에 따라 단지 이름만으로도 심각한 보안 사고로 이어질 수 있으므로 예방이 필요합니다. 

이 글에서는 "S3 환경에서의 예측 가능한 이름(Predictable Resource Name) 문제"가 어떻게 치명적으로 작용할 수 있는지를 살펴보려 합니다.

https://s3.amazonaws.com/myapp-us-east-1
https://s3.amazonaws.com/data-backup-us-west-2
https://s3.amazonaws.com/logs-prod-eu-west-1
https://s3.amazonaws.com/media-assets-ap-southeast-1
https://s3.amazonaws.com/customer-files-us-east-2
arn:aws:s3:::myapp-us-east-1
arn:aws:s3:::data-backup-us-west-2
arn:aws:s3:::logs-prod-eu-west-1
arn:aws:s3:::media-assets-ap-southeast-1
arn:aws:s3:::customer-files-us-east-2

AWS 환경이 생소하실 수도 있는 분들을 위해 약간의 이론을 짚어보겠습니다.

AWS 내에서는 자산을 식별하기 위한 식별자로 ARN(Amazon Resource Name)이 사용됩니다.
ARN의 일반적인 형식은 아래와 같습니다.

arn : partition : service : region : account-id : resource-id
arn : partition : service : region : account-id : resource-type / resource-id

각 필드에 대한 설명은 아래와 같습니다.

필드 설명
arn 문자열이 Amazon Resource Name임을 나타내는 고정 접두사입니다.
partition AWS의 구분된 환경(예:aws,aws-cn,aws-us-gov)을 나타냅니다. 일반적인 상용 환경은 aws 입니다.
service 리소스가 속한 서비스의 이름(EC2, S3, IAM 등)을 의미합니다.
region 리소스가 존재하는 리전의 코드입니다(예:ap-northeast-2).
서비스가 전역 범위라면 생략될 수도 있습니다.
account-id 리소스를 소유한 AWS 계정의 ID를 나타냅니다.
고객 계정에 종속되지 않는 서비스의 경우 생략될 수도 있습니다.
resource-type / resource-id 리소스의 세부 구분과 고유한 ID를 나타냅니다. 서비스마다 표현 방식이 상이할 수 있습니다.

일반적으로 S3 name 은 해당 S3가 속한 region 이나, S3를 보유한 계정에 상관없이 고유하기 때문에, S3의 ARN은 region 필드와 account-id 필드가 생략됩니다.

대부분의 ARN은 해당 리소스가 생성된 지역이나 소유한 계정에 따라 region 이나 account-id 필드로 각 리소스들을 구분합니다.   
예를 들어, EC2 인스턴스나 RDS 데이터베이스의 ARN을 보면 'arn:aws:ec2:ap-northeast-2:123456789012:instance/...'처럼 리전 정보가 포함되어 있습니다. 일반적으로 동일한 리소스 이름이라도 리전이 다르면 서로 공존할 수 있습니다.

하지만 S3는 다릅니다.  
S3의 ARN은 region과 account-id 필드가 생략되기 때문에 S3 버킷의 식별자는 리전과 관계없이 AWS 전역(Global Namespace) 에서 고유합니다. 한 번 특정 이름이 등록되면, 그 이름은 다른 어떤 리전/계정에서도 다시 사용할 수 없습니다. 마치 인터넷 도메인처럼 전 세계에서 단 한 사람이 소유할 수 있는 식별자와 같죠.

이 전역 유일성은 관리 측면에서는 간편할 수 있습니다. 어떤 리전에서 접근하더라도 동일한 URL 패턴으로 버킷을 식별할 수 있기 때문입니다. 그러나 바로 이 특성 때문에 누군가 서비스의 버킷 명명 규칙을 추측하여 선점하거나, 비공개로 사용될 이름을 미리 확보할 수 있다면 해당 리소스에 대한 통제권을 빼앗길 가능성이 생깁니다. 

이때, 외부에서 리소스 이름을 추측할 수 있는 상황을 Predictable Resource Name 이라고 하며
위 원리를 이용해 외부에서 S3 name을 선점하는 공격을 Bucket monopoly 또는 Resource namesquatting 이라고 합니다. 

출처 : aquasec


AWS CDK 서비스에서 Bucket Monopoly / Resource namesquatting 공격이 실제로 피해로 이어진 사례가 있습니다.

CDK는 Cloud Development Kit의 약자로, CloudFormation을 이용해 일반적인 프로그래밍용 언어(C, Java, Python)로 AWS 환경을 관리하는 기능을 제공하는 AWS Native Service입니다. CDK 의 동작 방식을 정리하면 다음과 같습니다.

1. C, Java, Python 등의 런타임에서 AWS 리소스를 정의
2. CDK에서 이를 CloudFormation Stack으로 변환
3. CloudFormation 서비스에서 만들어진 Stack을 배포

출처 : AWS

CDK는 개발자의 Local PC에서 동작합니다. CDK를 사용하기 위해서는 Local PC에서 CDK bootstrap 을 실행하여야 하며 이때 AWS에서 Local PC에서 동작하는 CDK를 위한 IAM 정책들과 리소스들이 생성됩니다. 그 중에는 CDK에 의해 생성되는 CloudFormation Stack 문서를 저장하는 S3(S3 Staging Bucket)CloudFormation Stack 배포를 실행하기 위해 AdministrateAccess 권한이 포함된 IAM Role(CloudFormationExecutionRole)이 있습니다.

출처 : aquasec

CloudFormation Stack 문서가 저장되는 S3 Staging Bucket 이 공격자에 의해 점유된 상태라면 어떤 문제가 발생할까요?
공격자가 CloudFormation Stack 이 배포되기 전 해당 문서에 접근하여 악성코드를 삽입하는 경우, Stack 배포 시점에 AdministratorAccess 권한으로 악성코드가 실행될 가능성이 있습니다.

어떻게 CDK의 S3 Staging Bucket 이 공격자들에 의해 점유될 수 있었을까요?

해당 Bucket name 은 다음 5개의 필드로 자동으로 설정되었습니다. 이 중 Qualifier 만이 공격자가 예측할 수 없는 문자열이나, 이마저도 기본값 "hnb659fds" 을 그대로 사용하거나, 해당 값이 유출되는 경우(github 등에 노출) 예측이 가능하여 문제가 될 수 있었습니다.

필드 설명 예측 가능 여부
Prefix "cdk" 로 고정 "cdk" 로 고정되므로 예측 가능
Qualifier 개발자가 설정하는 단순 문자열. 기본값 "hnb659fds" 기본값을 그대로 사용하거나, 코드가 노출되는 경우 가능
Constant "assets" 로 고정 "assets" 로 고정되므로 예측 가능
Account ID AWS Account ID Account-id 가 노출되는 경우 가능
Region AWS Region 공격자가 생성될 Region을 미리 알거나 모든 Region에 해당하는 S3를 미리 생성하는 경우 예측 가능

출처 : aquasec

예를 들어, 공격자는 점유한 S3 버킷에 희생자 AWS Account 의 리소스가 접근할 수 있도록 설정하고, 실제로 CloudFormation Stack이 업로드되면 Lambda 등을 이용해 자동으로 악성코드를 삽입하는 방식을 채택할 수 있었을 겁니다.

출처 : aquasec


또한, 조직에서 사용하는 오픈소스에서 S3 등을 별도로 사용하는 경우에도 문제가 될 수 있습니다.
실제로 npm 패키지 중 하나인 "bignum" 에서 Bucket monopoly / Resource namesquatting 공격이 25년 초에 보고되었습니다.
설치 스크립트에 포함되었지만 유지보수되고 있지 않던 S3 name을 공격자가 악용하여 해당 패키지를 설치한 PC에 악성코드가 동작하게 된 사례였습니다.  

 

Hijacking S3 Buckets: New Attack Technique

Without altering a single line of code, attackers poisoned the NPM package “bignum” by hijacking the S3 bucket serving binaries necessary for its function and replacing them with malicious ones

checkmarx.com


예방하기 위해서는 어떠한 조치들을 할 수 있을까요?

1. 근본적으로 Resource name을 예측할 수 없도록 명명 규칙에 충분한 길이의 난수를 포함하는 것이 가장 좋습니다.
2. 자동으로 생성되는 S3 bucket 이 있는 경우, 그 이름을 기본값으로 사용하지 않도록 해야 합니다.
3. 외부 계정이 소유한 리소스에 접근할 수 없도록 최소 권한 원칙에 따라 IAM 정책을 설계해야 합니다.

이러한 규칙이 조직적으로 적용될 수 있도록 CI/CD 파이프라인에서 자동으로 검증될 수 있도록 하거나, 조직 차원의 리소스 명명 규칙을 세우는 등의 조직적인 노력도 큰 도움이 될 수 있습니다.

위의 사례가 AWS 공유 책임 모델에 의하여 AWS에서 조치하여야 했던 영역이며 현재 위 문제는 조치되었기 때문에 고객이 신경쓰지 않아도 된다고 할 수도 있습니다. 하지만 Bucket monopoly / Resource namesquatting 는 고객 책임 범위에서도 발생될 수 있는 문제입니다.

누군가는 '아니 뭐 이름 몇 글자가 대수라고' 라는 등의 반응을 보일 수도 있습니다. 많은 보안 사고가 작은 부주의가 쌓이고 쌓여 발생함을 기억하고 건강한 조직 문화를 만들 수 있도록 함께 노력하는 것이 가장 중요한 것이 아닐까 합니다.


Beaver Dam 팀은 공격자의 시선에서 바라보는 Cloud 보안인, Offensive Cloud Security 분야를 연구하고 학습하고 있는 팀입니다.
매월 무료 세미나를 개최하며, 다양한 정보 아래 링크들에서 공유드리고 있으니 많은 관심을 부탁드립니다.