WorstFit: การเปิดเผยตัวแปลงที่ซ่อนอยู่ของ Windows ANSI
TL;DR
- ค้นพบพื้นผิวการโจมตีใหม่โดยการใช้ประโยชน์จากฟีเจอร์การแปลงชุดอักขระภายในของ Windows ที่ชื่อ Best-Fit
- แปลงฟีเจอร์นี้ให้กลายเป็นการโจมตีเชิงปฏิบัติจริง เช่น path traversal, การฉีดอาร์กิวเมนต์ และการรันโค้ดจากระยะไกล (RCE)
- สาเหตุรากของปัญหาคือพฤติกรรมของคอมไพเลอร์, runtime ของ C/C++, และความผิดพลาดของนักพัฒนา
- บทความยังอภิปรายถึงความยากของการนำการแก้ไขไปใช้ในระบบนิเวศโอเพ่นซอร์ส
การถอดรหัสการเข้ารหัสของ Windows
เริ่มต้น: ANSI และ code page
- Windows ใช้การเข้ารหัส ANSI ในช่วงแรก ซึ่งมีประสิทธิภาพกับภาษาบางภาษา แต่ไม่สามารถจัดการชุดอักขระแบบผสมได้
- มี code page หลายตัว และแต่ละหน้าโค้ดรองรับภาษาเฉพาะ
ยุค Unicode: UTF-16
- Windows เปลี่ยนมาใช้ Unicode ในช่วงกลางทศวรรษ 1990 ทำให้สามารถแทนอักขระของแทบทุกภาษาได้ภายใต้มาตรฐานเดียว
- ในตอนแรกใช้ UCS-2 จากนั้นจึงอัปเกรดเป็น UTF-16
ยุคการเข้ารหัสคู่
- เพื่อรองรับ code page ANSI เดิม Windows จึงนำ API สองเวอร์ชันมาใช้งาน
- มีทั้ง ANSI API และ Unicode API ซึ่งช่วยให้นักพัฒนาสามารถเลือกรูปแบบข้อมูลที่ต้องการได้
ข้อดีของ Best-Fit
- การแปลงอักขระ "Best-Fit" ของ Windows คือวิธีจัดการตัวอักษรที่ไม่อยู่ในหน้าโค้ดเป้าหมายเมื่อแปลงจาก UTF-16 เป็น ANSI
- ตัวอย่างเช่น สัญลักษณ์
∞ ไม่มีใน code page Windows-1252 จึงถูกแมปเป็น 8 โดย Microsoft
WorstFit: พื้นผิวการโจมตีใหม่ของ Windows
🔥 ฝันร้ายเอเชียตะวันออก - CVE-2024-4577
- CVE-2024-4577 คือช่องโหว่ที่ทำให้เซิร์ฟเวอร์ PHP-CGI ที่ตั้งค่าเป็น code page ภาษาจีนหรือญี่ปุ่นเสียหายได้ด้วยคำขอเพียง
?%ADs
- การทำงานของ Best-Fit ทำให้ U+00AD (soft hyphen) ถูกแมปเป็นเครื่องหมายยัติภังค์
- และสามารถใช้เพื่อข้ามการป้องกันได้
🔥 การลักลอบชื่อไฟล์
- ในการจัดการชื่อไฟล์สามารถใช้ WorstFit เพื่อแปลงให้เป็น payload สำหรับ path traversal ได้
- ตัวอย่างเช่น Chrome V8 Developer Shell (
d8.exe) ใช้ ANSI API เพื่อรับ working directory ปัจจุบัน
🔥 การแยกอาร์กิวเมนต์
- โดยการดัดแปลงผลลัพธ์ของ
GetCommandLineA อาจนำการทำงานของ WorstFit มาใช้กับการแยกวิเคราะห์บรรทัดคำสั่ง
- ตัวอย่างเช่น การป้อน
" --use-askpass=calc " สามารถทำให้ระบบรัน calc.exe ได้
สรุป
- การทำงานของ Best-Fit ช่วยเปิดพื้นผิวการโจมตีในขั้นตอนการแปลงระดับระบบ ซึ่งอาจทำให้เกิดช่องโหว่ได้ในเครื่องมือต่างๆ
- ฟังก์ชันจากไลบรารีมาตรฐานหรือภาษาการเขียนโปรแกรมไม่สามารถป้องกันการโจมตีเหล่านี้ได้อย่างสมบูรณ์
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
Microsoft รู้จักปัญหานี้มาอย่างน้อยหนึ่งปีแล้วผ่านกฎการวิเคราะห์โค้ดพิเศษชื่อ CA2101 ที่ไม่แนะนำให้ใช้การแมปแบบ best-fit โดยเฉพาะ โดยได้พูดถึงความเสี่ยงด้านความปลอดภัยไว้แล้ว แต่รายละเอียดยังคงคลุมเครือ
ปัญหานี้เป็นเชิงระบบ Microsoft มีการให้บริการการแมปโค้ด "best fit" เพื่อแปลง Unicode เป็น ASCII การแมปนี้ถูกใช้ในหลายพื้นที่ และจำเป็นต้องมีอยู่ต่อไปเพราะ Microsoft ยึดความเข้ากันได้ย้อนหลังเป็นหลัก ซึ่งจะแตกต่างจากค่าเริ่มต้นว่าเชื่อมต่อกับทุกอย่าง
มักถูกใช้เพื่อแปลงโค้ดพอยต์ที่ผิดปกติเป็นเครื่องหมายทับ (slash), ขีดกลาง (hyphen), และเครื่องหมายอัญประกาศ ซึ่งสามารถถูกใช้ในทางที่ผิดได้ ในภาษาการเขียนโปรแกรมสมัยใหม่จะถูกประมวลผลอย่างถูกต้อง แต่จะมีปัญหาเมื่อส่งต่อไปยังคำสั่งเชลล์หรือ Win32 API
ผู้ดูแลโปรเจกต์ curl กล่าวว่าความผิดพลาดอยู่ที่ "curl เป็นเหยื่อ" แต่ต้นตอของปัญหานี้อยู่ที่อื่น ปัญหาจะเกิดขึ้นเมื่อมีการตรวจสอบ input ของผู้ใช้และจัดการกับมันในไลบรารีระบบด้วยวิธีที่ต่างกัน
การปิดการทำงานการแปลง "best fit" ในพื้นที่ Win32 แบบเลือกได้อาจเป็นทางออกได้ และผู้ให้บริการโอเพนซอร์สอาจเพิ่มสิ่งนี้เป็นแนวทางปฏิบัติที่ดี
Windows เหมือนเกมการ์ด Munchkin ที่ฟังก์ชันต่างๆ หลายอย่างบังเอิญมารวมกันจนเกิดช่องโหว่รุนแรง การแปลงระบบย่อย ANSI เป็น UTF-8 อาจช่วยบรรเทาปัญหานี้ได้
ตั้งแต่ NT 3.5 Microsoft ค่อยๆ เลิกใช้ ANSI และส่งเสริมการใช้ Wide Character API มาอย่างต่อเนื่อง อย่างไรก็ตาม การใช้งานไลบรารีรันไทม์ C/C++ ของ Microsoft คืออุปสรรคหลัก
ความเป็นไปได้ที่ Microsoft จะเปิดใช้ UTF-8 โดยค่าเริ่มต้นใน Windows ทั้งหมดนั้นค่อนข้างต่ำ เพราะแอปที่เก่าบางตัวยังพึ่งพา code page เฉพาะหรืออักขระ 1 ไบต์
มีสองวิธีในการบังคับให้แอปพลิเคชันตั้งค่า code page "Ansi" เป็น UTF-8 วิธีแรกคือใช้ไฟล์ Manifest และอีกวิธีคือเครื่องมือ "App Locale"
ที่คอมพิวเตอร์ Windows ส่วนตัว ผมตั้งโหมด UTF-8 เพื่อความปลอดภัยจากบั๊กนี้ และตั้งค่าดังกล่าวเพราะมีเกมต่างชาติสมัยเก่าๆ แสดงผลข้อความผิดเพี้ยน
การแก้ปัญหาจะไม่สามารถทำได้แค่ด้วยการแทนที่ main() ด้วยเวอร์ชัน wide-character อย่างเดียว ทุกตัวแปรต้องถูกเปลี่ยนเป็น wchar_t * ซึ่งเป็นงานที่เจ็บปวดและเกิดข้อผิดพลาดได้ง่าย
รู้ว่า Windows API มีการแปลง best-fit แต่นึกไม่ถึงว่าเป็นพฤติกรรมเริ่มต้น ลักษณะการทำงานนี้ควรได้รับการห้าม
สงสัยว่าเช็กบ็อกซ์ Beta มีลักษณะเหมือนการตั้ง ActiveCodePage เป็น UTF-8 หรือไม่ แต่ GDI จะยึด code page ระดับโกลบอล ไม่ยอมรับการตั้งระดับโปรเซส ทำให้ไม่สามารถเลือก UTF-8 ได้ทั้งหมด