วิธีค้นหา AWS Account ID ของ S3 bucket ใด ๆ
(tracebit.com)- Tracebit สรุปเทคนิคในการประมาณ AWS Account ID ของ S3 bucket ทั้งแบบสาธารณะและไม่สาธารณะ และกู้คืน
123456789101จาก bucket ตัวอย่างbucket-alpha - เบาะแสสำคัญคือเงื่อนไข
s3:ResourceAccountใน policy ของ Interface VPC Endpoint สำหรับ S3 และการดูว่า request ถูกบันทึกไว้ใน log CloudTrail ของตนเองหรือไม่ - สำหรับ bucket ที่ไม่สาธารณะ แม้ response สุดท้ายจะยังคงเป็น
AccessDeniedแต่เฉพาะ request ที่ผ่าน policy ของ VPC Endpoint เท่านั้นที่จะปรากฏใน CloudTrail จึงสามารถตัดสินได้ว่าตรงกับแพตเทิร์นตัวเลขหรือไม่ - เนื่องจากการ propagate policy และความล่าช้าของ CloudTrail การค้นหาแบบง่ายอาจใช้เวลาถึงประมาณ
40 * 12นาที = 8ชั่วโมงแต่สามารถลดลงเหลือต่ำกว่า 10 นาทีได้ด้วยการทดสอบแบบขนานโดยใช้ policy statement 120 รายการ ผ่านaws:useridและRoleSessionName - เทคนิคนี้เป็นไปได้เพราะ
StringLikeอนุญาตให้ทำ partial match กับs3:ResourceAccountและกิจกรรมบางส่วนอาจถูกบันทึกใน CloudTrail ของเจ้าของ bucket ด้วย
เริ่มจากการต่อยอดเทคนิคสำหรับ bucket สาธารณะ
- ในปี 2021 Ben Bridts เผยแพร่บทความ วิธีค้นหา AWS Account ID ของ S3 bucket สาธารณะ
- วิธีของ Tracebit นำองค์ประกอบหลายส่วนของไอเดียนี้กลับมาใช้ โดยเน้นการค้นหา Account ID ของ S3 bucket ซึ่งครอบคลุมถึง bucket ที่ไม่สาธารณะ ด้วย
- ในการรันตัวอย่าง ได้รวบรวมชื่อ session ที่ผ่านจาก CloudTrail สำหรับ
bucket-alphaและสุดท้ายกู้คืนค่า123456789101ได้
เหตุผลที่วิธีเดิมสำหรับ bucket สาธารณะทำได้
- วิธีของ Ben Bridts ทำงานได้จากเงื่อนไข 3 ข้อที่ประกอบกัน
- สามารถนำ IAM policy ไปใช้กับ request ได้
- สามารถอนุมานได้ว่า policy อนุญาตหรือบล็อก request
- สามารถใช้ wildcard matching กับ condition key
s3:ResourceAccountได้
- สำหรับ bucket สาธารณะ หาก policy บล็อก request จะได้
AccessDeniedและหาก policy อนุญาต request จะสำเร็จ จึงแยกได้ง่ายว่า request ผ่าน policy หรือไม่ - เมื่อลดขอบเขต
s3:ResourceAccountทีละหลัก พื้นที่ค้นหาทั้งหมดจะลดจากระดับหลายล้านล้านเหลือเพียงระดับไม่กี่ร้อย
สำหรับ bucket ที่ไม่สาธารณะ ให้ดู CloudTrail แทน response
- bucket ที่ไม่สาธารณะจะได้ response สุดท้ายเป็น AccessDenied เนื่องจาก policy ของ bucket เป้าหมาย ไม่ว่าจะใช้ policy แบบใดก็ตาม
- วิธีของ Tracebit ใช้เกณฑ์ว่าคำขอปรากฏใน CloudTrail log ของตนเองหรือไม่ แทนการดูผลลัพธ์ของ response
- หาก request ปรากฏใน CloudTrail หมายความว่า policy ของ VPC Endpoint อนุญาตแล้ว และถูกปฏิเสธภายหลังโดย bucket policy
- หาก request ไม่อยู่ใน CloudTrail หมายความว่าถูกบล็อกโดย policy ของ VPC Endpoint
- เมื่อสร้าง Interface VPC Endpoint สำหรับ S3 จะสามารถนำ policy ของ VPC Endpoint ไปใช้กับ request ได้ และ policy นี้จะถูกประเมินร่วมกับ policy อื่น ๆ เช่น bucket policy และ IAM policy ของผู้ส่ง request
- ใน policy ของ VPC Endpoint ก็สามารถใช้ wildcard ของ
StringLikeและ resource condition key ได้ จึงใช้วิธีค้นหาแบบเดียวกันได้
ขั้นตอนพื้นฐาน: ตั้งแต่ตรวจสอบ Region จนถึงค้นหา event
- ก่อนอื่นต้องหา Region ของ bucket เป้าหมาย
- หากส่ง
curlไปยัง HTTP endpoint ของ bucket แม้ request จะถูกห้าม ก็จะได้รับ headerx-amz-bucket-regionกลับมา - ในตัวอย่าง ตรวจสอบพบ
us-east-1จาก response header ของbucket-alpha.s3.amazonaws.com
- หากส่ง
- deploy VPC และ VPC Endpoint สำหรับ S3 ใน Region เดียวกับ bucket เป้าหมาย
- VPC Endpoint ต้องเป็นชนิด Interface เพื่อให้สามารถใช้ policy ได้
- เนื่องจาก VPC Endpoint นี้ส่งผลต่อ request ไปยัง S3 ของ VPC จึงควรสร้าง VPC เฉพาะสำหรับวัตถุประสงค์นี้
- รัน EC2 instance ภายใน VPC เพื่อส่ง request ไปยัง S3 และตรวจสอบว่า instance นั้นใช้ VPC Endpoint สำหรับ S3
- แก้ไข policy ของ VPC Endpoint เพื่อทดสอบว่า
s3:ResourceAccountเริ่มต้นด้วยตัวเลขที่ระบุหรือไม่- เช่น หากต้องการตรวจสอบว่า Account ID เริ่มด้วย
0หรือไม่ ให้ใส่เงื่อนไข"0*"ในs3:ResourceAccount
- เช่น หากต้องการตรวจสอบว่า Account ID เริ่มด้วย
- จาก EC2 instance ส่ง Management request เช่น
GetBucketAclไปยัง bucket เป้าหมาย- การใช้ Management request ช่วยลดความจำเป็นในการจัดการพิเศษในการตั้งค่า CloudTrail
- ผลลัพธ์ของ request จะเป็น
AccessDeniedตามที่คาดไว้
วิธีตัดสินแพตเทิร์นตัวเลขด้วย CloudTrail
- หลังส่ง request ให้ค้นหาว่า event
GetBucketAclปรากฏใน CloudTrail หรือไม่ - หาก event ปรากฏ หมายความว่า policy ของ VPC Endpoint อนุญาต request ดังนั้น Account ID จึงตรงกับแพตเทิร์นที่ทดสอบ
- ตัวอย่าง: หากเห็น event ภายใต้เงื่อนไข
"0*"แปลว่า Account ID เริ่มต้นด้วย0
- ตัวอย่าง: หากเห็น event ภายใต้เงื่อนไข
- หาก event ไม่ปรากฏ หมายความว่า policy ของ VPC Endpoint บล็อก request จึงไม่ตรงกับแพตเทิร์นนั้น
- เนื่องจากอาจใช้เวลาหลายนาทีกว่า event จะปรากฏใน CloudTrail จึงแนะนำให้ รอ 10 นาที ก่อนสรุปว่าไม่มี event
- การเปลี่ยนแปลง policy ของ VPC Endpoint ก็ต้องใช้เวลากว่าจะ propagate และมีผลโดยสมบูรณ์เช่นกัน โดยการ รอ 5 นาที หลังแก้ policy ใช้งานได้ดี
ทำ automation แล้ว แต่วิธีพื้นฐานยังช้า
- Tracebit เขียนสคริปต์เพื่อทำกระบวนการนี้แบบอัตโนมัติ ทำให้สามารถค้นหา Account ID ของ bucket ได้อย่างเสถียร
- แทนที่จะตรวจทุกตัวเลขทีละหลักแบบตรง ๆ จะลดจำนวนครั้งในการทดสอบด้วยวิธีที่ใกล้เคียง binary search ในแต่ละหลัก
- เช่น ใส่หลายแพตเทิร์นในเงื่อนไข
s3:ResourceAccountอย่าง["0*", "1*", "2*", "3*", "4*"]เพื่อแบ่งช่วง
- เช่น ใส่หลายแพตเทิร์นในเงื่อนไข
- ถึงอย่างนั้น เวลารอให้ policy มีผลและรอผลจาก CloudTrail ก็ยังเป็นคอขวด
- แม้ใช้ binary search ก็อาจใช้เวลาประมาณ
40 * 12นาที = 8ชั่วโมง
- แม้ใช้ binary search ก็อาจใช้เวลาประมาณ
- ในตัวอย่างที่รันหลายชั่วโมง สามารถค้นหา Account ID ของ
bucket-alphaได้สำเร็จเป็น123456789101
ลดเหลือต่ำกว่า 10 นาทีด้วย policy statement 120 รายการ
- วิธีที่เร็วกว่าคือใส่ชุดตัวเลขและตำแหน่งหลักที่เป็นไปได้ทั้งหมดไว้ล่วงหน้าใน policy ของ VPC Endpoint
- ใน policy มี statement 120 รายการ รวมทั้งหมด
- ทดสอบตัวเลขที่เป็นไปได้ 10 ตัวสำหรับแต่ละตำแหน่งของ AWS Account ID
- แต่ละ statement ใช้แพตเทิร์นของตำแหน่งหลักที่ระบุใน
s3:ResourceAccountร่วมกับเงื่อนไขaws:userid
- เงื่อนไข
aws:useridใช้เพื่อ match ค่าRoleSessionNameที่สามารถกำหนดได้อย่างอิสระในการเรียก STSAssumeRole- เมื่อ assume role ด้วย
RoleSessionNameเฉพาะ ก็จะสามารถให้ผ่านเฉพาะ policy statement ที่สอดคล้องกับการทดสอบตำแหน่งหลักและตัวเลขนั้นได้
- เมื่อ assume role ด้วย
- policy นี้มีขนาดเกือบเต็มขีดจำกัดความยาวตัวอักษรสูงสุดของ policy ของ VPC Endpoint
- เนื่องจากทดสอบความเป็นไปได้ทั้ง 120 รายการแบบขนาน จึงลดความจำเป็นในการแก้ policy ทุกครั้งหรือรอผล CloudTrail ทีละรายการ
- ด้วยวิธีนี้ เวลาในการค้นหา Account ID ลดลงเหลือ ต่ำกว่า 10 นาที
ขอบเขตการเปิดเผยและความเป็นไปได้ในการประยุกต์ใช้
- กิจกรรมบางส่วนอาจปรากฏใน CloudTrail log ของเจ้าของ bucket เป้าหมาย
- Tracebit ปรึกษากับ ทีม AWS Security ก่อนเผยแพร่
- มีการถกเถียงกันมากอยู่แล้วว่า AWS Account ID เป็นข้อมูลอ่อนไหวหรือไม่ และใน event ตัวอย่างของ CloudTrail นั้น Account ID ของบุคคลที่สามถูกปิดบังเป็น
HIDDEN_DUE_TO_SECURITY_REASONS - เทคนิคเดียวกันอาจนำไปใช้กับ resource condition key อื่นที่เกี่ยวข้องกับ bucket ได้ด้วย
- เช่น
aws:ResourceOrgID - เช่น
aws:ResourceOrgPaths - เช่น
aws:ResourceTag
- เช่น
- นอกเหนือจาก S3 ยังอาจประยุกต์ใช้กับบริการอื่นที่สามารถใช้เทคนิคนี้ได้
- หากสร้าง VPC และ VPC Endpoint ที่ peering กันข้ามทุก Region ก็อาจสามารถสร้าง configuration ที่ทำงานได้ไม่ว่า bucket เป้าหมายจะอยู่ใน Region ใด
- เทคนิคนี้เป็นไปได้เพราะสามารถใช้ StringLike partial match กับเงื่อนไข
s3:ResourceAccountได้ - หาก event ที่ถูกปฏิเสธโดย policy ของ VPC Endpoint ถูกบันทึกใน CloudTrail ด้วย ก็อาจเป็นประโยชน์
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
แปลกมากที่ คีย์เงื่อนไข s3:ResourceAccount สามารถใช้การจับคู่แบบ wildcard ได้
ดูไม่มีเหตุผลที่สมควรเลยในการอนุญาตหรือปฏิเสธสิทธิ์จากการที่ตรงกับเพียงบางส่วนของ account ID
StringLikeกับสตริง account IDน่าสนใจดีที่ฝั่ง DevOps เริ่มมีแนวโน้มค้นพบ side-channel attack กันมากขึ้น Meltdown และ Spectre ซึ่งเป็น side-channel จาก speculative execution ของ CPU ก็สร้างแรงกระเพื่อมครั้งใหญ่ตอนถูกค้นพบ และก่อนหน้านั้นก็มีด้านอย่าง power analysis, การตรวจจับ magnetic distortion และ constant-time cryptography อยู่แล้ว
https://en.m.wikipedia.org/wiki/Side-channel_attack
https://en.m.wikipedia.org/wiki/Power_analysis
ไม่นานมานี้ฉันทำฟีเจอร์สำหรับ side project ที่เขียน query ในรูปแบบซึ่งได้แรงบันดาลใจจาก OWL และมีไลบรารี relational operator สำหรับดึง host จาก URL, query แบบ prefix, query แบบ
like, query แบบ regex เป็นต้นมันเป็น side project ของ side project อีกที เลยเลือกทางที่ง่ายและเปิดให้ใช้ operator ได้เสมอแม้ในกรณีที่ไม่สมเหตุสมผล ฉันไม่รู้ด้วยซ้ำว่าถ้าเอา regex query ไปใช้กับตัวเลขจะเกิดอะไรขึ้น และก็ไม่ได้สนใจ ภายใน AWS เองอาจมีอะไรคล้ายกันได้ แต่ถ้าเป็นระบบที่มีผู้ใช้จำนวนมากและอ่อนไหวด้านความปลอดภัย มาตรฐานก็ควรต่างออกไป
อาจมีคนคิดไอเดียนี้ขึ้นมาแล้วรู้สึกว่าฉลาด แต่การเอาไปใช้ในระบบที่ตัวเองควบคุมไม่ได้ 100% ดูเป็นเรื่องไม่ฉลาดเลย
โดยทั่วไปคงไม่เผยแพร่ account ID ออกสู่สาธารณะ แต่ก็ควรคิดไว้ว่าเข้าสักวันบางส่วนจะหลุดออกมา
ผู้ให้บริการ third-party และแพลตฟอร์ม SaaS จำนวนมากขึ้นกำลังเปลี่ยนไปใช้การเชื่อมต่อแบบที่นิยม การมอบหมาย role แทน IAM user และ access key ซึ่งก็ควรเป็นแบบนั้น อย่างน้อย account ID ของบัญชีที่ใช้เป็นจุดเชื่อมต่อก็จะถูกเปิดเผยให้อีกฝ่ายรู้ และฝั่งนั้นเองก็มี dependency และช่องโหว่ต่าง ๆ เช่นกัน
AWS account ID ก็คล้ายกับ IP address อาจเป็นข้อมูลอ่อนไหวได้ แต่ถ้าจะทำงานก็ต้องมีใครบางคนรู้
ตัวอย่างเช่น เมื่อ 1-2 ปีก่อนมี third party ที่ต้องเชื่อมต่อกันเพราะขั้นตอนป้องกันการฟอกเงิน โดยปกติ PrivateLink ปลอดภัยกว่าการเปิดพอร์ต SFTP สาธารณะ เราเลยเสนอให้ตั้งค่ากับองค์กรนั้น แต่บริษัทคู่กรณีกลับปฏิเสธด้วยเหตุผลด้านความปลอดภัยว่าต้องปกปิด account ID ทั้งที่มันจำเป็นต่อ role ARN ของ PV endpoint สำหรับสิทธิ์แบบสองทาง
สุดท้ายเราก็ต้อง allowlist ช่วง public IP ที่พวกเขาใช้กับพอร์ตขาเข้า 22
บทเรียนคือคุณอาจคิดว่าการทำให้ ID คลุมเครือเป็นเรื่องฉลาด แต่ถ้าอีกฝ่ายไม่รู้ว่าต้องส่งกลับไปที่ไหน ก็ยากที่จะดำเนินธุรกิจได้
ในฐานะฝั่ง vendor ปกติเราจะเชื่อมต่อด้วย VPC Endpoint Service วิธีนี้การสื่อสารเป็นทางเดียว และบริการของเราจะถูกเปิดผ่าน load balancer endpoint ภายใน VPC ของลูกค้า
เผื่อใครสนใจ ฉันเอาโค้ดไว้ที่นี่: https://github.com/tracebit-com/find-s3-account
เป็นการค้นพบที่น่าสนใจจริง แต่พอเห็นชื่อเรื่องครั้งแรก ฉันนึกว่าจะมีวิธีที่ตรงกว่านี้
คงดีถ้าใน AWS เราสามารถถามจากบัญชีผู้ดูแลในองค์กรได้ง่าย ๆ ว่า “resource X อยู่ที่ไหน” และรู้ได้เร็วว่า S3 bucket หนึ่งอยู่ใน account ไหน เรื่องนี้เกิดกับ resource อื่นด้วย แต่กับ S3 bucket จะเด่นเป็นพิเศษ
พูดตามตรง ปัญหานี้มักเกิดกับ bucket แบบ legacy ก่อนที่จะมีแนวปฏิบัติที่ดีกว่า หรือพวกที่มีอยู่มาตั้งแต่ก่อนจะนิยาม bucket ทั้งหมดไว้ในโค้ด ถึงอย่างนั้นถ้ามี AWS account เยอะ การตามหา resource ที่อยู่ใน account และ region ที่ไม่รู้จักก็อาจเป็นงานที่น่าเบื่อมาก
แบบนั้นการหาว่า account ไหนเป็นเจ้าของ resource ก็ทำได้ประมาณ
select accountId where arn = "x"AWS resource สาธารณะอื่น ๆ ที่มี global namespace ก็เผยให้เห็น AWS account 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
อย่างไรก็ตาม สิ่งหนึ่งที่ทำได้จากข้อมูลนี้คือใช้ดูความเชื่อมโยงกัน หากมีการรันหลาย S3 site ภายใต้ AWS account เดียวกัน คนอื่นก็อาจเห็นได้ว่าสิ่งเหล่านั้นโฮสต์อยู่ในบัญชีเดียวกัน เรื่องนี้สำคัญหรือไม่ขึ้นอยู่กับ threat model
แม้จะไม่สมบูรณ์แบบ แต่ก็เพิ่มชั้น abstraction ขึ้นมาอีกหนึ่งชั้น
ที่เกี่ยวข้องกันคือ ใน AWS key ID เอง ถึงจะไม่ใช่ส่วน secret key แต่ก็มี account ID ฝังอยู่ในรูปแบบที่ถูก bit shift หนึ่งครั้ง
https://medium.com/@TalBeerySec/a-short-note-on-aws-key-id-f...
key ID นี้รวมอยู่ใน pre-signed URL ของ S3 ดังนั้นมีความเป็นไปได้สูงว่าคุณอาจเปิดเผย account ID อยู่แล้ว
อาจจะโดน downvote แต่ถ้ายังอ่านอยู่ นี่คือตัวอย่างว่าทำไม security through obscurity ถึงไม่ใช่การป้องกันที่ดี ผู้ป้องกันย่อมพลาดบางอย่าง และผู้โจมตีที่ดื้อดึงจะไม่พลาด
ความปลอดภัยที่ไม่พึ่งการซ่อนเร้นยังคงใช้ได้ ไม่ว่าฉันจะเข้าใจบางอย่างหรือไม่ก็ตาม เว้นแต่ว่าผู้โจมตีจะจ้างอัจฉริยะมาทำลาย AES-256 ได้ตรง ๆ เช่น: https://www.youtube.com/watch?v=KEkrWRHCDQU
ทำไมเรื่องนี้ถึงอาจสำคัญ? ตัวอย่างที่ชัดเจนคือ ถ้ามี production bucket มาให้ ตอนนี้ก็อาจตามหา development bucket ขององค์กรเดียวกันได้แล้ว ส่วนตัวคิดว่านี่ไม่ใช่พฤติกรรมที่ควรคาดหมาย
ถ้าต้องการป้องกันความพยายาม enumerate แบบนี้ ก็ควรใส่ prefix หรือ suffix ที่สุ่มขึ้นมาในชื่อ bucket และอีกวิธีที่ดีในฐานะมาตรการเสริม ไม่ใช่ตัวทดแทน คือเปิดเผย object ของ bucket ภายใต้ชื่อที่ไม่ใช่ hostname เริ่มต้น เพื่อไม่ให้ชื่อ bucket รั่วออกมาเอง
ตัวอย่างเช่น ที่อยู่บ้านเป็นข้อมูลสาธารณะในเชิงเทคนิคก็จริง แต่ฉันไม่อยากให้มันไปอยู่บนป้ายโฆษณาข้างทางหลวงพร้อมรูปครอบครัวและบอกว่านี่คือที่ที่ฉันอยู่แน่นอน ฉันให้ข้อมูลนี้เฉพาะกับคนที่จำเป็นต้องรู้ และโดยทั่วไปก็เชื่อและคาดหวังว่ามันจะถูกเก็บไว้ใกล้เคียงกับความลับ หรืออย่างน้อยก็มีการจำกัดวัตถุประสงค์การใช้งาน
ถ้าไม่ใช่ความลับ ไม่อ่อนไหว และไม่เป็นความลับทางธุรกิจ แล้วทำไมถึงต้อง แชร์อย่างระมัดระวัง?
เพราะผู้ใช้อาจมองต่างออกไป