Understanding Buffer Overflow Attacks

ในระหว่างการสัมมนาของแฮกเกอร์เมื่อฤดูร้อนที่ผ่านมา ผู้ที่ทำงานด้านการรักษาความปลอดภัยคอมพิวเตอร์ หลายคนบ่นว่าไม่มีอะไรใหม่ ๆ ในเรื่องการรักษาความปลอดภัยเลย มีเพียงแต่บางอย่างที่เก่า ๆ ที่เพิ่มขึ้นมาเท่านั้นเอง บางอย่างที่ควรได้รับการแก้ไขหลายปีมาแล้ว แต่กลับไม่ได้รับการแก้ไข

การ โจมตีคอมพิวเตอร์อย่างหนึ่งที่มีปรากฏไปทั่วคือ การโจมตีโดยใช้ buffer overflow การโจมตีแบบนี้ต้องอาศัย ความรู้เฉพาะเกี่ยวกับภาษาแอสแซมบลี และในกรณีของวินโดวส์ต้องมีความรู้ เกี่ยวกับ interface (ตัวกลางในการสื่อสารระหว่างระบบสองระบบ)ของระบบปฏิบัติการด้วย เมื่อมีคนเขียนโค้ดที่ใช้ประโยชน์จาก ช่องโหว่นี้และเผยแพร่มันแล้ว ใคร ๆ ก็สามารถใช้มันได้ ผลจากโค้ดเหล่านี้ทำให้สามารถใช้ shell (command interpreter) ในระบบยูนิกซ์ได้ ส่วนของวินโดวส์ทำให้ สามารถอัปโหลดและรันคำสั่งได้ตามที่ต้องการ

สิ่งที่น่าเสียดายเกี่ยวกับช่องโหว่ของ buffer overflow คือ ลักษณะการเขียนโปรแกรมที่ดีนั้นสามารถขจัด ช่องโหว่ที่อาจทำความเสียหายมากได้ แต่สิ่งที่ง่าย ๆ อย่างนี้ก็ไม่ได้เกิดขึ้น ผู้พัฒนาระบบปฏิบัติการ ที่มีไมโครซอฟท์อยู่ด้วยยังคงใช้ subroutine call(คำสั่งเรียกใช้ลำดับของคำสั่งสำหรับการทำงานบางอย่าง โดยเฉพาะ) ที่อันตรายแบบเดิมที่เปิดโอกาสให้มีการใช้ประโยชน์จากช่องโหว่ ไม่น่าสงสัยเลยว่าทำไมผู้เชี่ยวชาญ ด้านความปลอดภัยถึงได้เบื่อนัก

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

เวอมอันฉาวโฉ่

ในเดือนพฤศจิกายน คศ.1998 Internet Worm ทำให้ระบบมากกว่า 6,000 ระบบปิดตัวเองลงเพียงเพราะ มันทำให้การติดต่อทางอินเทอร์เน็ตหยุดชะงักลง วิธีการหนึ่งที่มันใช้เพื่อเข้าสู่ระบบคือการ ใช้ช่องโหว่ buffer overflow ของเซอร์วิสที่มีในยูนิกซ์ที่เรียกว่า finger เมื่อคุณใช้คำสั่ง finger ยูสเซอร์คนหนึ่ง เซอร์วิสนี้ก็จะตอบกลับข้อมูลเกี่ยวกับยูสเซอร์นั้น เช่น ชื่อจริงของยูสเซอร์และหมายเลขโทรศัพท์ แต่การโจมตี buffer overflow ไปยัง finger ไปเปลี่ยนโปรแกรมของเซิร์ฟเวอร์เป็น shell หรือ command interpreter ของยูนิกซ์ ต่อมา shell จึงถูกใช้เพื่อทำสำเนาโปรแกรม Worm ที่ถูกอัปโหลด เชื่อมโยงเข้าด้วยกัน แล้วจึงรันตัว Worm นี้ขึ้นมา

การ โจมตีแบบ buffer overflow นี้ยังคงไม่ได้รับการกล่าวถึงเป็นเวลาหลายปีหลังจาก Worm ได้เกิดขึ้น ตัวอย่างที่เป็นที่รู้จักกันดีเกิดขึ้นในเดือนพฤศจิกายน คศ. 1994 เมื่อเว็ปเซิร์ฟเวอร์ที่จำหน่ายเชิงพาณิชย์ตัวแรกที่รันใน HP-UX (Hewlett-Packard UNIX) ถูกบุกรุกได้สำเร็จโดยการโจมตีแบบ buffer overflow ต่อเว็ปเซิร์ฟเวอร์ NCSA 1.3 ถึงแม้ว่าเว็ปเซิร์ฟเวอร์นี้จะตั้งอยู่ภายในเน็ตเวิร์คภายในของ เป้าหมายและต้องติดต่อโดยผ่านไฟร์วอล เท่านั้นก็ตาม ผู้โจมตียังสามารถเข้าถึงเน็ตเวิร์คภายในของเป้าหมายได้อย่างเต็มที่ โดยผู้โจมตีที่เรียกตัวเองว่า Internet Liberation Front วุ่นอยู่กับการโจมตีนี้อย่างสนุกสนาน

เหตุการณ์ที่จุดชนวนให้เกิดการโจมตีบ่อยครั้งคือการเผยแพร่ข้อเขียน "Smashing the Stack for Fun and Profit" ที่นำข้อมูลจากข้อเขียนที่เขียนโดย Mudge of the L0pht ใน Phrack พฤศจิกายน คศ.1996 ข้อเขียนของ Aleph One อธิบายถึงวิธีการให้โปรแกรมเมอร์เขียนช่องโหว่ที่ใช้ประโยชน์ จาก buffer overflow ต่อระบบยูนิกซ์ ในความเห็นของผม ข้อเขียนนี้น่าสนใจมาก โดยเฉพาะถ้าคุณ ต้องการเข้าใจถึงวิธีการทำงานของการใช้ประโยชน์จาก buffer overflow

ในปี คศ.1997 และ 1998 exploit ที่เกี่ยวกับ buffer overflow กลายเป็นสิ่งที่ใช้กันอย่างแพร่หลายมาก โดยเฉพาะกับเป้าหมายที่เป็นระบบยูนิกซ์และกับยูนิกซ์ที่เป็นแบบ open source ในขณะที่ องค์การของ open source เช่นผู้แจกจ่ายลีนุกซ์เจ้าต่าง ๆ หรือ FreeBSD ต่างออกซอฟท์แวร์แก้ไขอย่างรวดเร็ว จำนวนของช่องโหว่มีจำนวนมากจนอาจทำให้ตะลึง คุณอาจไปที่ไซต์ www.rootshell.com แล้วค้นหาคำว่า "buffer overflow" หรือไซต์อื่นเช่น L0PHT (0 คือศูนย์ไม่ใช่ตัวโอ) แล้วจึงค้นหา exploit เพื่อให้คุณรู้ถึงขอบเขตของปัญหานี้

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

คุณลักษณะของ buffer overflow

ส่วนใหญ่ buffer overflow เกิดขึ้นเพราะภาษา C และส่วนหนึ่งเกิดจากลักษณะการเขียนโปรแกรมที่ไม่ดี ภาษา C จะไม่มีการเปลี่ยนแปลง แต่ลักษณะการเขียนโปรแกรมที่ไม่ดีสามารถพัฒนาได้

ภาษา C เกิดขึ้นมาตอนต้นทศวรรษที่ 70 ในฐานะเป็นเครื่องมือสำหรับการย้ายระบบปฏิบัติการยูนิกซ์ และยูทิลีตี้ไปยังเครื่องที่มีสถาปัตยกรรมหลากหลาย เริ่มแรกยูนิกซ์ถูกเขียนขึ้นจากภาษาแอสแซมบลี แล้วจึงย้ายไปสู่ภาษา C ทำให้ยูนิกซ์เป็นระบบปฏิบัติการแรกที่สามารถย้ายข้ามเครื่องได้อย่างแท้จริง ภาษา C ได้รับการออกแบบให้กระทัดรัดและเร็ว ความเร็วเป็นรองเพียงแค่ภาษาแอสแซมบลีเท่านั้น

ภาษา C เป็นภาษาโปรแกรมแบบโครงสร้างเช่นกัน ภาษาแบบ Object oriented เช่น Java ถูกจัดระบบ ให้ล้อมรอบข้อมูล ภาษาโปรแกรมแบบโครงสร้างใช้ function call เป็นส่วนหนึ่งของหน่วยของการจัดระบบ ผู้ออกแบบภาษา C ช่วยให้ leap (ภาษาสำหรับการแสดง procedure ที่มีความสัมพันธ์กัน) มีการพัฒนาก้าวไปข้างหน้า (ไม่ใช่ภาษาแรกที่ใช้ เนื่องจากภาษา ALGOL ก็เป็นภาษาแบบโครงสร้างเช่นกัน) ภาษาเหล่านี้ยังได้สร้างโครงสร้างสำหรับ buffer overflow ด้วย

แต่ละครั้งที่ function หนึ่ง ๆ ถูกเรียก argument (ค่าหรือการอ้างอิงหนึ่งที่จะถูกส่งไปยังฟังก์ชัน) ที่จะถูกส่งไปยังฟังก์ชันนั้นจะถูกทำสำเนาไปยังพื้นที่ของหน่วยความจำที่เรียกว่า stack ในภาษาแอสแซมบลี คุณสามารถเก็บข้อมูลลงใน stack โดยการ push (การใส่บางสิ่งลงใน stack)และเรียกคืนโดยการ pop (การนำบางสิ่งออกจากส่วนบนของ stack) ทุกสถาปัตยกรรมของ CPU ที่ใช้กันในขณะนี้สนับสนุนแนวคิดเกี่ยวกับ stack และมี register (stack pointer)พิเศษ และการทำงานสำหรับการ pushing และ popping และมีตัวดำเนินการ(operator) ที่นำค่าที่อยู่ในหน่วยความจำ(address) ออกจาก stack และ ทำสำเนาที่อยู่ในหน่วยความจำนี้ไปยัง program counter ซึ่งเป็น register ซึ่งกำหนดที่อยู่ในหน่วยความจำของ คำสั่งต่อไปที่จะถูกรัน การเรียกฟังก์ชันทำให้มีการ push ที่อยู่ในหน่วยความจำที่ส่งคืน(return address)ไปยัง stack

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

subroutine ภาษา C ที่ทำสำเนาข้อมูลแต่ไม่ได้มีการตรวจสอบขนาดของข้อมูลเป็นสาเหตุของข้อผิดพลาด (เช่นเดียวกับโปรแกรมเมอร์ที่ใช้ function call เหล่านี้) function call strcat(), strcpy(), sprintf(), vsprintf(), bcopy(), gets() และ scanf() สามารถนำมาใช้ประโยชน์จากช่องโหว่ได้ เนื่องจากฟังก์ชันเหล่านี้ไม่มีการตรวจสอบว่า buffer (พื้นที่ของหน่วยความจำ ใช้สำหรับเก็บข้อมูล) ที่ได้รับการจัดสรรพื้นที่ใน stack มีขนาดใหญ่เพียงพอสำหรับข้อมูลที่จะถูกทำสำเนาลงใน buffer หรือไม่ ขึ้นอยู่กับโปรแกรมเมอร์ว่าจะใช้ฟังก์ชันที่มีการตรวจสอบ(เช่น strncpy() )หรือนับจำนวนไบต์ของข้อมูล ก่อนที่จะทำสำเนาลงใน stack หรือไม่ ถ้าสามารถรู้ถึง subroutine call ที่ถูกใช้ในทางที่ผิด แล้วคุณคิดว่าจะสามารถตรวจสอบการใช้ฟังก์ชันเหล่านี้ทั้งหมด แล้วปัญหาจะรับการแก้ไขตลอดไป อย่างนั้นใช่หรือไม่ จริง ๆ แล้วมันไม่ง่ายเช่นนั้น ยังมีวิธีอื่น ๆ ที่จะทำคล้าย ๆ กัน และทำให้สามารถใช้ประโยชน์จากช่องโหว่ได้ และทำให้มีข้อผิดพลาด (เช่น การเพิ่มตัวอักษรหลาย ๆ ตัวเข้าไปในลูป (loop)

ถึง ตอนนี้เมื่อขั้นตอนได้ถูกเซ็ตไว้แล้ว แต่การเข้าใจในส่วนนี้ยังเป็นส่วนที่เข้าใจได้ง่าย ผู้โจมตีต้องเข้าใจ ภาษาแอสแซมบลีอย่างเพียงพอด้วย byte code (ไบนารีไฟล์ที่ประกอบด้วยโปรแกรมที่สามารถรันได้ สร้างมาจากลำดับคู่ของ op code และข้อมูล)ที่ถูกใช้โดยโปรเซสเซอร์ เช่น Intel Pentium สำหรับ การเขียนโค้ด exploit ในโค้ดของ exploit สำหรับ buffer overflow โค้ดจะถูกเขียนลงใน stack ให้เกินกว่าค่าที่อยู่ในหน่วยความจำที่ส่งค่าคืนและ argument ของ function call ต่อมาค่าที่อยู่ในหน่วยความจำที่ส่งคืนจะถูกดัดแปลงเพื่อให้ชี้ไปยังจุด เริ่มต้นของโค้ด(โดยประมาณ) ต่อมาเมื่อ function calls ส่งคืนค่าแล้ว โค้ดของผู้โจมตีจะถูกรันแทนที่การรันของโปรแกรมธรรมดา

การทำให้ค่าที่อยู่ในหน่วยความจำที่ส่งคืนให้ชี้ไปยังที่ที่ถูกต้องเป็นส่วนหนึ่งของ "ศิลป์" ของ exploit ของ buffer overflow โดยทั่วไป argument ที่สามารถเปลี่ยนแปลงค่าถ่วงดุลย์(offset) ทำให้ผู้โจมตี สามารถพยายามเปลี่ยนที่อยู่ในหน่วยความจำต่าง ๆ โดยการปรับเปลี่ยนค่าเพื่อที่จะเปลี่ยนตำแหน่งของค่า ของที่อยู่ในหน่วยความจำที่ถูกส่งคืนที่ได้รับการดัดแปลงแล้วนั้น

ส่วนอื่นของศิลปะของโค้ดในระบบยูนิกซ์ค่อนข้างง่าย โปรแกรมสั้น ๆที่สามารถถูกทำสำเนาจากตัว exploit เพื่อรันโปรแกรมที่อยู่ในระบบ โดยส่วนมากจะเป็นตัวแปลคำสั่งของยูนิกซ์ คือ /bin/sh ถ้าผู้โจมตีสามารถเข้าสู่ระบบได้แล้วผลจากการรัน exploit มักจะทำให้ตัวแปลคำสั่งนั้นรันด้วยสิทธิ์ของ superuser ถ้าผู้โจมตีอยู่ที่เครื่องอื่น การเชื่อมโยงแบบ TCP ที่ใช้เพื่อเริ่มต้นการโจมตีก็จะเชื่อมโยง ไปสู่ shell (ตัวแปลคำสั่งในยูนิกซ์) และมักจะมีสิทธิ์ของ superuser ด้วย

การโจมตีกับวินโดวส์

โค้ดสำหรับวินโดวส์จะมีความซับซ้อนมากกว่า ในวินโดวส์ การทำอะไรอย่างเดียวกันกับยูนิกซ์จะต้องเขียนโค้ดมากกว่า แต่ผู้โจมตีสามารถใช้ประโยชน์จากความรู้ในการเขียนโปรแกรมไวรัส เช่น ในข้อเขียนของ Dildog ได้อธิบายวิธีการค้นหา library routine (กลุ่มของ subroutine และ function ที่เก็บไว้ในไฟล์หนึ่งหรือมากกว่า มักจะอยู่ในรูปไฟล์ที่ได้รับการคอมไพล์แล้ว สำหรับการลิงค์กับโปรแกรมอื่น) ที่สามารถใช้เพื่อดาวน์โหลดโปรแกรมที่สามารถรันได้ตามที่ผู้โจมตีต้องการ เมื่อ save เป็นไฟล์แล้วจึงรันมัน มีขนาดโค้ดน้อยกว่า 500 ไบต์ ในกรณีนี้ buffer overflow เหมือนกับ Internet Worm มากกว่า ในโค้ดของ buffer overflow ตัวมันเองจะช่วยให้มีการดาวน์โหลด exploit จริง (ซึ่งอาจเป็น Back Orifice หรือโปรแกรมอื่น ๆ ที่คล้ายกันที่มีเป็นร้อย ๆ หรือโปรแกรมโทรจันสำหรับวินโดวส์) ดังนั้นในขณะที่การเขียน exploit สำหรับวินโดวส์ยากกว่าการเขียน exploit สำหรับยูนิกซ์ มีผลให้มีการใช้ HTTP เพื่อดาวน์โหลดโปรแกรมที่ผู้โจมตีต้องการ

เหยื่อจากการโจมตีแบบ buffer overflow อาจจะสังเกตได้หรือไม่ได้ถึงความผิดปกติ ในระบบยูนิกซ์ การโจมตีที่ล้มเหลวมักจะทำให้เกิด core file ขึ้น เป็นผลมาจากการโปรแกรมทำงานล้มเหลวถ้าค่า offset ไม่ถูกต้อง ในระบบของวินโดวส์การโจมตีที่สำเร็จอาจทำให้ registry เปลี่ยนแปลงไป ปรากฏหน้าต่างวินโดวส์ ซึ่งปรากฏเป็นเวลาสั้น ๆ หรือการเปิดบราวเซอร์ขึ้นมาเอง ซอฟท์แวร์ตรวจไวรัสสามารถตรวจเจอโทรจันหลาย ๆ ชนิดซึ่งอาจถูกลงไว้ในระบบในระหว่างการโจมตี

ไฟร์วอลอาจจะป้องกันหรือไม่ป้องกันจากการโจมตีเหล่านี้ ไฟล์วอลส่วนใหญ่ในทุกวันนี้ จะยอมให้การโจมตี แบบ buffer overflow ส่วนใหญ่ผ่านไปได้ ส่วนไฟล์วอลแบบ application gateway สามารถปกป้อง จาก exploit แบบ buffer overflow ได้ต่อเมื่อ application gateway ได้รับการกำหนดค่าอย่าง ถูกต้องและไฟร์วอลได้รับการกำหนดค่าให้ใช้ application gateway จริง ๆ (เป็นทางเลือกในไฟร์วอล ส่วนใหญ่และบางครั้งก็ถูกปิดการใช้งานเนื่องจาก applicaton gateway ทำงานช้ากว่าและอาจขัดขวาง การสื่อสารที่ดูคล้ายคลึงกับการโจมตีนี้)

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

แหล่งข้อมูล

Phrack, Smashing the stack, issue 49, article 14 of Phrack by Aleph One (Elias Levy): http://www.phrack.com/search.phtml?view&article=p49-14

The article by Dr. Mudge that got the ball rolling: http://www.L0pht.com/advisories/bufero.html

Cult of the dead cow, by Dildog ('R' rated language), The Tao of Windows Buffer Overflows: http://www.cultdeadcow.com/cDc_files/cDc-351/

Rootshell, a site you can safely visit and search for buffer overflow exploits: http://www.rootshell.com/

L0PHT also includes a search facility, as well as some anti-hacking tools: http://www.l0pht.com/

เขียนโดย Pom infosec.sran.org 07/2547

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License