xs
xsm
sm
md
lg

9Arm แฉยับ Coldcard หมกเม็ดโค้ด! ตกม้าตายเพราะบั๊ก

เผยแพร่:   ปรับปรุง:



ฮาร์ดแวร์เก็บคริปโตแบบออฟไลน์ถูกยกฐานะให้เป็น "ตู้เซฟดิจิทัล" ที่ได้รับการอวยไส้แตกในวงการ Bitcoin ว่าปลอดภัยระดับโลก กลับถูกเจาะจนพรุนเพียงเพราะความสะเพร่าในระดับเด็กฝึกงาน ล่าสุด "นายอาร์ม" ยูทูปเบอร์สายเทคโนโลยีชื่อดัง ได้ออกวิเคราะห์ Coinkite ผู้ผลิต Coldcard ผ่านช่องยูทูปถึงเบื้องลึกเบื้องหลังช่องโหว่ระดับล้านล้าน ที่เปลี่ยนกระเป๋าฮาร์ดแวร์สุดหรูให้กลายเป็นตู้เซฟไร้กลอนประตู

ตั้งระบบเลี่ยงโอเพนซอร์ส หวังฮุบเป็นของตัวเอง จุดเริ่มต้นแห่งความซวย

หากจะเข้าใจว่าหายนะระดับ 220 ล้านดอลลาร์ (หรือกว่า 6,000 ล้านบาท) นี้เกิดขึ้นได้อย่างไร ต้องย้อนกลับไปดู "ความงกและความอยากครอบครอง" ของ Coinkite บริษัทผู้สร้าง Coldcard ก่อน เดิมที Coldcard นำโค้ดสำหรับการสร้างตัวเลขสุ่ม (Random Number Generator - RNG) มาจาก Trezor ซึ่งเป็นคู่แข่งตัวเอ้ในตลาด แต่โค้ดของ Trezor นั้นอยู่ภายใต้สัญญาอนุญาต GPL (General Public License) ที่มีเงื่อนไขเหล็กว่า “หากเอาโค้ดผมไปดัดแปลง แล้วส่งต่อให้ลูกค้า คุณต้องเปิดเผยซอร์สโค้ดฉบับดัดแปลงนั้นต่อสาธารณะด้วย”

Coinkite ที่หัวหมอไม่อยากผูกมัด ไม่อยากให้ใครเห็นโค้ดทั้งหมดของตัวเอง จึงเล่นแง่ด้วยการเขวี้ยงโค้ด Trezor ทิ้ง แล้วให้ CTO ของบริษัทลงมือเขียนไลบรารีสุ่มเลขของตัวเองขึ้นมาใหม่ในชื่อ "NGU" (libngu) พร้อมกันนี้ยังได้ประโคมคำหรูหราอวดอ้างว่าเป็นโค้ดแบบ "Source-Verifiable" หรือเปิดให้ตรวจได้นะ...แต่ห้ามเอาไปใช้ซ้ำ เป็นการเลี่ยงบาลีทางกฎหมายเพื่อเลี่ยงความเป็นโอเพนซอร์สแท้จริง....และนี่คือจุดเริ่มของโดมิโน่แห่งความพินาศ

โค้ดเขียนถูก... แต่ระบบไม่เคยรัน

ตลกร้ายที่สุดในประวัติศาสตร์ซอฟต์แวร์ความปลอดภัยเกิดขึ้นตรงนี้ เมื่อ Coinkite ถอดโค้ด Trezor ออก แล้วถัดมาใช้ไลบรารี NGU ของตัวเอง นักพัฒนาได้เขียนฟังก์ชันสำหรับดึงค่าตัวเลขสุ่มจากชิปฮาร์ดแวร์ (Get Random) ไว้อย่างถูกต้อง สมบูรณ์แบบ ไม่มีที่ติ...แต่มันดันไปตั้งชื่อฟังก์ชันซ้ำเป๊ะกับฟังก์ชันที่มีอยู่แล้วในไลบรารี MicroPython (ซึ่งเป็นภาษาหลักที่ใช้ขับเคลื่อนเฟิร์มแวร์อุปกรณ์)

เมื่อถึงกระบวนการประกอบร่างโค้ด (Link) เข้าด้วยกัน ตัว Linker (ตัวเชื่อมโยงโค้ด) ซึ่งไม่มีสติปัญญาพอจะรู้ความตั้งใจของมนุษย์ มันก็แค่ค้นหาฟังก์ชันตามลำดับก่อน-หลังที่ถูกกำหนดไว้ในระบบ และแจ็คพอตก็แตก Linker มันเลือกหยิบฟังก์ชันของ MicroPython ไปใช้งานแทนที่จะเป็นฟังก์ชันฮาร์ดแวร์ของ Coinkite เอง

ส่งผลให้โค้ดส่วนดึงพลังสุ่มจากชิปฮาร์ดแวร์ที่ Coinkite ตั้งอกตั้งใจเขียนขึ้นมา "ไม่เคยถูกเรียกใช้งานเลยแม้แต่ครั้งเดียวในชีวิตจริง" เป็นได้แค่ขยะตัวหนังสือที่ประดับไว้ให้ดูดีเท่านั้น

ความห่วยของการวางระบบยังไม่จบแค่นั้น นายธนา​นนท์ ปฏิญญาศักดิ​กุล หรืออาร์ม ชี้ให้เห็นถึงความล้มเหลวซ้อนความล้มเหลว เมื่อนักพัฒนาของ Coinkite มีความเข้าใจผิดเกี่ยวกับตัวแปรระบบที่ชื่อว่า `MICROPY_HW_ENABLE_RNG` โดยทาง Coinkite คิดว่าหากตั้งค่าตัวแปรนี้เป็น `0` มันจะทำการปิดระบบสุ่มเลขแบบซอฟต์แวร์ทั้งหมด แล้วบังคับให้เครื่องวิ่งไปหาฟังก์ชันฮาร์ดแวร์ของตัวเองโดยอัตโนมัติ

แต่ในความเป็นจริง...มันเป็นไปไม่ได้เลย ไม่ว่าจะตั้งค่าเป็น `0` หรือ `1` ระบบก็ไม่มีวันวิ่งไปหาฟังก์ชันฮาร์ดแวร์ตัวจริงได้เลย เพราะชื่อฟังก์ชันมันโดนตัดหน้าไปตั้งแต่นาทีแรก ก่อนที่เงื่อนไขของตัวแปรนี้จะมีผลเสียอีก

ระบบหลุดไปไหน? หลุดได้ยังไง

เมื่อฟังก์ชันหลักใช้ไม่ได้ ระบบจึงตกวูบไปใช้ฟังก์ชันสุ่มเลขสำรองของ MicroPython ที่ชื่อว่า "Yasmarang" ซึ่งเป็นเพียงอัลกอริทึมสุ่มระดับอนุบาล อาศัยเพียง หมายเลขประจำชิป (Chip ID) ผสมกับ ค่าตัวจับเวลาตั้งแต่เปิดเครื่อง ผลลัพธ์คือ ขอบเขตความสุ่ม (Entropy) สรุปออกมาแคบเหลือเชื่อเพียง 0 ถึง 79,999 ค่าเท่านั้น

และสิ่งที่เรียกว่า "ตลกร้ายตบหน้าซ้ำ" คือ ในซอร์สโค้ดของ Coinkite เอง มีพฤติกรรมพยายามจะเพิ่มความสุ่ม ด้วยการเอาค่าอื่นมาเข้ากระบวนการทางคณิตศาสตร์ Exclusive OR (XOR) ร่วมกับค่า Yasmarang.... ซึ่งไส้ในที่เขานำมาผสม ดันเขียนล็อกตายตัวเป็น "ค่าคงที่" (Constant) ที่ไม่มีวันเปลี่ยน ในทางคณิตศาสตร์ความปลอดภัย การเอาค่าสุ่มมา XOR กับค่าคงที่ มีค่าเท่ากับไม่ได้เพิ่มเอนโทรปีหรือความซับซ้อนขึ้นเลยแม้แต่น้อยนิด และนั่นเองเท่ากับเป็นตัวจุดชนวนรอความพินาศ

โจรนั่งลูบปาก กวาดเงินเรียบออฟไลน์ที่บ้าน

ระบบความปลอดภัยของฮาร์ดแวร์วอลเล็ต ที่คนทั้งโลกฝากเงินล้านไว้ แต่กลับมีรหัสสุ่มที่เป็นไปได้เพียงแค่ 80,000 รูปแบบ (0 - 79,999) สำหรับแต่ละชิป......นายอาร์มระบุว่า กระบวนการไล่ล่าเงินของโจรในครั้งนี้ง่ายยิ่งกว่าปอกกล้วยเข้าปาก เพราะว่า

1.ไม่ต้องใช้ Supercomputer : คอมพิวเตอร์บ้าน ๆ ก็สามารถคำนวณรหัสที่เป็นไปได้ทั้งหมด 0 ถึง 79,999 ของแต่ละ Chip ID ได้ภายในเวลาไม่กี่นาที

2.สร้าง Seed Phrase ออฟไลน์ : นำชุดค่าสุ่มแคบ ๆ เหล่านั้นมาแปลงเป็น Seed Phrase 12/24 คำ และสร้างที่อยู่กระเป๋า (Bitcoin Address) รอไว้

3.ยิงเช็กบนเชนไร้ขีดจำกัด : การตรวจสอบยอดเงินในบัญชี Bitcoin เป็นการ Query ข้อมูลสาธารณะบน Blockchain ไม่มีระบบ Anti-brute force, ไม่มีใส่รหัสผิดเกิน 3 ครั้งแล้วล็อก, และมันไม่มีแคปช่าให้กด

แฮกเกอร์เพียงแค่เอาลิสต์ที่อยู่ทั้งหมดไปวิ่งเช็กกับโหนดสาธารณะ หากกระเป๋าไหนมีเงินขยับ...ก็หวานหมู กวาดให้เรียบไม่ให้เหลือซาก

ยอดความเสียหายที่นายอาร์มติดตาม ณ ช่วงบันทึกคลิป พุ่งสูงถึง 220 ล้านดอลลาร์ (กว่า 6,000 ล้านบาท) ซึ่งกระจุกตัวอยู่ใน 3 บัญชีมหาโหดที่ชุมชนคริปโตกำลังเพ่งเล็ง แม้ตัวเลขนี้อาจมีความผันผวนตามการประเมินของแต่ละสำนัก แต่มันคือสิ่งยืนยันว่า "หายนะนี้เกิดขึ้นจริง และรุนแรงเกินกว่าจะอภัย Coinkite

เตือนแล้วไม่ฟัง บทเรียนราคาแพงจากความประมาท

ท่ามกลางความสิ้นหวัง ยังมีกลุ่มคนที่ "รอดตายอย่างปาฏิหาริย์" นั่นคือ 

- ผู้ใช้ที่ใช้โหมดทอยลูกเต๋าจริง (Dice Roll) : ซึ่งเป็นการสร้างเอนโทรปีแบบ True Random ด้วยมือตัวเอง โดยไม่พึ่งพาระบบสุ่มของอุปกรณ์ Coldcard เลย

- ผู้ใช้ที่ตั้งค่า BIP-39 Passphrase เพิ่มเติม : ซึ่งเปรียบเสมือนรหัสผ่านคำที่ 13/25 ช่วยยืดความซับซ้อนไม่ให้แฮกเกอร์สุ่มเจอได้ง่ายๆ

แต่คำถามที่ฟาดหน้า Coinkite อย่างจังคือ...ทำไมเรื่องนี้ถึงหลุดมาถึงจุดนี้ได้?

นายอาร์มตั้งข้อสังเกตทิ้งท้ายว่า ความผิดพลาดเรื่องเงื่อนไข Preprocessor (`#ifdef` / `#if defined`) นั้นเจอบ่อยในวงการเขียนโปรแกรมทั่วไปแต่นี่คืออุปกรณ์ที่ดูแลทรัพย์สินระดับพันล้านหมื่นล้าน บริษัทระดับนี้กลับไม่มีกระบวนการ Integration Test หรือ Code Audit ที่จะตรวจสอบดูเลยหรือว่า “ฟังก์ชันที่ถูกเรียกใช้จริงในระดับ Binary สรุปแล้วมันวิ่งไปเอามาจากไลบรารีไหนกันแน่?”

และที่น่าเจ็บปวดไปกว่านั้น...มีข้อมูลระบุว่า เคยมีผู้ใช้งานรายหนึ่งเคยส่งสัญญาณเตือนไปยัง Coinkite มาก่อนหน้านี้แล้วว่าพฤติกรรมระบบสุ่มเลขของ Coldcard ดูแปลกประหลาดไม่ชอบมาพากล แต่ผลลัพธ์คืออะไร? ความเงียบเชียบและการเมินเฉยจากผู้ผลิต จนกระทั่งเงินโดนสูบหายไป 6,000 ล้านบาทนั่นแหละ ความจริงถึงได้แดงขึ้นมา

เรื่องนี้ไม่ใช่แค่เรื่องของ "บั๊กในโค้ด" แต่มันคือ "บทเรียนแห่งความยโสและประมาทเลินเล่อ" ของบริษัทเทคโนโลยีที่เอาความปลอดภัยของลูกค้ามาเล่นซ่อนแอบ เพียงเพราะไม่อยากแชร์โค้ดโอเพนซอร์สตามกฎ GPL แต่กลับเขียนโค้ดเองแบบหละหลวม ละเลยแม้กระทั่งคำเตือนของผู้บริโภค ความปลอดภัยที่แท้จริง ไม่ใช่การหลงเชื่อคำคุยโวของแบรนด์ แต่คือการตรวจสอบ (Verify) และไม่ไว้ใจใคร (Don't Trust, Verify) อย่างแท้จริง