• เมื่อ clone repository ที่ไม่น่าเชื่อถือบนระบบตระกูล Unix ด้วย git clone --recursive อาจเปิดทางให้รันโค้ดจากระยะไกลผ่าน CVE-2025-48384 ได้ จึงจำเป็นต้องอัปเดต Git และซอฟต์แวร์ที่ฝัง Git ไว้
  • สาเหตุอยู่ที่วิธีจัดการ Carriage Return (\r) แตกต่างกันระหว่างกระบวนการอ่านและเขียนการตั้งค่าของ Git ทำให้ค่าที่ผ่านการตรวจสอบแล้วไม่ตรงกับค่าที่ถูกใช้งานจริง
  • หากใส่ \r ไว้ท้าย path ของ submodule ใน .gitmodules เส้นทาง checkout อาจเปลี่ยนเป็น เส้นทางอื่นหลังผ่านการตรวจสอบแล้ว
  • Windows ไม่ได้รับผลกระทบโดยตรง เพราะไม่อนุญาตให้ใช้ control character ในชื่อไฟล์ แต่ macOS มีช่องโหว่ทั้ง CVE-2024-32002 และ CVE-2025-48384
  • แพตช์เปลี่ยนให้ค่าการตั้งค่าที่มี \r ถูกครอบด้วยเครื่องหมายคำพูด เพื่อปิดเส้นทางโจมตี เช่น การเขียนไฟล์ภายใน .git และการสร้าง Git hook

ภาพรวมช่องโหว่และสิ่งที่ควรทำทันที

  • CVE-2025-48384 เป็นช่องโหว่ของ Git ที่อาจนำไปสู่การรันโค้ดจากระยะไกล เมื่อ clone repository ที่ไม่น่าเชื่อถือบนแพลตฟอร์มตระกูล Unix ด้วย git clone --recursive
  • ควรอัปเดตเป็น Git เวอร์ชันที่แก้ไขแล้ว และอัปเดตซอฟต์แวร์ที่ฝัง Git ไว้ด้วย รวมถึง GitHub Desktop
  • หากรันเพียง git clone จาก command line ระบบจะไม่ clone submodule โดยอัตโนมัติ
    • วิธีลดความเสี่ยงคือรัน git clone โดยไม่ใช้ --recursive ก่อน จากนั้นตรวจสอบว่า .gitmodules ปลอดภัย แล้วจึง initialize submodule
  • GitHub Desktop โดยค่าเริ่มต้นจะ clone แบบ recursive จึงอาจได้รับผลกระทบจากพฤติกรรมดังกล่าว

Carriage Return และไฟล์ตั้งค่าของ Git

  • Carriage Return คืออักขระ ASCII หมายเลข 13 และในสตริงภาษา C แสดงเป็น \r
  • Unix ตั้งใจใช้เฉพาะ LF หรือ \n เพื่อแบ่งบรรทัด แต่ Windows และหลายโปรโตคอลบนอินเทอร์เน็ตใช้ CR+LF หรือ \r\n
  • รูปแบบการตั้งค่าแบบ .ini ของ Git ใช้ทั้งกับไฟล์ตั้งค่าผู้ใช้ และไฟล์ .gitmodules ที่อยู่ใน repository
  • parser การตั้งค่าของ Git รองรับ line ending แบบ DOS โดยทำงานเมื่ออ่านอักขระดังนี้
    • หากอักขระปัจจุบันเป็น \r จะตรวจสอบอักขระถัดไป
    • หากอักขระถัดไปเป็น \n จะทิ้ง \r และถือว่าเป็นการขึ้นบรรทัดใหม่
    • หากอักขระถัดไปไม่ใช่ \n จะดันอักขระถัดไปกลับไป แล้วคืนค่า \r เอง
  • การจัดการนี้ใช้ในระดับ รายบรรทัด ดังนั้นถ้าบรรทัดใดลงท้ายด้วย CR ตัว CR นั้นอาจถูกลบออกได้ โดยไม่ขึ้นกับรูปแบบของไฟล์ทั้งไฟล์

ความไม่สอดคล้องระหว่างการอ่านและการเขียน

  • Git ไม่ได้แค่อ่านไฟล์ตั้งค่าเท่านั้น แต่เมื่อเขียนค่าการตั้งค่า เช่นผ่าน git config ก็จะบันทึกคู่ key = value ในรูปแบบเดียวกัน
  • โค้ดเดิมจะครอบค่าด้วยเครื่องหมายคำพูดคู่เฉพาะในกรณีต่อไปนี้เมื่อเขียนค่ากลับ
    • ค่าเริ่มต้นด้วยช่องว่าง
    • ภายในค่ามี ; หรือ #
    • ค่าลงท้ายด้วยช่องว่าง
  • ในทางกลับกัน โค้ดอ่านการตั้งค่ารองรับสตริงที่อยู่ในเครื่องหมายคำพูดคู่ ทำให้เมื่อนำค่าที่ write_pair เขียนไว้กลับมาอ่านในภายหลัง \r ตัวสุดท้ายอาจหลุดหายไป
  • ตัวอย่างเช่น หากไฟล์ตั้งค่ามี key = "foo^M" หลังเขียนกลับอาจกลายเป็นรูปแบบ key = foo^M
    • ในที่นี้ ^M คืออักขระ CR จริง
  • เมื่อพฤติกรรมนี้รวมกับค่าจาก .gitmodules ที่ไม่น่าเชื่อถือ จะทำให้การจัดการเส้นทาง submodule สั่นคลอน

ความสับสนของเส้นทาง submodule และขอบเขตผลกระทบ

  • บนระบบ Unix-based สามารถใส่ control character ในชื่อไฟล์ได้ จึงสามารถใส่ค่าที่มี CR ใน path ของ .gitmodules ได้
  • ตัวอย่างมีรูปแบบดังนี้
[submodule "foo"]
  path = "foo^M"
  • เมื่อค่านี้ถูกบันทึกลงใน .git/modules/foo/config โดยโค้ดตั้งค่าของ Git อาจกลายเป็นรูปแบบดังนี้
[core]
  workdir = ../../../foo^M
  • การตรวจสอบเส้นทางที่ไม่น่าเชื่อถือซึ่งอ่านจาก .gitmodules ได้เสร็จสิ้นไปแล้ว
  • ต่อมา เมื่ออ่านการตั้งค่ากลับอีกครั้ง หาก \r ตัวสุดท้ายถูกลบออก Git ก็จะใช้เส้นทางที่แตกต่างจากเส้นทางที่ผ่านการตรวจสอบ
  • ผลคือระหว่าง clone submodule ตำแหน่งที่อ่านจาก path = ... กับตำแหน่งที่ถูกบันทึกและใช้งานจริงอาจแตกต่างกัน
  • หลักการนี้คล้ายกับ CVE-2024-32002
    • CVE-2024-32002 ใช้ประโยชน์จากการแยกแยะตัวพิมพ์เล็ก-ใหญ่ใน submodule เพื่อทำให้ Git สับสน
    • CVE-2025-48384 ต้องใช้ระบบไฟล์ที่อนุญาตให้มี control character ในชื่อไฟล์
  • Windows ไม่อนุญาตให้มี control character ในชื่อไฟล์ จึงไม่ได้เปราะบางโดยตรงต่อบั๊กนี้โดยเฉพาะ
  • macOS มีช่องโหว่ทั้ง CVE-2024-32002 และ CVE-2025-48384

แพตช์และความเป็นไปได้ในการโจมตี

  • แพตช์ เปลี่ยนให้ write_pair ครอบสตริงด้วยเครื่องหมายคำพูดเมื่อภายในค่ามี \r
  • การเปลี่ยนแปลงเป็นรูปแบบเรียบง่าย โดยเพิ่ม '\r' เข้าไปในเงื่อนไขที่เดิมตรวจเฉพาะ ; หรือ #
 	for (i = 0; value[i]; i++)
-		if (value[i] == ';' || value[i] == '#')
+		if (value[i] == ';' || value[i] == '#' || value[i] == '\r')
 			quote = "\"";
  • ด้วยความสับสนของเส้นทาง สามารถวางไฟล์อันตรายของ submodule ไว้เกือบทุกตำแหน่งในระบบไฟล์ได้ และเนื่องจากอยู่ในสภาพที่เลี่ยงการตรวจสอบแล้ว จึงสามารถตาม symbolic link ที่อยู่นอก repository ได้ด้วย
  • วิธีโจมตีที่ตรงที่สุดคือเขียนไฟล์เข้าไปในไดเรกทอรี .git แล้วสร้าง สคริปต์ Git hook เพื่อให้โค้ดที่ผู้โจมตีควบคุมถูกรันเมื่อ Git เรียกใช้ hook
  • ความเป็นไปได้อื่นคือการเขียนทับ .git/config
  • ยังไม่มีการเผยแพร่ PoC แต่ระบุไว้ว่าสามารถแก้ไข exploit ของ CVE-2024-32002 ได้แทบเล็กน้อยเท่านั้น
  • การทดสอบ ใน commit แก้ไขอาจให้เบาะแสสำคัญเกี่ยวกับวิธีโจมตี

ปัญหา CR ที่เกิดซ้ำและบทเรียน

  • นี่ไม่ใช่ครั้งแรกที่ Carriage Return ก่อปัญหาให้ Git
  • ในเดือนมกราคม RyotaK ค้นพบ ปัญหาในโปรโตคอล credential helper ที่สามารถถูกหลอกด้วย Carriage Return
  • ในปี 2023 André Baptista และ Vítor Pinho ค้นพบ CVE-2023-29007 ซึ่งเป็นข้อผิดพลาดเชิงตรรกะในการ parse การตั้งค่า
  • ช่องโหว่นี้ไม่ใช่ปัญหาของภาษา C เอง แต่ใกล้เคียงกับ ข้อผิดพลาดเชิงตรรกะ ที่เกิดขึ้นได้ในแทบทุกภาษา
  • จุดร่วมคือเกิดขึ้นในการ สื่อสารระหว่างโปรเซส ไม่ว่าจะระหว่างองค์ประกอบภายในของ Git หรือระหว่าง Git กับโปรเซสภายนอก
  • โครงสร้างยังคล้ายกับ CRLF injection, request smuggling และ SMTP smuggling ใน HTTP
  • ในอดีต อินเทอร์เน็ตพึ่งพาหลักความแข็งแกร่งของ Postel ที่ว่า “จงอนุรักษนิยมเมื่อส่ง และจงเปิดกว้างเมื่อรับ” แต่ปัจจุบันคำแนะนำนั้นอาจไม่ใช่แนวทางที่สมเหตุสมผลที่สุดแล้ว
  • หัวข้อนี้มีการอธิบายเพิ่มเติมใน RFC 9413

คำขอบคุณและการแก้ไขที่เกี่ยวข้อง

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

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