2 คะแนน โดย GN⁺ 2023-08-24 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • Discord ซึ่งเป็นแอปเดสก์ท็อปที่สร้างบน Electron ทำงานภายในเป็นหน้าต่าง Chromium จึงสามารถเปิดเครื่องมือนักพัฒนาเว็บได้ และใช้แก้ไขโค้ดฝั่งไคลเอนต์ได้
  • ไคลเอนต์ม็อดและปลั๊กอินบางตัวสามารถปลดล็อกฟีเจอร์บางส่วนที่ต้องใช้ สมาชิกแบบเสียเงิน Nitro ได้จากฝั่งไคลเอนต์
  • แกนสำคัญคือการฉีดโค้ดเข้าไปในตัวแปร webpackChunkdiscord_app ซึ่งเก็บโมดูลที่ถูกแยกด้วย Webpack code splitting เพื่อสำรวจและแทนที่ฟังก์ชันภายใน
  • ฟังก์ชันตรวจสอบคุณภาพการแชร์หน้าจอถูกตรวจสอบแค่ฝั่งไคลเอนต์จึงเลี่ยงได้ แต่ ธีมพรีเมียม มีการตรวจสอบการซิงก์จากเซิร์ฟเวอร์เพิ่มเติม
  • เทคนิคสาธิตทั้งหมด ละเมิดข้อกำหนดการใช้งานของ Discord และมีความเสี่ยงทั้งการถูกระงับบัญชีและการถูกขโมยบัญชีจากการรันโค้ดที่ไม่ได้รับการตรวจสอบ

ภาพรวม

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

การเปิดใช้งานเครื่องมือนักพัฒนา

  • Discord พัฒนาด้วยเฟรมเวิร์ก Electron สำหรับสร้างแอปข้ามแพลตฟอร์มด้วยเทคโนโลยีเว็บ จึงมีลักษณะคล้ายเว็บไซต์ที่ทำงานอยู่ในเว็บเบราว์เซอร์
  • โดยแก่นแล้วมันคือ หน้าต่าง Chromium จึงเปิดเครื่องมือนักพัฒนาได้ แต่ค่าเริ่มต้นจะปิดไว้
    • มีหลายกรณีที่ผู้ใช้ถูก social engineering จากผู้ไม่หวังดีให้วางคำสั่งอันตราย จนบัญชีถูกขโมย ทำให้ฟีเจอร์นี้ถูกปิดกั้น
  • หากเข้าใจความเสี่ยงแล้ว สามารถเปิดได้โดยเพิ่มค่า DANGEROUS_ENABLE_DEVTOOLS_ONLY_ENABLE_IF_YOU_KNOW_WHAT_YOURE_DOING เป็น true ในไฟล์ตั้งค่า %appdata%/discord/settings.json
    • จากนั้นใช้ ctrl + shift + i เพื่อเปิดคอนโซลนักพัฒนา

การแพตช์ฟังก์ชันของ Discord

  • เพื่อเพิ่มประสิทธิภาพตอนโหลดครั้งแรก โค้ดจะถูกแยกเป็นหลายชังก์และโหลดแบบขนานหรือแบบหน่วงเวลา (lazy-loaded) โดยใช้ฟีเจอร์ code splitting ของ Webpack
  • ชังก์ที่โหลดแล้วทั้งหมดจะถูกเก็บไว้ในตัวแปร webpackChunkdiscord_app
  • หากต้องการรันโค้ดที่ใช้ตัวแปรหรือฟังก์ชันของ Discord จำเป็นต้อง ฉีด (inject) โค้ดเข้าไปในตัวแปรนี้
    • a.c คืออ็อบเจ็กต์ที่เก็บ โมดูล ทั้งหมด โดยแต่ละโมดูลมีรูปแบบ chunk_id, loaded, exports
    • ใน exports จะมีฟังก์ชัน อ็อบเจ็กต์ หรือค่าพื้นฐานที่โมดูลนั้น export ออกมา
  • สามารถสร้างฟังก์ชัน find เพื่อวนดู JavaScript หลายโมดูลแล้วค้นหาและเรียกใช้ฟังก์ชันเป้าหมายได้
    • ตัวอย่าง: ค้นหาและเรียกฟังก์ชัน getCurrentUser()
  • ฟังก์ชันต้นฉบับยังสามารถถูก แทนที่ (replace) ด้วยฟังก์ชันอื่นเพื่อเปลี่ยนพฤติกรรมได้ จึงแก้ไขโค้ดได้โดยตรง

การปลดล็อกสตรีมคุณภาพสูง

  • เนื้อหาหลังจากนี้ ขัดต่อข้อกำหนดการใช้งานของ Discord อย่างชัดเจน และอาจนำไปสู่การถูกระงับบัญชี ผู้ใช้ต้องยอมรับความเสี่ยงเอง
  • ฟังก์ชัน canStreamMidQuality และ canStreamHighQuality ที่ใช้ตรวจสอบระดับสมาชิกของผู้ใช้จะถูกเรียกก่อนหน้าหน้าจอเลือกคุณภาพการแชร์หน้าจอ
    • หากบังคับให้ค่าที่ทั้งสองฟังก์ชันส่งคืนเป็น true ก็จะสามารถเลือกแชร์หน้าจอที่ ความละเอียดต้นฉบับ 60 FPS ได้แม้ไม่ได้สมัคร Nitro
  • ปัจจุบันดูเหมือนว่า Discord ไม่ได้ตรวจสอบจากฝั่งเซิร์ฟเวอร์ ว่าสตรีมที่ส่งออกมาปฏิบัติตามข้อจำกัดของผู้ไม่สมัครจริงหรือไม่
    • อย่างไรก็ตาม Discord ยังไม่ได้ใช้ End-to-End encryption จึงยังมีความเป็นไปได้ที่จะตรวจจับได้
    • ตามบทความอ้างอิง Discord มีการตรวจสอบคอนเทนต์เสียงและวิดีโอของผู้ใช้อยู่แล้ว และอาจเพิ่มการตรวจสอบฝั่งเซิร์ฟเวอร์ก่อนการแชร์หน้าจอในอนาคต

การปลดล็อกธีมพรีเมียม

  • หากเปลี่ยนค่าที่ฟังก์ชัน canUseClientThemes ส่งคืน ซึ่งบอกว่าผู้ใช้สามารถใช้ธีม Nitro ได้หรือไม่ ให้เป็น true ปุ่มเลือกธีมพรีเมียมก็จะถูกปลดล็อก
  • แต่ธีมจะถูกใช้เพียงชั่วครู่แล้วกลับไปเป็นธีมเริ่มต้นทันที
    • เพราะไคลเอนต์พยายามบันทึกการเปลี่ยนธีม แต่ เซิร์ฟเวอร์ตรวจสอบว่าไม่มีสิทธิ์ และเมื่อได้รับการตอบกลับก็จะนำธีมเริ่มต้นกลับมาใช้อีกครั้ง
  • หากแพตช์ฟังก์ชัน shouldSync ให้คืนค่า false เมื่อค่าเป็น appearance เพื่อบล็อกเฉพาะการซิงก์การตั้งค่ารูปลักษณ์ การเปลี่ยนธีมก็จะคงอยู่
  • ธีมถูกจัดการ ทั้งหมดจากฝั่งไคลเอนต์ และยังสามารถแก้ CSS ได้โดยตรงเสมอ จึงเป็นเรื่องยากที่ Discord จะป้องกันสิ่งนี้ได้

บทสรุป

  • แม้จะมีโค้ดที่ทำให้การแฮ็กตามตัวอย่างนำไปใช้ได้ง่าย แต่การแก้ไขไคลเอนต์หรือใช้ม็อดควรทำอย่างระมัดระวัง
  • Discord สามารถระงับบัญชีที่ละเมิดข้อกำหนดการใช้งานได้ทุกเมื่อ และการ รันโค้ดที่ไม่ได้รับการตรวจสอบ ซึ่งมีสิทธิ์เข้าถึงบัญชีนั้นอันตรายอย่างยิ่ง
    • ข้อความที่เข้ารหัสไว้ในบทความ: "อย่าวางโค้ดที่คุณไม่เข้าใจลงในคอนโซลนักพัฒนา — นี่คือวิธีที่ง่ายที่สุดในการถูกขโมยบัญชี"

1 ความคิดเห็น

 
GN⁺ 2023-08-24
ความคิดเห็นบน Hacker News
  • ถ้าใช้วิธีให้รัน blob ที่เข้ารหัส base64 ง่าย ๆ ต่อท้ายแล้วเปิดใช้ทุกฟีเจอร์ได้ทั้งหมด ก็คิดว่าอาจกลายเป็นช่องทางโจมตีที่ค่อนข้างง่ายได้
    ไม่ได้หมายความว่าสงสัยผู้เขียนนะ แต่เมื่อกี้ลองถอดรหัสดูแล้วโดนเข้าให้เต็ม ๆ

    • สำหรับคนที่ขี้เกียจรัน base64 --decode ผลลัพธ์เป็นแบบนี้:
      // Here's a cake if you thought about inspecting the code before executing it:
      let css = 'font-size: 36px; font-weight: bold; color: red';
      console.log("%cNEVER paste code you don't understand into the development console.", css);
      console.log("%cThis is the best way to compromise your account.", css);
  • “ตอนนี้ดูเหมือนว่า Discord ไม่ได้ตรวจสอบฝั่งเซิร์ฟเวอร์ว่าสตรีมที่ส่งอยู่นั้นปฏิบัติตามเกณฑ์ของผู้ไม่ได้สมัครสมาชิกจริงหรือไม่” ฟังแล้วสงสัยว่าใครคิดว่านี่เป็นการ implement ที่ดี
    การตรวจสอบฝั่งไคลเอนต์ ดูเหมือนความผิดพลาดระดับมือใหม่จริง ๆ

    • น่าจะไม่ใช่แนวคิดว่า “นี่เป็นการ implement ที่ยอดเยี่ยม” แต่ใกล้เคียงกับ “ถ้ามีคนหาวิธีเจาะได้ค่อยแพตช์ตอนนั้น” มากกว่า
      ผมใช้ Discord มาหลายปี เพิ่งเคยได้ยินครั้งแรกว่ามีความพยายามเลี่ยงโครงสร้างสิทธิ์เข้าถึงของ Nitro และถ้ามันไม่ได้แพร่หลาย ก็ไม่เห็นเหตุผลให้ต้องใส่ใจนัก
    • ในฐานะคนที่ implement เฉพาะฟีเจอร์ที่เสร็จสมบูรณ์ ในรูปแบบปลายทางที่ต้องการ แล้วส่ง PR ขึ้นไป ผมไม่เข้าใจเลยว่าทำไมถึงทำแบบนั้น
      อีกเรื่องหนึ่ง เดือนนี้ผมเพิ่งเริ่มงานใหม่เป็นงานที่ห้าแล้ว และดูเหมือนจะเข้ากับ project manager ไม่ได้เอาเสียเลย
    • ถ้าคิดว่าคนสมัยนี้ โดยเฉพาะเด็ก ๆ ใช้เงินกันค่อนข้างง่าย แค่นี้ก็อาจเพียงพอแล้ว
      คิดว่าคงมีสักกี่คนที่เข้าใจการแฮ็กแบบนี้และยังเต็มใจจ่ายเงินให้ Nitro อยู่ดี อาจมีคนใช้ม็อดไคลเอนต์ Discord หลายตัวที่เปิดใช้ฟีเจอร์บางส่วนหรือทั้งหมดของ Nitro อยู่เป็นพัน ๆ หรืออาจเป็นหมื่น ๆ คน แต่ก็สงสัยว่าถ้าบล็อกสิ่งนี้ได้จะทำให้เกิดรายได้จริงเพิ่มขึ้นสักเท่าไร
      ส่วนตัวถ้าจำเป็นหรือต้องการฟีเจอร์นั้น ผมยอมจ่ายเดือนละ 10 ดอลลาร์จะยุ่งยากน้อยกว่ามาก ดีกว่าต้องคอยตามม็อดที่จะพังทุกครั้งที่ Discord อัปเดต และต้องหวังว่าผู้ดูแลจะไม่ใส่โค้ดอันตรายลงไป
    • ผ่าน API ยังดูชื่อ หัวข้อ และเวลาของข้อความล่าสุดใน ช่องที่ซ่อนอยู่ ได้ด้วย
      ช่องถูกซ่อนเฉพาะฝั่งไคลเอนต์เท่านั้น เพื่อให้ชัดเจน ถึงอย่างนั้นก็ไม่ได้แปลว่าจะดูเนื้อหาข้อความในช่องนั้นได้
    • นี่เป็นเพราะมันไม่ใช่องค์ประกอบของ Discord API แต่เป็นฝั่ง เซิร์ฟเวอร์เสียง ที่ถูก deploy ไว้หลายร้อยเครื่องทั่วโลก
      เซิร์ฟเวอร์เสียงแค่ตรวจสอบกับ API ว่ามีสิทธิ์เข้าหรือไม่ และหลังจากนั้นก็ทำงานเหมือนตัว broadcast แพ็กเก็ตแบบหนึ่งต่อหลาย
  • นี่ใกล้เคียงกับ แฮ็กเกอร์ แบบคลาสสิกในความหมายของคนที่อยากรู้อยากเห็นและมีฝีมือ
    ไม่มีความเสียหายร้ายแรง และช่วยเตือนอีกครั้งว่าแอปไม่ใช่เวทมนตร์ และโค้ดก็เจ๋ง

    • อาจใกล้กับ “cracker” มากกว่า แต่เห็นด้วยว่านี่เป็นความรู้ประเภทที่บริษัทต่าง ๆ ไม่อยากให้เรารู้
      เพราะจะได้พยายามรีดเงินจากเราให้มากขึ้นโดยไม่มีแรงต้าน
  • ขอเตือนอีกครั้งว่า ทุกอย่างที่ส่งผ่าน Discord รวมถึง DM จะถูกบันทึกเป็นข้อความล้วนใน Discord และ Discord รวมถึงผู้ที่ซื้อบริษัทนั้นจะเข้าถึงได้
    ครั้งหนึ่งดูเหมือน Microsoft จะซื้อ และตอนนี้ดูเหมือนจะมุ่งไปทาง IPO
    แค่มีบัญชีก็ทำให้บทสนทนาส่วนตัวที่เอื้อต่อการทำ data mining สำหรับ AI หรือการใช้งานโดย Discord และผู้ดูแลฐานข้อมูล/ระบบ กลายเป็นเรื่องปกติ Section 702 ของ FISA Amendments Act (FAA702) อนุญาตให้หน่วยสืบสวนของรัฐบาลกลางเข้าถึงข้อมูลแบบนั้นได้โดยไม่ต้องมีหมายค้น
    แค่ความเป็นไปได้ในการ front-run คริปโต ก็น่าจะมีมูลค่าหลายสิบล้านดอลลาร์ต่อวันแล้ว
    ถ้าเป็นเพื่อนกัน ก็ไม่ควรแนะนำให้เพื่อนใช้ระบบแชตที่ไม่ใช่การเข้ารหัสแบบ end-to-end อย่ากลายเป็นน้ำผึ้งในกับดัก

    • ขอเตือนอีกครั้งว่า ทุกอย่างที่ส่งไปยังฟอรัม รวมถึง DM จะถูกบันทึกเป็นข้อความล้วนและผู้ดูแลเข้าถึงได้
      นี่เป็นเรื่องปกติ เว้นแต่ว่าคุณจะใช้หนึ่งในไม่กี่บริการที่ implement zero trust model ของจริงซึ่งผู้ใช้จัดการกุญแจเอง เหตุผลที่เก็บข้อความก็เพื่อให้ผู้ใช้กลับมาเข้าถึงได้อีก
      มีบริการอย่าง Matrix ที่ทำ group messaging แบบเข้ารหัส end-to-end ได้ในวิธีที่ค่อนข้างเป็นมิตร แต่ก็เป็นข้อยกเว้น การกล่าวหาเฉพาะ Discord ว่ามีเจตนาร้ายเป็นพิเศษเพียงเพราะทำแบบเดียวกับที่แพลตฟอร์มสื่อสารอิเล็กทรอนิกส์ 95% ทำกันมานั้นไม่ยุติธรรม
    • ถึงจะบอกว่า “แค่มีบัญชีก็ทำให้มันกลายเป็นเรื่องปกติ” แต่มันกลายเป็นเรื่องปกติมานานแล้ว และ Discord ก็ชนะไปแล้ว
      ไม่ได้หมายความว่านั่นเป็นเรื่องดี แต่ในบางชุมชนไม่มีทางเลือกอื่นนอกจากไม่เข้าร่วม
      แย่กว่าสมัย “ทุกคนใช้ WhatsApp” ด้วยซ้ำ ตอนนั้นเรายังมีเบอร์ของอีกฝ่าย เลยติดต่อด้วยวิธีอื่นได้ง่าย แต่เสน่ห์ของ Discord ไม่ใช่บทสนทนาส่วนตัว แต่เป็นชุมชนที่มีพื้นที่กึ่งสาธารณะ ในที่นี้ “สาธารณะ” ไม่ได้หมายถึงเปิดให้ทุกคน แต่ใกล้เคียงกับมีคนอ่านสัก 100 คน
    • โชคดีที่ Discord มี การลบข้อความแบบไม่เหลือร่องรอย
      เมื่อลบข้อความแล้ว ข้อความจะไม่ถูกเก็บในฐานข้อมูลอีกต่อไป
  • เมื่อไม่นานมานี้ OpenAI ปิด Discord สำหรับ beta tester อย่างกะทันหัน ทำให้เราไม่สามารถดูได้อีกว่าเคยคุยกับใครและคุยเรื่องอะไร
    ผมสงสัยว่ามีวิธี export แชตเพื่อไม่ให้ผู้ดูแลเซิร์ฟเวอร์ตัดขาดชุมชนออกจากกันโดยไม่เหลือวิธีไล่ดูประวัติหรือไม่
    มี Chrome extension สำหรับ export แชต Discord อยู่พอสมควร แต่ดูเหมือนส่วนใหญ่จะล็อกฟีเจอร์บางส่วนไว้แบบเสียเงิน
    ปรากฏว่ามันง่ายมากที่จะคัดลอก ซอร์สของ extension จากโฟลเดอร์ Chrome แล้วแก้ boolean check อย่าง is_pro ใน JavaScript จากนั้นโหลดกลับเข้าไปใหม่เป็น unpacked extension

    • DiscordChatExporter: https://github.com/Tyrrrz/DiscordChatExporter
      เป็นโปรแกรม standalone ที่ใช้ API แบบไม่เป็นทางการ แต่ผมยังไม่เคยเห็นใครถูกแบนเพราะใช้สิ่งนี้
  • เขาเตือนอย่างถูกต้องว่าใช้ทริกแบบนี้แล้วอาจถูกแบนได้ แต่ Discord เคยมี ประวัติแบน ผู้ใช้เพราะเรื่องแบบนี้หรือเปล่า?

    • Discord ไม่ลังเลที่จะแบนคนด้วยเหตุผลอะไรก็ได้
      แม้แต่การใช้ไคลเอนต์ทางเลือกก็ถูกห้าม ถ้าบัญชีสำคัญ ก็อย่าไปยุ่งกับ Discord จะดีกว่า
    • ใน README ของ Vencord ซึ่งเป็นหนึ่งในม็อดที่ได้รับความนิยมที่สุด เขียนไว้แบบนี้:
      “การแก้ไขไคลเอนต์ขัดต่อข้อกำหนดการใช้บริการของ Discord”
      อย่างไรก็ตาม Discord ค่อนข้างไม่สนใจเรื่องนี้ และไม่มีกรณีที่เป็นที่รู้จักว่าถูกแบนเพราะใช้ม็อดไคลเอนต์ ดังนั้นถ้าไม่ใช้ปลั๊กอินที่ implement พฤติกรรมในทางละเมิด โดยทั่วไปก็น่าจะไม่เป็นไร ไม่ต้องกังวล เพราะว่ากันว่าปลั๊กอินในตัวทั้งหมดปลอดภัย
  • พอเข้าใจได้ว่าธีมบางอย่างเปิดใช้ด้วยวิธีแบบนี้ แต่การให้ คุณภาพการสตรีม ถูกตัดสินด้วยการตรวจฝั่งไคลเอนต์แทนที่จะเป็นฝั่งเซิร์ฟเวอร์นั้นค่อนข้างเข้าใจยาก

    • ยิ่งโดยเฉพาะเพราะแค่ตรวจ บิตเรต ขาเข้า/ขาออกก็พอแล้ว
      ไม่จำเป็นต้องเข้าถึงวิดีโอจริง และแม้จะเข้ารหัสเต็มรูปแบบ ก็ยังดูจากปริมาณข้อมูลที่ส่งได้ว่ากำลังสตรีมด้วยคุณภาพใด
  • หลักใหญ่ของวิศวกรรม frontend ถูกพิสูจน์ว่าถูกต้องอีกครั้ง
    “อย่าเชื่อใจ ไคลเอนต์ เด็ดขาด ไม่มีวัน”

  • ตั้งแต่เห็นว่ามันทำได้ฝั่งไคลเอนต์อย่างเดียว ผมก็เปิด developer tools แล้วใช้ ปุ่มสตรีม 1080p60 มาตลอด และมันใช้งานได้จริง
    ไม่รู้ว่ากี่ปีแล้ว แต่ดูเหมือนฝั่ง Discord ไม่มีใครสนใจ

  • เมื่อก่อนเคยพยายามเดินเส้นทางนี้ แต่สุดท้ายก็ยอมแพ้ เพราะหาวิธีแพตช์ไคลเอนต์ให้สม่ำเสมอไม่ได้ โดยเฉพาะหลังอัปเดต
    แต่พอเห็นวิธีนี้แล้วก็เกิดแรงกระตุ้นอีกครั้งว่า “อยากแกะดูแล้วจิ้ม ๆ”
    มีวิธีง่าย ๆ สำหรับไล่ดู API ที่ใช้ได้ด้วยวิธีนี้ไหม? เคยพยายาม log ทุกอย่าง แต่เละเทะเกินไปเพราะมี map, i18n และอื่น ๆ สงสัยว่ามีใครลงแรงทำ เอกสาร reverse engineering อะไรไว้บ้างหรือเปล่า