การดีซีเรียลไลซ์ที่ไม่ปลอดภัย

หมวดหมู่ OWASP: MASVS-CODE: คุณภาพโค้ด

ภาพรวม

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

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

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

เวกเตอร์หลัก 3 รายการสำหรับการใช้ประโยชน์จากการจัดรูปแบบข้อมูลอีกครั้งใน Android มีดังนี้

  • ใช้ประโยชน์จากการคาดเดาที่ไม่ถูกต้องของนักพัฒนาแอปว่าการแยกวิเคราะห์ออบเจ็กต์ที่มาจากคลาสประเภทที่กำหนดเองนั้นปลอดภัย ในทางปฏิบัติ ออบเจ็กต์ที่มาจากคลาสใดก็ตามอาจถูกแทนที่ด้วยเนื้อหาที่เป็นอันตราย ซึ่งในกรณีที่เลวร้ายที่สุดอาจรบกวนโปรแกรมโหลดคลาสของแอปพลิเคชันเดียวกันหรือแอปพลิเคชันอื่นๆ การแทรกแซงนี้อยู่ในรูปแบบของการแทรกค่าที่เป็นอันตราย ซึ่งอาจนำไปสู่การลักลอบนำข้อมูลออกหรือการควบคุมบัญชีโดยขึ้นอยู่กับวัตถุประสงค์ของคลาส
  • การใช้ประโยชน์จากวิธีการดีซีเรียลไลซ์ที่การออกแบบถือว่าไม่ปลอดภัย (ตัวอย่างเช่น CVE-2023-35669 ซึ่งเป็นข้อบกพร่องในการโจมตีเพื่อยกระดับสิทธิ์ในเครื่องที่อนุญาตการแทรกโค้ด JavaScript ที่กำหนดเองผ่านเวกเตอร์ดีซีเรียลไลซ์ของ Deep Link)
  • การใช้ประโยชน์จากข้อบกพร่องในตรรกะแอปพลิเคชัน (เช่น CVE-2023-20963 ซึ่งเป็นข้อบกพร่องของการโจมตีเพื่อยกระดับสิทธิ์ภายในที่อนุญาตให้แอปดาวน์โหลดและเรียกใช้โค้ดภายในสภาพแวดล้อมที่ได้รับสิทธิ์ผ่านข้อบกพร่องในตรรกะพัสดุของ WorkSource ของ Android)

ผลกระทบ

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

ความเสี่ยง: การดีอนุกรมของอินพุตที่ไม่น่าเชื่อถือ

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

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

การลดปัญหา

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

โซลูชันที่เป็นไปได้อาจเป็นการใช้รูปแบบมองล่วงหน้าสำหรับไลบรารี java.io.ObjectInputStream การแก้ไขโค้ดที่รับผิดชอบในการแปลงข้อมูลย้อนกลับช่วยให้มั่นใจได้ว่าจะมีการแปลงข้อมูลย้อนกลับเฉพาะชุดคลาสที่ระบุไว้อย่างชัดเจนภายใน Intent

ตั้งแต่ Android 13 (API ระดับ 33) ได้มีการอัปเดตเมธอดหลายรายการภายในคลาส Intent ซึ่งถือว่าเป็นทางเลือกที่ปลอดภัยกว่าเมธอดเก่าๆ ที่เลิกใช้งานแล้วสำหรับการจัดการพัสดุ เมธอดใหม่ที่ปลอดภัยกว่าประเภทเหล่านี้ เช่น getParcelableExtra(java.lang.String, java.lang.Class) และ getParcelableArrayListExtra(java.lang.String, java.lang.Class) จะดำเนินการตรวจสอบประเภทข้อมูลเพื่อจับจุดอ่อนที่ไม่ตรงกันซึ่งอาจทำให้แอปพลิเคชันขัดข้องและอาจถูกนำไปใช้โจมตีเพื่อยกระดับสิทธิ์ เช่น CVE-2021-0928

ตัวอย่างต่อไปนี้แสดงวิธีใช้งานคลาส Parcel เวอร์ชันที่ปลอดภัย

สมมติว่าคลาส UserParcelable ใช้ Parcelable และสร้างอินสแตนซ์ของข้อมูลผู้ใช้ซึ่งจะเขียนลงใน Parcel จากนั้น จะใช้วิธี readParcelable ที่ปลอดภัยกว่าต่อไปนี้เพื่ออ่านพัสดุไปรษณีย์แบบเรียงอันดับได้

Kotlin

val parcel = Parcel.obtain()
val userParcelable = parcel.readParcelable(UserParcelable::class.java.classLoader)

Java

Parcel parcel = Parcel.obtain();
UserParcelable userParcelable = parcel.readParcelable(UserParcelable.class, UserParcelable.CREATOR);

สังเกตการใช้ UserParcelable.CREATOR ในเมธอดของตัวอย่าง Java ด้านบน พารามิเตอร์ที่จำเป็นนี้จะบอกเมธอด readParcelable ว่าจะพบในประเภทใดและมีประสิทธิภาพมากกว่าเมธอด readParcelable เวอร์ชันที่เลิกใช้งานแล้ว

ความเสี่ยงที่เฉพาะเจาะจง

ส่วนนี้จะรวบรวมความเสี่ยงที่ต้องใช้กลยุทธ์การบรรเทาความเสี่ยงที่ไม่ใช่มาตรฐานหรือได้รับการบรรเทาในระดับ SDK บางระดับ และอยู่ที่นี่เพื่อความสมบูรณ์

ความเสี่ยง: การจัดรูปแบบออบเจ็กต์ใหม่ที่ไม่พึงประสงค์

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

การลดปัญหา

หากคลาสรับค่าอินเทอร์เฟซ Serializable ตามคำแนะนำของ OWASP คุณควรใช้เมธอด readObject ดังนี้เพื่อหลีกเลี่ยงไม่ให้ชุดออบเจ็กต์ในคลาสได้รับการแปลงข้อมูลอีกครั้ง

Kotlin

@Throws(IOException::class)
private final fun readObject(in: ObjectInputStream) {
    throw IOException("Cannot be deserialized")
}

Java

private final void readObject(ObjectInputStream in) throws java.io.IOException {
    throw new java.io.IOException("Cannot be deserialized");
}

แหล่งข้อมูล