10 คะแนน โดย outsideris 2021-04-07 | ยังไม่มีความคิดเห็น | แชร์ทาง WhatsApp

เรื่องราวของนักเรียน ม.6 ที่มีเวลาว่างเพราะ Covid เลยออกล่าบั๊กบาวน์ตี และได้รับเงิน 35,000 ดอลลาร์จากบั๊กบาวน์ตีของหน้า private ของ GitHub

เขารายงานบั๊กบาวน์ตีของหน้า private ของ GitHub และมีโบนัส CTF อยู่ 2 อย่าง

  • 10,000 ดอลลาร์: อ่านแฟล็กจาก flag.private-org.github.io ได้โดยไม่ต้องมี user interaction และถ้าสามารถอ่านแฟล็กนี้ได้จากบัญชีที่อยู่นอกองค์กร private-org จะมีโบนัสเพิ่มอีก 5,000 ดอลลาร์

  • 5,000 ดอลลาร์: อ่านแฟล็กจาก flag.private-org.github.io ได้ผ่าน user interaction

โฟลว์การยืนยันตัวตน

GitHub Pages โฮสต์อยู่บนโดเมนแยกคือ github.io ดังนั้นคุกกี้ยืนยันตัวตนของ github.com จะไม่ถูกส่งไปยังเซิร์ฟเวอร์ private pages เพราะฉะนั้นการยืนยันตัวตนของหน้า private จะไม่สามารถรู้ตัวตนผู้ใช้ได้หากไม่มีการเชื่อมต่อเพิ่มเติมกับ github.com ดังนั้น GitHub จึงสร้างโฟลว์การยืนยันตัวตนแบบกำหนดเองขึ้นมา

  • เมื่อเข้าเยี่ยมชมหน้า private เซิร์ฟเวอร์จะตรวจสอบว่ามีคุกกี้ __Host-gh_pages_token หรือไม่

  • ถ้าไม่มีคุกกี้หรือคุกกี้ไม่ถูกต้อง เซิร์ฟเวอร์ private page จะ redirect ไปที่ https://github.com/login

  • การ redirect นี้จะตั้งค่า nonce ไว้ในคุกกี้ __Host-gh_pages_session ด้วย

    • คุกกี้นี้ใช้คำนำหน้า __Host- จึงป้องกันไม่ให้ตั้งค่าด้วย JavaScript จากที่อื่นซึ่งไม่ใช่ host domain ได้
  • /login จะ redirect ไปที่ /pages/auth?nonce=&page_id=&path=

  • ที่นี่จะสร้างคุกกี้ยืนยันตัวตนชั่วคราวจากพารามิเตอร์ token แล้วส่งต่อไปยัง https://pages-auth.github.com/redirect

  • /redirect จะ forward ไปยัง https://repo.org.github.io/__/auth

  • endpoint สุดท้ายนี้จะตั้งค่าคุกกี้ยืนยันตัวตน __Host-gh_pages_token และ __Host-gh_pages_id บนโดเมน repo.org.github.io

  • ที่นี่จะตรวจสอบ nonce จาก __Host-gh_pages_session ที่ตั้งค่าไว้ก่อนหน้านี้ด้วย

พาธของคำขอเดิมและ page ID จะถูกเก็บไว้ใน query parameter path, page_id ตามลำดับ และ nonce ก็จะถูกเก็บไว้ในพารามิเตอร์ nonce ด้วย

การโจมตี

การคืนค่า CRLF

  • ช่องโหว่แรกคือการฉีด CRLF ในพารามิเตอร์ page_id ของ https://repo.org.github.io/__/auth

  • พบว่าการ parse page_id จะละเลย whitespace และค่านี้ถูกตั้งลงใน header Set-Cookie โดยตรง

  • แม้จะทำให้การ parse พังได้ด้วยการฉีด CRLF แบบดั้งเดิม แต่ก็ไม่มีผลกระทบอื่น

  • เนื่องจาก header Location: ถูกต่อท้ายหลัง header Set-Cookie จึงทำให้ถึงจะเป็น 302 redirect แต่ Location header ถูกมองข้ามและมีการ render body แทน

การโจมตี

  • จากการดูโค้ด GitHub Enterprise ทำให้รู้ว่าเซิร์ฟเวอร์ private page ถูกสร้างด้วย openresty nginx

  • เขาทำ XSS สำเร็จด้วยการเพิ่ม null byte โดย null byte นี้ต้องอยู่ตอนต้นของ body จึงไม่สามารถใช้การโจมตีแบบ header injection ได้

  • จากจุดนี้จึงสามารถรันโค้ด JavaScript ตามอำเภอใจบนโดเมน private page ได้

  • ตอนนี้สิ่งที่เหลืออยู่คือหาวิธีข้าม nonce ให้ได้

ข้าม nonce

  • จากการสังเกตพบว่า sibling private pages ภายในองค์กรเดียวกันสามารถตั้งค่าคุกกี้ให้กันและกันได้

  • คุกกี้ที่ตั้งจาก private-org.github.io จะถูกส่งไปยัง private-page.private-org.github.io

  • ถ้าหลบการป้องกันด้วยคำนำหน้า __Host- ได้ ก็จะข้าม nonce ได้ง่าย

  • ไม่ใช่ทุกเบราว์เซอร์ที่จะรองรับสิ่งนี้ และ IE ไม่รองรับคำนำหน้า __Host-

  • แต่ระหว่างพยายามหาวิธีที่ดีกว่า ก็เกิดไอเดียที่น่าสนใจขึ้นมา

  • หลังจากตรวจสอบว่าคุกกี้จัดการตัวพิมพ์เล็กใหญ่แบบใด ก็พบว่า __HOST กับ __Host ถูกปฏิบัติแตกต่างกัน และ GitHub private pages จะละเลยตัวพิมพ์ใหญ่เมื่อตอน parse คุกกี้

  • จึงสามารถกำหนด nonce ผ่าน JavaScript ได้

  • ทำให้ได้รับโบนัส 5,000 ดอลลาร์

การปนเปื้อนแคช

  • การตอบกลับของ endpoint /__/auth? ถูกแคชด้วยค่า integer ของ page_id ที่ถูกฟิชชิงมา

  • ดังนั้นหากทำ cache poisoning ผ่าน XSS payload สำเร็จ ก็จะกระทบผู้ใช้ที่ไม่ได้มีปฏิสัมพันธ์ด้วยเช่นกัน

  • ผู้โจมตีสามารถโจมตี unprivileged.org.github.io เพื่อทำให้การยืนยันตัวตนปนเปื้อน และแคช XSS payload ไว้ได้

  • เพราะคุกกี้ถูกแชร์บน parent domain คือ org.github.io ผู้โจมตีจึงสามารถโจมตี privileged.org.github.io ได้ด้วย

หน้า private ที่เป็น public

  • เพื่อจะรับโบนัส 15,000 ดอลลาร์ จำเป็นต้องทำให้ผู้ใช้ที่ไม่อยู่ในองค์กรสามารถทำการโจมตีนี้ได้

  • การตั้งค่าผิดพลาดที่เปิดใช้ private pages บน public repository ทำให้โจมตีแบบนี้ได้

    • หมายถึงการสร้าง pages จาก repo แบบ private แล้วค่อยเปลี่ยน repository ให้เป็น public
  • private pages ที่เกิดจากการตั้งค่าผิดพลาดนี้จะบังคับให้ผู้ใช้ทุกคนเข้าสู่โฟลว์การยืนยันตัวตน และทำให้ผู้ใช้นอกองค์กรมีสิทธิ์อ่านได้

ยังไม่มีความคิดเห็น

ยังไม่มีความคิดเห็น