2P by GN⁺ | ★ favorite | 댓글 1개
  • Tracebit은 공개·비공개 S3 버킷의 AWS Account ID를 추정하는 기법을 정리하고, 예시 버킷 bucket-alpha에서 123456789101을 복원함
  • 핵심 단서는 S3용 Interface VPC Endpoint 정책의 s3:ResourceAccount 조건과, 요청이 자체 CloudTrail 로그에 남는지 여부임
  • 비공개 버킷은 최종 응답이 계속 AccessDenied여도, VPC Endpoint 정책을 통과한 요청만 CloudTrail에 나타나므로 숫자 패턴 일치 여부를 판별할 수 있음
  • 정책 전파와 CloudTrail 지연 때문에 단순 탐색은 약 40 * 12분 = 8시간까지 걸릴 수 있지만, aws:useridRoleSessionName을 이용한 120개 정책 문장 병렬 테스트로 10분 미만까지 줄어듦
  • 이 기법은 StringLikes3:ResourceAccount부분 일치를 허용하기 때문에 가능하며, 일부 활동은 버킷 소유자의 CloudTrail에도 남을 수 있음

공개 버킷 기법에서 출발한 확장

  • 2021년 Ben Bridts는 공개 S3 버킷의 AWS Account ID를 찾는 방법을 공개함
  • Tracebit의 방식은 이 아이디어의 여러 요소를 재사용하되, 비공개 버킷까지 포함해 S3 버킷의 Account ID를 찾는 데 초점을 둠
  • 예시 실행에서는 bucket-alpha에 대해 CloudTrail에서 통과한 세션 이름을 모아 최종적으로 123456789101을 복원함

기존 공개 버킷 방식이 가능한 이유

  • Ben Bridts의 방법은 세 조건이 맞물려 작동함
    • 요청에 IAM 정책을 적용할 수 있음
    • 정책이 요청을 허용했는지 차단했는지 추론할 수 있음
    • s3:ResourceAccount 조건 키에 와일드카드 매칭을 적용할 수 있음
  • 공개 버킷에서는 정책이 요청을 막으면 AccessDenied가 나고, 정책이 허용하면 요청이 성공하므로 정책 통과 여부를 쉽게 구분할 수 있음
  • s3:ResourceAccount를 한 자리씩 좁히면 전체 탐색 공간이 수조 개에서 수백 개 수준으로 줄어듦

비공개 버킷에서는 응답 대신 CloudTrail을 본다

  • 비공개 버킷은 어떤 정책을 적용해도 대상 버킷 정책 때문에 최종 응답이 AccessDenied가 됨
  • Tracebit 방식은 응답 결과가 아니라 요청이 자체 CloudTrail 로그에 나타나는지를 기준으로 삼음
    • 요청이 CloudTrail에 나타나면 VPC Endpoint 정책은 허용했고, 이후 버킷 정책에서 거부된 것임
    • 요청이 CloudTrail에 없으면 VPC Endpoint 정책에서 차단된 것임
  • S3용 Interface VPC Endpoint를 만들면 요청에 VPC Endpoint 정책을 적용할 수 있고, 이 정책은 버킷 정책과 요청 주체의 IAM 정책 등 다른 정책들과 함께 평가됨
  • VPC Endpoint 정책에서도 StringLike 와일드카드와 리소스 조건 키를 사용할 수 있어 같은 탐색 방식이 가능함

기본 절차: 리전 확인부터 이벤트 조회까지

  • 먼저 대상 버킷의 리전을 찾아야 함
    • 버킷 HTTP 엔드포인트에 curl을 보내면 요청이 금지되어도 x-amz-bucket-region 헤더가 반환됨
    • 예시에서는 bucket-alpha.s3.amazonaws.com 응답 헤더에서 us-east-1을 확인함
  • 대상 버킷과 같은 리전에 VPC와 S3용 VPC Endpoint를 배포함
    • VPC Endpoint는 정책 적용이 가능한 Interface 타입이어야 함
    • 해당 VPC Endpoint가 VPC의 S3 요청에 영향을 주므로, 이 목적 전용 VPC를 만드는 것이 좋음
  • VPC 안에서 S3 요청을 보내기 위해 EC2 인스턴스를 실행하고, 해당 인스턴스가 S3용 VPC Endpoint를 사용하는지 확인함
  • VPC Endpoint 정책을 수정해 s3:ResourceAccount가 특정 숫자로 시작하는지 테스트함
    • 예를 들어 Account ID가 0으로 시작하는지 확인하려면 s3:ResourceAccount"0*" 조건을 둠
  • EC2 인스턴스에서 대상 버킷에 GetBucketAcl 같은 Management 요청을 보냄
    • Management 요청을 쓰면 CloudTrail 설정에서 별도 처리가 덜 필요함
    • 요청 결과는 예상대로 AccessDenied가 됨

CloudTrail로 숫자 패턴을 판별하는 방식

  • 요청 후 CloudTrail에서 GetBucketAcl 이벤트가 나타나는지 조회함
  • 이벤트가 나타나면 VPC Endpoint 정책이 요청을 허용한 것이므로 Account ID가 테스트한 패턴과 일치함
    • 예: "0*" 조건에서 이벤트가 보이면 Account ID가 0으로 시작함
  • 이벤트가 나타나지 않으면 VPC Endpoint 정책이 요청을 막은 것이므로 해당 패턴과 일치하지 않음
  • CloudTrail에 이벤트가 나타나는 데 몇 분이 걸릴 수 있어, 이벤트가 없다고 판단하기 전 10분 대기를 권장함
  • VPC Endpoint 정책 변경도 완전히 전파되어 적용되는 데 시간이 걸리며, 정책 수정 후 5분 대기가 잘 작동함

자동화했지만 기본 방식은 느리다

  • Tracebit은 이 과정을 자동화하는 스크립트를 작성해 버킷의 Account ID를 안정적으로 찾을 수 있게 함
  • 단순히 한 자리씩 모든 숫자를 확인하는 대신, 각 자리에서 이진 탐색에 가까운 방식으로 테스트 횟수를 줄임
    • 예를 들어 s3:ResourceAccount 조건에 ["0*", "1*", "2*", "3*", "4*"]처럼 여러 패턴을 넣어 범위를 나눔
  • 그래도 정책 적용과 CloudTrail 확인 대기 시간이 병목으로 남음
    • 이진 탐색을 써도 약 40 * 12분 = 8시간이 걸릴 수 있음
  • 몇 시간 실행한 예시에서는 bucket-alpha의 Account ID로 123456789101을 성공적으로 찾음

120개 정책 문장으로 10분 미만까지 단축

  • 더 빠른 방식은 VPC Endpoint 정책에 가능한 모든 자리·숫자 조합을 미리 넣는 것임
  • 정책에는 총 120개 문장이 들어감
    • AWS Account ID의 각 위치별 가능한 숫자 10개를 테스트함
    • 각 문장은 s3:ResourceAccount의 특정 자리 패턴과 aws:userid 조건을 함께 사용함
  • aws:userid 조건은 STS AssumeRole 호출에서 자유롭게 지정할 수 있는 RoleSessionName 값을 매칭하는 데 사용됨
    • 특정 RoleSessionName으로 역할을 가정하면 특정 자리·숫자 테스트에 해당하는 정책 문장을 선택적으로 통과시킬 수 있음
  • 이 정책은 VPC Endpoint 정책의 최대 문자 길이에 간신히 들어감
  • 모든 120개 가능성을 병렬로 테스트하므로 정책을 매번 수정하거나 CloudTrail 결과를 개별적으로 기다릴 필요가 줄어듦
  • 이 방식으로 Account ID 탐색 시간이 10분 미만으로 줄어듦

노출 범위와 응용 가능성

  • 일부 활동은 대상 버킷 소유자의 CloudTrail 로그에 보일 수 있음
  • Tracebit은 공개 전에 AWS Security 팀과 상의함
  • AWS Account ID가 민감 정보인지에 대해서는 이미 많은 논의가 있었고, 예시 CloudTrail 이벤트에서는 서드파티 Account ID가 HIDDEN_DUE_TO_SECURITY_REASONS로 가려져 있음
  • 같은 기법은 버킷과 관련된 다른 리소스 조건 키에도 적용될 수 있음
    • 예: aws:ResourceOrgID
    • 예: aws:ResourceOrgPaths
    • 예: aws:ResourceTag
  • S3 외에 이 기법을 적용할 수 있는 다른 서비스에도 응용될 수 있음
  • 모든 리전에 상호 피어링된 VPC와 VPC Endpoint를 만들면 대상 버킷의 리전에 관계없이 작동하는 구성을 만들 수 있을 가능성이 있음
  • 이 기법은 s3:ResourceAccount 조건에 대해 StringLike 부분 일치를 사용할 수 있기 때문에 가능함
  • VPC Endpoint 정책에 의해 거부된 이벤트도 CloudTrail에 기록된다면 유익할 수 있음

댓글과 토론

Hacker News 의견들
  • s3:ResourceAccount 조건 키에 와일드카드 매칭을 적용할 수 있다는 게 정말 이상함
    계정 ID의 일부 일치 여부로 권한을 허용하거나 거부할 정당한 이유는 없어 보임

    • AWS 정책 실행에는 여러 연산자와 피연산자가 있고, 이 경우 계정 ID 문자열에 StringLike를 쓰는 구조라서 그런 듯함
      DevOps 쪽에서 이제 부채널 공격을 발견하는 흐름이 좀 흥미로움. CPU의 추측 실행 부채널인 Meltdown, Spectre도 발견 당시 큰 파장을 일으켰고, 그 전에는 전력 분석과 자기왜곡 검출, 상수 시간 암호학 같은 분야도 있었음
      https://en.m.wikipedia.org/wiki/Side-channel_attack
      https://en.m.wikipedia.org/wiki/Power_analysis
    • 나도 그 부분이 놀라웠음. 이 필드는 정확히 일치 외에는 허용되면 안 될 것 같고, 계정 ID에 패턴 매칭을 쓸 사례가 떠오르지 않음
    • 아마 일반화하려는 경향에서 나온 듯함
      최근 사이드 프로젝트에서 OWL에서 영감을 받은 형식으로 질의를 작성하는 기능을 만들었는데, URL에서 호스트를 뽑거나 접두사 질의, like 질의, 정규식 질의 등을 하는 관계 연산자 라이브러리가 있음
      내 사이드 프로젝트의 또 다른 사이드 프로젝트라 쉬운 대로 했고, 말이 안 되는 경우에도 연산자를 항상 사용할 수 있게 둠. 숫자에 정규식 질의를 하면 어떻게 되는지도 모르고 신경 안 씀. AWS 내부에도 비슷한 것이 있을 수는 있지만, 사용자도 많고 보안에 민감한 시스템이라면 기준이 달라야 함
    • Unix 시스템에서 그룹 ID의 비트필드를 매칭하는 것과 비슷함
      누군가 이런 아이디어를 떠올리고 똑똑하다고 생각할 수는 있겠지만, 100% 통제하지 않는 시스템에 구현하는 건 어리석어 보임
  • 일반적으로 계정 ID를 공개적으로 뿌리지는 않겠지만, 언젠가는 일부가 노출된다고 봐야 함
    더 많은 서드파티 벤더와 SaaS 플랫폼이 IAM 사용자와 액세스 키 대신 역할 위임을 선호하는 통합 방식으로 옮겨가고 있고, 그래야 맞음. 그러면 최소한 통합 지점으로 쓰는 계정의 계정 ID는 다른 당사자에게 알려지며, 그쪽에도 의존성, 취약점 등이 있음

    • 이게 궁금함. 공격자가 AWS 계정 ID로 무엇을 할 수 있나? 누군가의 이메일 주소를 아는 것과 어떻게 다른가?
  • AWS 계정 ID는 IP 주소와 비슷함. 민감할 수는 있지만, 일을 하려면 누군가는 알아야 함
    예를 들어 1~2년 전 자금세탁방지 절차 때문에 통합해야 하는 서드파티가 있었음. 보통 공개 SFTP 포트보다 더 안전하니 그 조직과 PrivateLink를 설정하자고 했는데, 상대 회사는 계정 ID를 숨겨야 한다는 보안상의 이유로 거부함. 상호 권한을 위한 PV 엔드포인트의 역할 ARN에 필요했는데도 그랬음
    결국 그들이 인바운드 22번 포트에 쓰는 공개 IP 대역을 허용 목록에 넣었음
    교훈은 ID를 난독화해서 똑똑하다고 생각할 수는 있지만, 상대가 되돌아올 주소를 모르면 비즈니스를 운영하기 어렵다는 것임

    • AWS PrivateLink에는 이런 통합에 대체로 바람직하지 않게 만드는 성질이 하나 더 있음. 통신이 양방향이고, IP 서브넷이 겹치면 안 됨
      우리는 벤더 입장에서 보통 VPC Endpoint Service로 통합함. 이 방식은 통신이 단방향이고, 우리 서비스가 고객 VPC 안의 로드 밸런서 엔드포인트로 노출됨
  • 관심 있는 분들을 위해 코드를 여기에 올려둠: https://github.com/tracebit-com/find-s3-account

  • 흥미로운 발견인 건 맞지만, 제목만 보고는 좀 더 직접적인 방법이 있는 줄 알았음
    AWS에서 관리자 계정으로 조직 안에서 “X 리소스가 어디에 있나”를 간단히 물어보고, 특정 S3 버킷이 어느 계정에 있는지 빠르게 알 수 있으면 좋겠음. 다른 리소스도 그렇지만 S3 버킷이 특히 큼
    솔직히 이건 더 나은 관행이 생기기 전의 레거시 버킷이나, 버킷을 전부 코드로 정의하기 전부터 있던 것들에서 주로 생기는 문제임. 그래도 AWS 계정이 많으면 알 수 없는 계정과 리전에 있는 리소스를 찾는 일이 지루해질 수 있음

    • 조직용 AWS Config 애그리게이터를 설정해 두면, 모든 조직 계정의 리소스 인벤토리를 Athena SQL로 질의할 수 있음
      그러면 어떤 계정이 리소스를 소유하는지 찾는 건 대략 select accountId where arn = "x" 정도로 가능함
  • 전역 네임스페이스를 가진 다른 공개 AWS 리소스들도 AWS 계정 ID를 드러냄
    https://blog.plerion.com/conditional-love-for-aws-metadata-e...

  • 약간 관련해서, Cloudflare account_id와 zone_id는 공개되어도 안전함
    https://github.com/cloudflare/cloudflare-docs/issues/474
    https://community.cloudflare.com/t/api-zone-id/355566

    The Zone ID and Account ID are not sensitive. Sensitive data like account API Key, Secrets etc. can all be revoked, rotated or changed. See the comment 36 below on the Wrangler repo: as per our security team, it’s completely Fine to have your zone_id and account_id public, the Global API key and associated email address should be kept secret.

    • AWS 계정 ID도 공개되어도 안전함
      다만 이걸로 할 수 있는 것 중 하나는 상관관계 파악임. 같은 AWS 계정에서 여러 S3 사이트를 운영한다면, 사람들이 그것들이 같은 계정에서 호스팅된다는 걸 볼 수 있음. 이게 중요한지는 위협 모델에 따라 달라짐
    • CF 계정에는 Gmail의 + 기능을 써서 쉽게 추측할 수 없는 완전히 고유한 이메일 주소를 만듦
      완벽하지는 않지만 추상화 계층을 하나 더해 줌
  • 관련해서, AWS 키 ID는 비밀 키 부분이 아니어도 그 안에 계정 ID가 한 비트 시프트된 형태로 들어 있음
    https://medium.com/@TalBeerySec/a-short-note-on-aws-key-id-f...
    이 키 ID는 S3 사전 서명 URL에 포함되므로, 이미 계정 ID를 공개하고 있었을 가능성이 큼

    • 이 스레드에서 꽤 많은 사람이 AWS 키 ID를 은닉에 의한 보안이나 심층 방어의 일부로 가정하는 듯함
      아마 다운보트되겠지만, 그래도 읽는다면 이건 은닉에 의한 보안이 좋은 방어가 아닌 이유를 보여주는 예임. 본인은 뭔가를 놓칠 것이고, 집요한 공격자는 놓치지 않을 것임
      은닉에 의존하지 않는 보안은 공격자가 예컨대 AES-256을 그냥 깨버릴 천재를 고용하지 않는 한, 내가 무언가를 이해했는지와 상관없이 적용됨: https://www.youtube.com/watch?v=KEkrWRHCDQU
  • 이게 왜 중요할 수 있나? 명확한 예로, 프로덕션 버킷이 주어지면 이제 같은 조직의 개발 버킷을 찾을 수 있게 됨. 개인적으로는 예상된 동작이 아님

    • 프로덕션과 개발에 같은 계정을 쓰는 경우에만 맞음. 같은 계정을 쓰지 말아야 할 또 다른 이유가 됨
    • 그걸 하려면 버킷 이름만 있으면 됨
      이런 열거 시도를 막으려면 버킷 이름에 무작위로 생성한 접두사나 접미사를 넣어야 함. 또한 대체가 아니라 추가 조치로, 기본 호스트명이 아닌 이름으로 버킷 객체를 공개해서 버킷 이름 자체가 새지 않게 하는 것도 좋은 방법임
    • 어떻게 그렇게 됨? 먼저 개발 버킷의 이름을 알아야 하지 않나?
    • 개발 버킷이 어떻게든 같은 계정에 있지 않은 한 가능성은 낮음
  • While account IDs, like any identifying information, should be used and shared carefully, they are not considered secret, sensitive, or confidential information.
    https://docs.aws.amazon.com/accounts/latest/reference/manage...

    • 적어도 디지털 세계에서는 정보가 공개 아니면 비공개 둘 중 하나인 것처럼 보임. 권한이 필요한 정보나 보호된 정보에 대한 개념이 별로 좋지 않음
      예를 들어 집 주소는 기술적으로 공개 정보지만, 가족 사진과 함께 고속도로 옆 광고판에 내가 사는 곳이라고 걸리는 건 절대 원하지 않음. 필요한 사람에게만 주고, 대체로 기밀에 가깝게 유지되거나 용도 제한이 있을 거라고 믿고 기대함
    • 이게 무슨 뜻인가?
      비밀도 아니고 민감하지도 않고 기밀도 아니라면, 왜 주의해서 공유해야 하나?
    • “비밀, 민감, 기밀 정보로 간주되지 않는다”는 건 “우리 기준으로는”이라고 써야 할 듯함
      사용자는 다르게 볼 수 있음