การโหลดไลบรารีอย่างปลอดภัยเพื่อป้องกันการโจมตีการโหลด DLL ล่วงหน้า

การสนับสนุนสําหรับ Windows Vista Service Pack 1 (SP1) สิ้นสุดลงในวันที่ 12 กรกฎาคม 2011 เมื่อต้องการรับการอัปเดตด้านความปลอดภัยสําหรับ Windows ต่อไป ตรวจสอบให้แน่ใจว่าคุณกําลังใช้งาน Windows Vista ที่มี Service Pack 2 (SP2) สําหรับข้อมูลเพิ่มเติม โปรดดูเว็บเพจของ Microsoft นี้: การสนับสนุนสําหรับ Windows บางเวอร์ชันจะสิ้นสุดลง

เมื่อแอปพลิเคชันโหลด Dynamically Link Library (DLL) แบบไดนามิกโดยไม่ระบุเส้นทางแบบเต็ม Windows จะพยายามค้นหา DLL โดยการค้นหาชุดไดเรกทอรีที่กําหนดไว้อย่างดี หากผู้โจมตีสามารถควบคุมหนึ่งในไดเรกทอรีได้ ผู้โจมตีสามารถบังคับให้แอปพลิเคชันโหลดสําเนา DLL ที่เป็นอันตรายแทน DLL ที่คาดหวังได้ การโจมตีเหล่านี้เรียกว่า "การโจมตีการโหลด DLL ล่วงหน้า" และเป็นเรื่องปกติสําหรับระบบปฏิบัติการทั้งหมดที่สนับสนุนการโหลดไลบรารี DLL ที่ใช้ร่วมกันแบบไดนามิก ผลของการโจมตีดังกล่าวอาจเกิดจากผู้โจมตีสามารถดําเนินการโค้ดในบริบทของผู้ใช้ที่กําลังเรียกใช้แอปพลิเคชันได้ เมื่อแอปพลิเคชันกําลังเรียกใช้ในฐานะผู้ดูแลระบบ สิ่งนี้อาจนําไปสู่การยกระดับสิทธิ์ให้สูงขึ้นในเครื่อง เราทราบเกี่ยวกับความสนใจต่ออายุของการโจมตีเหล่านี้ เพื่อจํากัดผลกระทบที่ปัญหานี้มีต่อลูกค้าซึ่งกันและกันเราจะปล่อยเอกสารนี้ไปยังชุมชนนักพัฒนาเพื่อให้แน่ใจว่าพวกเขารู้เกี่ยวกับปัญหานี้และมีเครื่องมือที่จําเป็นในการแก้ไขปัญหาในแอปพลิเคชันของพวกเขา

สรุป

คําอธิบายของการโจมตีการโหลด DLL ล่วงหน้า

การโจมตีตามโหลด Library

เมื่อแอปพลิเคชันโหลด DLL แบบไดนามิกโดยไม่ระบุเส้นทางแบบเต็ม Windows จะพยายามค้นหา DLL นี้โดยการค้นหาผ่านชุดไดเรกทอรีที่กําหนดไว้อย่างดี หรือที่เรียกว่า ลําดับการค้นหา DLL หาก Windows ค้นหา DLL ภายในลําดับการค้นหา DLL ระบบจะโหลด DLL นั้น อย่างไรก็ตาม หาก Windows ไม่พบ DLL ในไดเรกทอรีใดๆ ในลําดับการค้นหา DLL จะส่งกลับความล้มเหลวของการดําเนินการโหลด DLL ต่อไปนี้คือ ลําดับการค้นหา DLL สําหรับฟังก์ชัน LoadLibrary และ LoadLibraryEx ซึ่งใช้เพื่อโหลด DLL แบบไดนามิก:

  1. ไดเรกทอรีที่โหลดแอปพลิเคชัน
  2. ไดเรกทอรีของระบบ
  3. ไดเรกทอรีระบบ 16 บิต
  4. ไดเรกทอรี Windows
  5. ไดเรกทอรีที่ทํางานอยู่ในปัจจุบัน (CWD)
  6. ไดเรกทอรีที่แสดงอยู่ในตัวแปรสภาพแวดล้อม PATH

                
ลองทำตามสถานการณ์ต่อไปนี้:

  • โปรแกรมประยุกต์โหลด DLL โดยไม่ระบุเส้นทางแบบเต็มที่คาดว่าจะพบใน CWD ของโปรแกรมประยุกต์
  • แอปพลิเคชันได้เตรียมพร้อมอย่างเต็มที่เพื่อจัดการกับเคสเมื่อไม่พบ DLL
  • ผู้โจมตีทราบข้อมูลนี้เกี่ยวกับแอปพลิเคชันและควบคุม CWD
  • ผู้โจมตีคัดลอก DLL รุ่นที่ออกแบบมาเป็นพิเศษของตนเองใน CWD สันนิษฐานว่าผู้โจมตีมีสิทธิ์ในการทําเช่นนี้
  • Windows จะค้นหาผ่านไดเรกทอรีในลําดับการค้นหา DLL และค้นหา DLL ใน CWD ของแอปพลิเคชัน

ในสถานการณ์นี้ DLL ที่สร้างขึ้นเป็นพิเศษจะทํางานภายในแอปพลิเคชันและได้รับสิทธิ์ของผู้ใช้ปัจจุบัน

คำแนะนำ

เพื่อป้องกันการโจมตีนี้ แอปพลิเคชันสามารถเอาไดเรกทอรีการทํางานปัจจุบัน (CWD) ออกจากเส้นทางการค้นหา DLL โดยเรียก SetDllDirectory API โดยใช้สตริงว่าง ("") ถ้าแอปพลิเคชันขึ้นอยู่กับการโหลด DLL จากไดเรกทอรีปัจจุบัน โปรดรับไดเรกทอรีการทํางานปัจจุบันและใช้เพื่อส่งผ่านในเส้นทางแบบเต็มของ LoadLibrary

นอกจากนี้ เรายังทราบว่านักพัฒนาบางรายใช้ LoadLibrary เพื่อตรวจสอบว่ามี DLL เฉพาะเพื่อตรวจสอบว่าผู้ใช้กําลังใช้งาน Windows เวอร์ชันใดอยู่ คุณควรทราบว่าการดําเนินการนี้อาจทําให้แอปพลิเคชันมีความเสี่ยงได้ หากไลบรารีที่ได้รับผลกระทบไม่มีอยู่ใน Windows รุ่นที่ดําเนินการแอปพลิเคชัน ผู้โจมตีอาจแนะนําไลบรารีที่มีชื่อเดียวกันนั้นลงใน CWD เราขอแนะนําให้ใช้เทคนิคนี้ ให้ใช้เทคนิคที่แนะนําที่อธิบายไว้ในบทความ MSDN "การรับรุ่นของระบบ" แทน

แอปพลิเคชันที่โหลดปลั๊กอินของบริษัทอื่น และที่ไม่สามารถบังคับให้ปลั๊กอินใช้เส้นทางที่มีคุณสมบัติสําหรับการเรียกใช้ LoadLibrary ควรเรียก SetDllDirectory("") เพื่อลบ CWD จากนั้นเรียก SetDllDirectory("ตําแหน่งที่ตั้งการติดตั้งปลั๊กอิน") เพื่อเพิ่มไดเรกทอรีการติดตั้งปลั๊กอินไปยังเส้นทางการค้นหา DLL

การโจมตีโดยใช้ SearchPath

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

  • ไดเรกทอรีที่โหลดแอปพลิเคชัน
  • ไดเรกทอรีที่ทํางานอยู่ในปัจจุบัน (CWD)
  • ไดเรกทอรีของระบบ
  • ไดเรกทอรีระบบ 16 บิต
  • ไดเรกทอรี Windows
  • ไดเรกทอรีที่แสดงอยู่ในตัวแปรสภาพแวดล้อม PATH

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

ShellExecute และ CreateProcess

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

เราขอแนะนําให้นักพัฒนาทําดังต่อไปนี้:

  • ตรวจสอบความถูกต้องของแอปพลิเคชันสําหรับอินสแตนซ์ของการโหลดไลบรารีที่ไม่ปลอดภัย (ตัวอย่างของแต่ละรายการจะได้รับในภายหลังในบทความนี้) ซึ่งรวมถึงรายการต่อไปนี้:

    • การใช้ SearchPath เพื่อระบุตําแหน่งที่ตั้งของไลบรารีหรือคอมโพเนนต์
    • การใช้ LoadLibrary เพื่อระบุเวอร์ชันของระบบปฏิบัติการ
  • ใช้เส้นทางแบบเต็มสําหรับการโทรทั้งหมดไปยัง LoadLibrary, CreateProcess และ ShellExecute ที่คุณสามารถทํา

  • ใช้การเรียก SetDllDirectory กับสตริงว่าง ("") เพื่อเอาไดเรกทอรีการทํางานปัจจุบันออกจากลําดับการค้นหา DLL เริ่มต้นที่จําเป็นต้องใช้ โปรดทราบว่า SetDllDirectory มีผลต่อกระบวนการทั้งหมด ดังนั้น คุณควรทําเช่นนี้ในครั้งแรกในการเตรียมใช้งานกระบวนการ ไม่ใช่ก่อนและหลังการโทรไปยัง LoadLibrary เนื่องจาก SetDllDirectory มีผลต่อกระบวนการทั้งหมด เธรดหลายเธรดที่เรียก SetDllDirectory ที่มีค่าต่างกันอาจทําให้เกิดลักษณะการทํางานที่ไม่ได้กําหนดไว้ นอกจากนี้ หากกระบวนการถูกออกแบบมาให้โหลด DLL ของบริษัทอื่น การทดสอบจําเป็นเพื่อตรวจสอบว่าการตั้งค่าทั้งกระบวนการจะทําให้เกิดความเข้ากันหรือไม่ ปัญหาที่ทราบคือเมื่อแอปพลิเคชันขึ้นอยู่กับ Visual Basic for Applications การตั้งค่าทั้งกระบวนการอาจทําให้เกิดความเข้ากันไม่ได้

  • ใช้ฟังก์ชัน SetSearchPathMode เพื่อเปิดใช้งานโหมดการค้นหากระบวนการที่ปลอดภัยสําหรับกระบวนการ การดําเนินการนี้จะย้ายไดเรกทอรีการทํางานปัจจุบันไปยังตําแหน่งสุดท้ายในรายการค้นหา SearchPath สําหรับอายุการใช้งานของกระบวนการ

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

คําแนะนําเกี่ยวกับการระบุการโหลดไลบรารีที่ไม่ปลอดภัย

ในโค้ดต้นฉบับ ต่อไปนี้คือตัวอย่างของการโหลดไลบรารีที่ไม่ปลอดภัย:

  • ในตัวอย่างรหัสต่อไปนี้ แอปพลิเคชันจะค้นหา "schannel.dll" โดยใช้เส้นทางการค้นหาที่ปลอดภัยน้อยที่สุด หากผู้โจมตีสามารถวาง schannel.dll ใน CWD ได้ ผู้โจมตีจะโหลดก่อนแอปพลิเคชันจะค้นหาไดเรกทอรีของ Windows สําหรับไลบรารีที่เหมาะสม

    DWORD retval = SearchPath(NULL, "schannel", ".dll", err, result, NULL); 
    HMODULE handle = LoadLibrary(result);
    
  • ในตัวอย่างโค้ดต่อไปนี้ แอปพลิเคชันจะพยายามโหลดไลบรารีจากแอปพลิเคชันต่างๆ และตําแหน่งที่ตั้งของระบบปฏิบัติการที่อธิบายไว้ในตอนต้นของเอกสารนี้สําหรับการโทร LoadLibrary() โปรแกรมประยุกต์อาจพยายามโหลดแฟ้มจากไดเรกทอรีที่ทํางานอยู่ในปัจจุบัน สถานการณ์นี้อันตรายน้อยกว่าตัวอย่างก่อนหน้านี้เล็กน้อย อย่างไรก็ตามผู้ใช้แอปพลิเคชันยังคงเสี่ยงหากสภาพแวดล้อมไม่สามารถคาดการณ์ได้อย่างสมบูรณ์

    HMODULE handle = LoadLibrary("schannel.dll");
    

                
                
ต่อไปนี้คือตัวอย่างของการโหลดไลบรารีที่ดีกว่าและปลอดภัยมากขึ้น:

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

    HMODULE handle = LoadLibrary("c:\\windows\\system32\\schannel.dll");
    

    หมายเหตุ สําหรับข้อมูลเกี่ยวกับวิธีการระบุไดเรกทอรีระบบ ดูทรัพยากรต่อไปนี้:

    GetSystemDirectory
    http://msdn.microsoft.com/en-us/library/ms724373%28VS.85%29.aspx SHGetKnownFolderPath
    http://msdn.microsoft.com/en-us/library/bb762188%28v=VS.85%29.aspx

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

    SetDllDirectory ("");
    HMODULE handle = LoadLibrary("schannel.dll");
    
  • ในระบบทั้งหมดที่ติดตั้งการอัปเดตความปลอดภัย 963027 (อธิบายไว้ใน MS09-014) รหัสต่อไปนี้จะย้าย CWD ไปยังตําแหน่งสุดท้ายในลําดับการค้นหาอย่างถาวร การเรียกใดๆ ในภายหลังไปยังฟังก์ชัน SetSearchPathMode จากภายในกระบวนการนั้นที่พยายามเปลี่ยนโหมดการค้นหาจะล้มเหลว

    SetDllDirectory ("");
    HMODULE handle = LoadLibrary("schannel.dll");
    
    
  • ในตัวอย่างรหัสต่อไปนี้ ไดเรกทอรีที่ใช้งานได้ในปัจจุบันจะถูกเอาออกจากเส้นทางการค้นหาก่อนที่จะเรียกใช้ LoadLibrary วิธีนี้จะช่วยลดความเสี่ยงได้อย่างมาก เนื่องจากผู้โจมตีจะต้องควบคุมไดเรกทอรีแอปพลิเคชัน ไดเรกทอรีของ Windows หรือไดเรกทอรีใดๆ ที่ระบุในเส้นทางของผู้ใช้เพื่อใช้การโจมตีการโหลด DLL ล่วงหน้า

    SetSearchPathMode (BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | BASE_SEARCH_PATH_PERMANENT );
    HMODULE handle = LoadLibrary("schannel.dll");
    
    
    

การใช้ตัวตรวจสอบกระบวนการเพื่อตรวจหาโหลดที่ไม่ปลอดภัยแบบไดนามิก

Microsoft เผยแพร่เครื่องมือที่มีชื่อว่า ตัวตรวจสอบกระบวนการ เครื่องมือนี้ช่วยให้นักพัฒนาและผู้ดูแลระบบสามารถติดตามพฤติกรรมของกระบวนการทํางานได้อย่างใกล้ชิด ตัวตรวจสอบกระบวนการสามารถใช้เพื่อตรวจจับแบบไดนามิกว่าหนึ่งในแอปพลิเคชันของคุณอาจมีความเสี่ยงต่อปัญหาประเภทนี้หรือไม่

  • เมื่อต้องการดาวน์โหลดตัวตรวจสอบกระบวนการ โปรดเยี่ยมชมเว็บเพจต่อไปนี้ของ Microsoft:
    http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx

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

  • ตั้งค่าตัวตรวจสอบกระบวนการด้วยตัวกรองต่อไปนี้:

    371495f2-14de-f99c-c55a-f75d31fe9ca8

  • หากเส้นทางเปราะบางถูกตีคุณจะเห็นสิ่งที่คล้ายกับต่อไปนี้: 9acdd1ae-29b9-e499-9de9-8bc665b95e76

     การเรียกไปยังการใช้แฟ้มระยะไกลร่วมกันเพื่อโหลด DLL ระบุว่านี่เป็นโปรแกรมที่มีช่องโหว่

ข้อมูลเพิ่มเติม

สําหรับข้อมูลเพิ่มเติม โปรดเยี่ยมชมเว็บเพจต่อไปนี้ของ Microsoft:

ลําดับการค้นหา Dynamic Link Library

.aspx http://msdn.microsoft.com/en-us/library/ms682586(VS.85) เอกสารประกอบ MSDN บนฟังก์ชัน SearchPath

http://msdn.microsoft.com/en-us/library/aa365527(VS.85).aspx เอกสารประกอบ MSDN บนฟังก์ชัน LoadLibrary

http://msdn.microsoft.com/en-us/library/ms684175(VS.85).aspx เอกสารประกอบ MSDN บนฟังก์ชัน SetDllDirectory

http://msdn.microsoft.com/en-us/library/ms686203(VS.85).aspx เอกสารประกอบ MSDN บนฟังก์ชัน SetSearchPathMode

http://msdn.microsoft.com/en-us/library/dd266735(VS.85).aspx โพสต์ในบล็อกโดย David Leblanc ซึ่งเป็นวิศวกรด้านความปลอดภัยหลักกับ Microsoft Office

http://blogs.msdn.com/b/david_leblanc/archive/2008/02/20/dll-preloading-attacks.aspx โพสต์ในบล็อกโดย Andrew Roths ทีมวิศวกร MSRC เกี่ยวกับการโจมตีโหลด DLL ล่วงหน้า

http://blogs.technet.com/b/srd/archive/2009/04/14/ms09-014-addressing-the-safari-carpet-bomb-vulnerability.aspx

แหล่งข้อมูลเพิ่มเติม