Project Zero ของ Google ค้นพบวิธีหลีกเลี่ยง Knox Hypervisor ของ Samsung (แก้ไขในแพทช์เดือนมกราคม)

ในบล็อกโพสต์ล่าสุดของ Project Zero ทีมงานได้ค้นพบวิธีเลี่ยงการป้องกันเคอร์เนลแบบเรียลไทม์ของ Samsung ที่เรียกว่า Knox Hypervisor

ทีม Project Zero ของ Google ได้ตรวจสอบช่องโหว่จำนวนหนึ่งที่ทำให้โทรศัพท์ของ Samsung ที่ใช้ชุดรักษาความปลอดภัย Samsung Knox ที่คาดว่าจะปลอดภัยถูกโจมตีได้ บล็อกตั้งข้อสังเกตว่าช่องโหว่ทั้งหมดได้ถูกส่งไปยัง Samsung ซึ่งได้ออกการแก้ไขจริงในการอัพเดตซอฟต์แวร์เดือนมกราคม


พื้นหลัง

ในฐานะที่เป็นส่วนหนึ่งของชุดซอฟต์แวร์รักษาความปลอดภัย Samsung Knox ที่ Samsung เปิดตัว มีซอฟต์แวร์ชิ้นหนึ่งที่อยู่ระหว่างแอปพลิเคชัน Android และเคอร์เนลที่เรียกว่า ไฮเปอร์ไวเซอร์. ซึ่งสามารถใช้เป็นเลเยอร์เพิ่มเติมเพื่อเพิ่มความปลอดภัยให้กับอุปกรณ์ Android Samsung Knox Hypervisor มีชื่อว่า "การป้องกันเคอร์เนลแบบเรียลไทม์" หรือเรียกสั้น ๆ ว่า RKP ดังที่ฉันจะอ้างอิงในส่วนที่เหลือของบทความนี้

เคอร์เนลอยู่ต่ำกว่า RKP ในกลุ่มซอฟต์แวร์ Android และแอปพลิเคชันที่ทำงานบนอุปกรณ์จะอยู่ด้านบนสุด แนวคิดเบื้องหลัง RKP คือการจัดหาชั้นความปลอดภัยเพิ่มเติมให้กับอุปกรณ์ตามคำขอทั้งหมด (หน่วยความจำและทรัพยากรอื่นๆ) ที่ดำเนินการโดย แอปพลิเคชันไปยังเคอร์เนลจะต้องผ่าน Knox ก่อน ซึ่งจะพยายามตรวจสอบว่าแอปพลิเคชันกำลังทำอะไรอยู่หรือไม่ ไม่ควร RKP ยังให้การรักษาความปลอดภัยผ่านความสับสนด้วยเลเยอร์พิเศษเพื่อซ่อนข้อมูลที่ละเอียดอ่อนซึ่งแอปพลิเคชันสามารถใช้เพื่อโจมตีอุปกรณ์ได้

โพสต์ในบล็อกเจาะลึกเกี่ยวกับวิธีการทำงานของหน่วยความจำ Android, RKP และระบบปฏิบัติการโดยทั่วไป ดังนั้นฉันจึงย่อและทำให้ง่ายขึ้นเพื่อให้เห็นภาพรวมโดยย่อของสิ่งที่ค้นพบ ฉันขอแนะนำให้คุณอ่านบทความฉบับเต็มหากคุณมีเวลา เพราะมันให้ความกระจ่างแจ้งมาก


ใช้ประโยชน์ #1:

คาสเลอร์ หรือการสุ่มเค้าโครงพื้นที่ที่อยู่เคอร์เนลเป็นกระบวนการเปลี่ยนตำแหน่งของรหัสเคอร์เนลในหน่วยความจำด้วยจำนวนสุ่มเมื่อบูต ทุกครั้งที่บูตอุปกรณ์ เคอร์เนลจะถูกโหลดลงในพื้นที่ที่อยู่อื่น (พื้นที่ในหน่วยความจำ) แนวคิดคือการทำให้ยากขึ้นในการค้นหาว่าโค้ดเคอร์เนลอยู่ที่ไหนเพื่อที่จะโจมตีมัน เนื่องจากหลังจากการบู๊ตแต่ละครั้ง โค้ดเคอร์เนลจะ "เลื่อน" ตามจำนวนสุ่มในหน่วยความจำ ดูเหมือนเป็นขั้นตอนที่ดีในการป้องกันผู้โจมตีแต่เพิ่งเกิดขึ้นไม่นานนี้ วิจัย ได้แสดงให้เห็นว่าคุณสามารถเอาชนะสิ่งนี้ได้จริง ๆ โดยไม่ต้องมีข้อบกพร่องหรือช่องโหว่ของซอฟต์แวร์ เนื่องจาก KASLR นั้นยากมากที่จะนำไปใช้ในลักษณะที่แข็งแกร่งเพื่อต่อต้านผู้โจมตีในพื้นที่

ในกรณีของซอฟต์แวร์ RKP ความสามารถในการเลี่ยงผ่าน KASLR นั้นแท้จริงแล้วง่ายกว่าการวิจัยที่อ้างถึงข้างต้น พอยน์เตอร์อ้างอิงหน่วยความจำของอุปกรณ์ Android ทั้งหมด และเพื่อปกป้องอุปกรณ์จากการถูกโจมตี เมื่อใดก็ตามที่อุปกรณ์ Android พิมพ์หรือส่งออก (ไม่ว่าจะไปที่หน้าจอหรือ เพื่อบันทึกหรือแก้ไขจุดบกพร่อง) การอ้างอิงพอยน์เตอร์จะไม่เปิดเผยชื่อ ซึ่งทำให้เป็นไปไม่ได้ที่จะค้นหาว่าพอยน์เตอร์ชี้ไปที่ใดเมื่ออ่านข้อความ เอาท์พุท

ลองนึกถึงพอยน์เตอร์แห่งความทรงจำเหมือนป้ายถนนที่ชี้ไปยังสถานที่หนึ่งๆ และคิดว่าการทำให้ไม่เปิดเผยตัวตนเป็นการเบลอสิ่งนั้น เช่นเดียวกับโทรทัศน์ การไม่ระบุชื่อจะทำหลังการถ่ายทำ Android ยังใช้การไม่ระบุชื่อนี้ในเวลาเอาต์พุตและเฉพาะเมื่อมีการกำหนดค่าการไม่ระบุชื่ออย่างถูกต้อง และผู้เขียนระบุว่า ที่อุปกรณ์ทุกเครื่อง [เขา] พบมีการกำหนดค่าพอยน์เตอร์แบบไม่เปิดเผยตัวตนอย่างถูกต้อง. อาจฟังดูเป็นเรื่องยากมากที่จะทำลาย แต่สิ่งที่คุณต้องทำคือหาพอยน์เตอร์ตัวเดียว (ลองนึกถึงป้ายถนน) ที่ไม่ได้ระบุชื่อ (เบลอออก) โดยนักพัฒนาเคอร์เนล (ระวังนี่ไม่ใช่นักพัฒนาแอป Android โดยเฉลี่ยของคุณ) เมื่อตัวชี้ถูกเขียนลงในบันทึกหรือตำแหน่งอื่น ๆ เช่น หน้าจอหรือ ไฟล์.

ดังนั้นหากคุณพบพอยน์เตอร์ที่ไม่เปิดเผยชื่อ คุณสามารถคำนวณการเปลี่ยนที่อยู่แบบสุ่มของเคอร์เนลเป็นความแตกต่างระหว่างทั้งสองได้ สิ่งที่น่าสนใจคือผู้เขียนไม่สามารถหาพอยน์เตอร์ที่สามารถหาประโยชน์ได้ในเคอร์เนล แต่พบมันใน RPK โดยที่นักพัฒนาลืมไม่ระบุชื่อตัวชี้ในเอาต์พุตการดีบัก (การบันทึก) ซึ่งเกิดขึ้นโดยวิธี a พิมพ์ผิด หากต้องการปกปิดชื่อพอยน์เตอร์ใน Android คุณต้องใช้รหัสพิเศษและปรากฎว่านักพัฒนา RPK ใช้ผิดพลาด ตัวพิมพ์เล็ก 'k' แทนที่จะเป็น ตัวพิมพ์ใหญ่ 'K'. ดังนั้นจึงค่อนข้างง่ายที่จะหาจำนวนการเปลี่ยนแปลงแบบสุ่มของโค้ดเคอร์เนลและโจมตีมัน


ใช้ประโยชน์ #2:

การหาประโยชน์ครั้งต่อไปจะซับซ้อนกว่านี้เล็กน้อย: Samsung Knox ปกป้องอุปกรณ์ของคุณโดยการใช้ชุดกฎกับหน่วยความจำของอุปกรณ์เพื่อหยุดโค้ดที่เป็นอันตราย กฎมีดังนี้:

  1. ทุกหน้า (โค้ดในหน่วยความจำ) ยกเว้นโค้ดของเคอร์เนล ถูกทำเครื่องหมายเป็น "Privileged Execute Never" (หมายความว่าโค้ดที่นี่ไม่สามารถเรียกใช้ได้)
  2. หน้าข้อมูลเคอร์เนล (ข้อมูลที่โปรแกรมใช้ในหน่วยความจำ) จะไม่ถูกทำเครื่องหมายว่าปฏิบัติการได้ (ดังนั้นโค้ดที่นี่จึงไม่สามารถทำงานได้)
  3. หน้ารหัสเคอร์เนล (รหัสในหน่วยความจำ) จะไม่ถูกทำเครื่องหมายว่าเขียนได้ (ดังนั้นจึงไม่มีรหัสที่เป็นอันตรายสามารถเปลี่ยนแปลงได้)
  4. หน้าเคอร์เนลทั้งหมดถูกทำเครื่องหมายเป็นแบบอ่านอย่างเดียวในตารางการแปลระยะที่ 2 (ตารางที่อยู่ระหว่างแอปพลิเคชันและเคอร์เนลเพื่อป้องกันไม่ให้แอปพลิเคชันทราบตำแหน่งหน่วยความจำจริงเพิ่มเติม)
  5. รายการการแปลหน่วยความจำทั้งหมดจะถูกทำเครื่องหมายว่าอ่านอย่างเดียวสำหรับแอปพลิเคชัน

เราจะเน้นที่กฎข้อ 3 เนื่องจากผู้เขียนพบปัญหาในการใช้กฎข้างต้น ในความเป็นจริง RPK ทำเครื่องหมายหน่วยความจำสำหรับเคอร์เนลเป็นแบบอ่านอย่างเดียว อย่างไรก็ตาม เนื่องจากการตรวจสอบ KASL มีการค้นพบช่องโหว่ ซึ่งนำไปสู่ การเขียนโค้ดในส่วน "อ่านอย่างเดียว" ที่คาดคะเน. เพื่อทำให้ตำแหน่งของเคอร์เนลสับสนในเวลาบูต หน่วยความจำจะถูกจัดสรรให้กับเคอร์เนล แต่หน่วยความจำจำนวนนี้มีขนาดใหญ่กว่าส่วนข้อความของเคอร์เนลมาก ด้วยการจัดสรรหน่วยความจำจำนวนมากขึ้น ทำให้การค้นหาโค้ดเคอร์เนลที่แท้จริงซึ่งอาจอยู่ที่ไหนก็ได้ทำได้ยากขึ้น และดังที่เราเห็นด้านบน รหัสดังกล่าวจะถูกย้ายแบบสุ่มในการบู๊ตอุปกรณ์แต่ละครั้ง

_text และ _etext ทำเครื่องหมายช่วงที่ได้รับการป้องกัน

ผู้เขียนสามารถยืนยันได้ว่าหน่วยความจำที่ใช้โดยเคอร์เนลนั้นถูกทำเครื่องหมายเป็น "อ่านอย่างเดียว" อย่างแน่นอน อย่างไรก็ตาม หน่วยความจำที่เหลือจำนวนมากที่ใช้ในการซ่อนเคอร์เนลนั้น ไม่ ทำเครื่องหมายว่า "อ่านอย่างเดียว" เนื่องจาก RKP จะปกป้องเฉพาะพื้นที่ที่มีข้อความของเคอร์เนลหลังจากใช้สไลด์ KASLR เท่านั้น


ใช้ประโยชน์จาก #3

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

ในการใช้ประโยชน์นี้ การควบคุม HCR คือ ไม่ครอบคลุมสองทะเบียน นั่นกลายเป็นเรื่องสำคัญมาก ผู้เขียนได้ขุดลึกเข้าไปในคู่มืออ้างอิง ARM และพบว่าการลงทะเบียนครั้งแรกทำให้เขาสามารถปิด RKP สำหรับแอปพลิเคชันโดยทั่วไปได้ "System Control Register for EL1 (SCTLR_EL1) ให้การควบคุมระบบระดับบนสุด รวมถึงระบบหน่วยความจำในโลกที่สมบูรณ์แบบ แอปพลิเคชันจะใช้หน่วยความจำที่แมปผ่าน RKP เพื่อให้ RKP สามารถควบคุมสิ่งที่แอปพลิเคชันสามารถเข้าถึงได้ อย่างไรก็ตาม การปิดการลงทะเบียนนี้ทำให้สามารถ RKP ที่จะปิดการใช้งาน โดยการคืนอุปกรณ์ให้กลับไปทำงานอย่างมีประสิทธิภาพก่อนที่จะติดตั้ง RKP ซึ่งหมายความว่าอุปกรณ์ถูกแมปกับหน่วยความจำกายภาพโดยไม่มีการรักษาความปลอดภัยเพิ่มเติมจาก RKP ในทางกลับกันนั่นหมายความว่าผู้เขียนสามารถอ่านและเขียนลงในหน่วยความจำที่ซอฟต์แวร์ RKP บล็อกไว้แต่แรกและถูกต้อง

การลงทะเบียนครั้งที่สองที่พลาดไปมีผลกระทบที่ละเอียดอ่อนกว่า แต่ท้ายที่สุดก็สร้างความเสียหายต่อความปลอดภัยเช่นกัน ที่ การลงทะเบียนการควบคุมการแปลสำหรับ EL1 (TCR_EL1) register เกี่ยวข้องโดยตรงกับจำนวนหน่วยความจำที่แอปพลิเคชันทำงานด้วยที่เรียกว่าเพจ RKP ได้รับการฮาร์ดโค้ดเป็นขนาดหน้า 4kb เนื่องจากเคอร์เนล AARCH64 Linux (เช่น Android) ใช้ขนาดการแปล 4KB การลงทะเบียนที่เป็นปัญหา (TCR_EL1) จะตั้งค่าชิปเซ็ต ARM ให้เป็นขนาดของหน่วยความจำที่จะส่งคืน ปรากฎว่า การลงทะเบียนนี้ไม่ได้รับการคุ้มครองโดย HCR ดังนั้นผู้โจมตีสามารถเปลี่ยนได้เมื่อผู้เขียนเปลี่ยนเป็นขนาดหน้า 64kb

ความหมายก็คือ เมื่อ RKP ตอบสนองคำขอ จำนวนหน่วยความจำจริงที่สามารถเข้าถึงได้ในขณะนี้คือ 64kb แทนที่จะเป็น 4kb เหตุผลก็คือชิปเซ็ต ARM ยังคงควบคุมขนาดหน้าและถูกกำหนดไว้ที่ 64kb จากการใช้ประโยชน์ เนื่องจาก RKP ปกป้องหน่วยความจำจากการเขียน เนื่องจากเป็นส่วนหนึ่งของกฎที่ระบุไว้ในการใช้ประโยชน์ #2 หน่วยความจำจึงยังคงได้รับการปกป้องจริง ๆ แต่นี่คือสิ่งที่จับได้ - เนื่องจาก RKP ถูกฮาร์ดโค้ดเป็น 4kb จึงไม่เปลี่ยนเป็นขนาดหน้า 64kb เมื่ออัปเดตการลงทะเบียน ดังนั้น มีการป้องกันเฉพาะหน่วยความจำ 4kb แรกเท่านั้น ปล่อยให้ผู้โจมตีทำ อะไรก็ได้ที่เขาอยากได้กับที่เหลือ 60kb.


ใช้ประโยชน์จาก #4

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

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


บทสรุป

ปัญหาใหญ่ที่สุดประการหนึ่งของช่องโหว่ทั้งสี่รายการข้างต้นคือผู้เขียนกล่าวถึงความยากลำบากในการดำเนินการเนื่องจากไม่มีฟังก์ชันในเคอร์เนล Android พื้นฐาน น่าแปลกที่ RKP Hypervisor ที่ปลอดภัยได้มอบเครื่องมือทั้งหมดที่จำเป็นสำหรับการโจมตี แสดงให้เห็นว่าบางครั้งซอฟต์แวร์ที่มีเจตนาดีก็ทำให้เกิดปัญหามากกว่าที่จะแก้ไขได้ และเราโชคดีที่เรามีบุคลากร เช่น Gal Beniamini เต็มใจที่จะทำให้มือของพวกเขาสกปรกและทดสอบว่าเอกสารตรงกับซอฟต์แวร์จริงหรือไม่ ทำ.

แม้ว่าการหาประโยชน์เหล่านี้จะดูน่ากลัวและทำให้ Knox ฟังดูอ่อนแอมาก แต่ฉันอยากให้ทุกคนมั่นใจว่าปัญหาเหล่านี้ได้รับการแก้ไขแล้ว แก้ไขแล้วในการอัปเดตเดือนมกราคม จากซัมซุง นอกจากนี้ การหาประโยชน์เหล่านี้จำเป็นต้องมีความเข้าใจอย่างลึกซึ้งเกี่ยวกับโปรเซสเซอร์และการเขียนโปรแกรม ARM ดังนั้นอุปสรรคในการเข้าสู่การใช้ช่องโหว่เหล่านี้จึงสูงมาก


ที่มา: โครงการศูนย์